From ccb06e4e7afaeebdbb6615ec1d7071d541da287a Mon Sep 17 00:00:00 2001 From: Wesley Shields Date: Thu, 16 Jul 2026 04:56:11 -0400 Subject: [PATCH 1/7] feat: expose ignore_invalid_rules in python API. (#711) This commit exposes the new feature of ignoring invalid rules during compilation. It is set to False by default but if turned on than errors are not raised on compilation failure and any ignored rules will be returned when calling the `Compiler::ignored_rules()` method. It returns a list of IgnoredRule objects which have two properties: name and reason. This makes it easier for users of this API to figure out why a rule was ignored. Now they can check the `reason` property on `IgnoredRule` to see if it is any of the IgnoredRuleReason values from the exposed enum. --- py/src/lib.rs | 87 ++++++++++++++++++++++++++++++++++++++++++-- py/tests/test_api.py | 21 +++++++++++ py/yara_x.pyi | 48 ++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/py/src/lib.rs b/py/src/lib.rs index 7af16985..edf57ac2 100644 --- a/py/src/lib.rs +++ b/py/src/lib.rs @@ -437,6 +437,27 @@ impl Module { } } +/// Reasons a rule can be ignored by the compiler. +#[pyclass(eq, eq_int, from_py_object)] +#[derive(PartialEq, Clone)] +enum IgnoredRuleReason { + IgnoredModule, + IgnoredRule, + CompileError, +} + +/// Structure that represents invalid rules by the compiler. See +/// [`Compiler::ignore_invalid_rules`]. +#[pyclass] +struct IgnoredRule { + #[pyo3(get)] + name: String, + #[pyo3(get)] + message: String, + #[pyo3(get)] + reason: IgnoredRuleReason, +} + /// Returns the names of the supported modules. /// /// These are the modules that can be used in `import` statements in your @@ -465,6 +486,7 @@ struct Compiler { relaxed_re_syntax: bool, error_on_slow_pattern: bool, includes_enabled: bool, + ignore_invalid_rules: bool, } impl Compiler { @@ -497,19 +519,24 @@ impl Compiler { /// /// The `error_on_slow_pattern` argument tells the compiler to treat slow /// patterns as errors, instead of warnings. + /// + /// `ignore_invalid_rules` argument tells the compiler to report reasons for + /// ignoring invalid rules in [`Compiler::ignored_rules`]. #[new] - #[pyo3(signature = (relaxed_re_syntax=false, error_on_slow_pattern=false, includes_enabled=true) + #[pyo3(signature = (relaxed_re_syntax=false, error_on_slow_pattern=false, includes_enabled=true, ignore_invalid_rules=false) )] fn new( relaxed_re_syntax: bool, error_on_slow_pattern: bool, includes_enabled: bool, + ignore_invalid_rules: bool, ) -> Self { let mut compiler = Self { inner: Self::new_inner(relaxed_re_syntax, error_on_slow_pattern), relaxed_re_syntax, error_on_slow_pattern, includes_enabled, + ignore_invalid_rules, }; compiler.inner.enable_includes(includes_enabled); compiler @@ -564,9 +591,14 @@ impl Compiler { src = src.with_origin(origin) } - self.inner - .add_source(src) - .map_err(|err| CompileError::new_err(err.to_string()))?; + match self.inner.add_source(src) { + Ok(_) => {} + Err(err) => { + if !self.ignore_invalid_rules { + return Err(CompileError::new_err(err.to_string())); + } + } + }; Ok(()) } @@ -685,6 +717,20 @@ impl Compiler { self.inner.max_warnings(n); } + /// Enables or disables ignoring invalid rules. If `True` the ignored rules + /// and the reasons for them being ignored are available in + /// [`Compiler::ignored_rules`] method. + /// + /// # Example + /// ```python + /// import yara_x + /// + /// compiler = yara_x.Compiler() + /// compiler.ignore_invalid_rules(True) + /// ``` + fn ignore_invalid_rules(&mut self, yes: bool) { + self.ignore_invalid_rules = yes + } /// Builds the source code previously added to the compiler. /// /// This function returns an instance of [`Rules`] containing all the rules @@ -731,6 +777,37 @@ impl Compiler { json_loads.call((warnings_json,), None) } + /// Retrieves all ignored rules from the compiler. + /// + /// Returns a list of tuples where the first item is the rule name and the + /// second item is the reason. + fn ignored_rules(&self) -> Vec { + self.inner + .ignored_rules() + .map(|(name, reason)| { + let (message, reason_type) = match reason { + yrx::IgnoredRuleReason::IgnoredModule(module) => ( + format!("depends on ignored module `{module}`"), + IgnoredRuleReason::IgnoredModule, + ), + yrx::IgnoredRuleReason::IgnoredRule(parent_rule) => ( + format!("depends on ignored rule `{parent_rule}`"), + IgnoredRuleReason::IgnoredRule, + ), + yrx::IgnoredRuleReason::CompileError(err) => ( + format!("error: {}", err.title()), + IgnoredRuleReason::CompileError, + ), + }; + IgnoredRule { + name: name.to_string(), + message, + reason: reason_type, + } + }) + .collect() + } + #[pyo3(signature = (tags, error = false))] fn allowed_tags( &mut self, @@ -1574,6 +1651,8 @@ fn yara_x(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; // This module still exposes unsendable classes and uses unsafe lifetime // extensions in the bindings, so it should not advertise free-threaded // safety until the API is properly audited and redesigned. diff --git a/py/tests/test_api.py b/py/tests/test_api.py index 1b7f6e2f..1dd487fa 100644 --- a/py/tests/test_api.py +++ b/py/tests/test_api.py @@ -40,6 +40,27 @@ def test_invalid_allowed_metadata_regexp(): with pytest.raises(ValueError): compiler.allowed_metadata('author', yara_x.MetaType.STRING, regexp='(AXS|ERS') +def test_ignore_invalid_rules(): + compiler = yara_x.Compiler(ignore_invalid_rules=True) + compiler.ignore_module("foo") + # "test" should be ignored because it depends on an ignored module. + # "test2" should be ignored because it depends on an ignored rule (test). + # "test3" should be ignored because it is a compilation error. + compiler.add_source(r'import "foo" rule test { condition: foo.bar == 1 } rule test2 { condition: test } rule test3 { condition: AXSERS }') + assert len(compiler.ignored_rules()) == 3 + assert compiler.ignored_rules()[0].name == 'test' + assert compiler.ignored_rules()[0].message == 'depends on ignored module `foo`' + assert compiler.ignored_rules()[0].reason == yara_x.IgnoredRuleReason.IgnoredModule + assert compiler.ignored_rules()[1].name == 'test2' + assert compiler.ignored_rules()[1].message == 'depends on ignored rule `test`' + assert compiler.ignored_rules()[1].reason == yara_x.IgnoredRuleReason.IgnoredRule + assert compiler.ignored_rules()[2].name == 'test3' + assert compiler.ignored_rules()[2].message == 'error: unknown identifier `AXSERS`' + assert compiler.ignored_rules()[2].reason == yara_x.IgnoredRuleReason.CompileError + # Turn off ignore_invalid_rules and we should raise a compile error now. + compiler.ignore_invalid_rules(False) + with pytest.raises(yara_x.CompileError): + compiler.add_source(r'rule test { condition: AXSERS }') def test_int_globals(): compiler = yara_x.Compiler() diff --git a/py/yara_x.pyi b/py/yara_x.pyi index 21faaad3..31480f03 100644 --- a/py/yara_x.pyi +++ b/py/yara_x.pyi @@ -135,6 +135,23 @@ class Compiler: """ ... + def ignore_invalid_rules(self, yes: bool) -> None: + r""" + Tell the compiler to ignore any rules that are invalid. + + Any ignored rules and the reasons for them being ignored are available + in [`Compiler::ignored_rules`]. + """ + ... + + def ignored_rules() -> List[IgnoredRule]: + r""" + Retrieve all ignored rules during compilation. + + Returns a list of [`IgnoredRule`] objects. + """ + ... + def build(self) -> Rules: r""" Builds the source code previously added to the compiler. @@ -193,6 +210,37 @@ class Compiler: r"""Define expected type and value for metadata on rules.""" ... +@final +class IgnoredRuleReason(Enum): + IgnoredModule: int + IgnoredRule: int + CompileError: int + +@final +class IgnoredRule: + r""" + Names and reasons for any ignored rules during compilation. + + See [`Compiler::ignore_invalid_rules] for details. + """ + def name() -> str: + r""" + Name of the ignored rule. + """ + ... + + def message() -> str: + r""" + Message for why the rule was ignored. + """ + ... + + def reason() -> IgnoredRuleReason: + r""" + Reason for why the rule was ignored. + """ + ... + @final class ScanOptions: r""" From 754170d794a876d650128ee73ad165228de04053 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 11:11:39 +0200 Subject: [PATCH 2/7] tests: add test case that covers all the `wsock32_ord_to_name` function. --- ...aec203e78695d54e1a7874b0fd5ac80568b.in.zip | Bin 0 -> 991 bytes ...09aaec203e78695d54e1a7874b0fd5ac80568b.out | 458 ++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.in.zip create mode 100644 lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.out diff --git a/lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.in.zip b/lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..ac02326b48b5fc20b9ac32ddb8b96584889c09d5 GIT binary patch literal 991 zcmWIWW@Zs#U|`^2@Q?fub22*h;&&znhHe1{24|qCg=wOtu|;x{k%6g&nSqIsk!6~( zv1yV~nxTPZVq$8tk%4ilxrLdfX^N>ys$rtJg}F(RL0XDwVzPySshLHRUS?kJH1BPX z6?j;m@0C(^m@x0>?*FQ#4z5DKm;ODXAah$(axaP#1~4X+a9Hyk|}cH@?9P`Kyo%`Nd=5$4JA^3T70Q}nN@ ztX+J6*P(Or&+jSENsH`_ee=R~sc^RVwYxXIo?dl5dF8RZzc0RiIoChu^6#WKbw<_V z4?kBtdHAEq{d%E1mer~1H{QRWv-;)SjySb@{11)y+h2P>|JSPbZ@JTRn08y-;Mi>y(Rf!Tr{QkB z`}~C_-+s)U!IXe=MulQy?E&r$PQWsm3S3=1mH=T{PJq@3{t}1ws z*0FEw_eHh7O}$^GDq1=#a{F`hV&9qE#ankC`gWRs-PtqTpR+a}`lf2O?(LcOvte_s zRBvqaJz)Mo`UCF|uAJL;U`CtOyNfrLNdSe|YMA#~=UB^vnXLCkx7CJz5Uo+=J6~LT zds5T-2iiYQE!~$JeJ~s-`ZVbNGPNIE`56{r1#%v*(9gN8_edJZSr!<7qioIx-yh2?-gW*uWOlVB|B(5^*>1Iox1S=ILY((O zoc>>jzWrmo-;;mLJpR?~ZRdZa)fn&l3AA^{$Fo58=cW7JmK-yGJp1ES@7mkpqVZD; z@1NOm_M6_zexHvaHlqGV*WRe!WFK}k_~Vtp>krv(UlH*?y7@t*TUhT~QJo4g8}8ED z2PHS^6Z_9zj6ZPx)|6>~FV5X}JaMmE(O3R8uV2Y0#QtSov%0>a^eg{{dH=Q___yZ5 z`Gp(G{~nf*ymr<8z^-5H5#j$C5DE>heeQ7QpBMXAe~tG34eS0K+VzS1^(lfJyPF%2 q7XD!l@MdHZVaA=afSC&n8W=$o74lhtH!B;+C`KSG1kw@AARYk5Yu>v6 literal 0 HcmV?d00001 diff --git a/lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.out b/lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.out new file mode 100644 index 00000000..2d1cdc46 --- /dev/null +++ b/lib/src/modules/pe/tests/testdata/85a938cb2058604229f335b2f109aaec203e78695d54e1a7874b0fd5ac80568b.out @@ -0,0 +1,458 @@ +is_pe: true +machine: MACHINE_I386 +subsystem: SUBSYSTEM_WINDOWS_CUI +os_version: + major: 4 + minor: 0 +subsystem_version: + major: 4 + minor: 0 +image_version: + major: 0 + minor: 0 +linker_version: + major: 0 + minor: 0 +opthdr_magic: IMAGE_NT_OPTIONAL_HDR32_MAGIC +characteristics: 0x102 # EXECUTABLE_IMAGE | MACHINE_32BIT +dll_characteristics: 0x0 +timestamp: 0 # 1970-01-01 00:00:00 UTC +image_base: 0x400000 +checksum: 0 +base_of_code: 0x1000 +base_of_data: 0x2000 +entry_point: 0x200 +entry_point_raw: 0x1000 +section_alignment: 0x1000 +file_alignment: 0x200 +loader_flags: 0x0 +size_of_optional_header: 0xe0 +size_of_code: 0x400 +size_of_initialized_data: 0x400 +size_of_uninitialized_data: 0x0 +size_of_image: 0x3000 +size_of_headers: 0x200 +size_of_stack_reserve: 0x100000 +size_of_stack_commit: 0x1000 +size_of_heap_reserve: 0x100000 +size_of_heap_commit: 0x1000 +pointer_to_symbol_table: 0x0 +win32_version_value: 0 +number_of_symbols: 0 +number_of_rva_and_sizes: 16 +number_of_sections: 1 +number_of_imported_functions: 118 +number_of_delayed_imported_functions: 0 +number_of_resources: 0 +number_of_version_infos: 0 +number_of_imports: 1 +number_of_delayed_imports: 0 +number_of_exports: 0 +number_of_signatures: 0 +sections: + - name: ".text" + full_name: ".text" + characteristics: 0x60000020 # SECTION_MEM_READ | SECTION_MEM_WRITE | SECTION_SCALE_INDEX + raw_data_size: 0x400 + raw_data_offset: 0x200 + virtual_address: 0x1000 + virtual_size: 0x1000 + pointer_to_relocations: 0x0 + pointer_to_line_numbers: 0x0 + number_of_relocations: 0 + number_of_line_numbers: 0 +data_directories: + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x1000 + size: 0x200 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 + - virtual_address: 0x0 + size: 0x0 +import_details: + - library_name: "wsock32.dll" + number_of_functions: 118 + functions: + - name: "accept" + ordinal: 1 + rva: 0x1040 + - name: "bind" + ordinal: 2 + rva: 0x1044 + - name: "closesocket" + ordinal: 3 + rva: 0x1048 + - name: "connect" + ordinal: 4 + rva: 0x104c + - name: "getpeername" + ordinal: 5 + rva: 0x1050 + - name: "getsockname" + ordinal: 6 + rva: 0x1054 + - name: "getsockopt" + ordinal: 7 + rva: 0x1058 + - name: "htonl" + ordinal: 8 + rva: 0x105c + - name: "htons" + ordinal: 9 + rva: 0x1060 + - name: "ioctlsocket" + ordinal: 10 + rva: 0x1064 + - name: "inet_addr" + ordinal: 11 + rva: 0x1068 + - name: "inet_ntoa" + ordinal: 12 + rva: 0x106c + - name: "listen" + ordinal: 13 + rva: 0x1070 + - name: "ntohl" + ordinal: 14 + rva: 0x1074 + - name: "ntohs" + ordinal: 15 + rva: 0x1078 + - name: "recv" + ordinal: 16 + rva: 0x107c + - name: "recvfrom" + ordinal: 17 + rva: 0x1080 + - name: "select" + ordinal: 18 + rva: 0x1084 + - name: "send" + ordinal: 19 + rva: 0x1088 + - name: "sendto" + ordinal: 20 + rva: 0x108c + - name: "setsockopt" + ordinal: 21 + rva: 0x1090 + - name: "shutdown" + ordinal: 22 + rva: 0x1094 + - name: "socket" + ordinal: 23 + rva: 0x1098 + - name: "GetAddrInfoW" + ordinal: 24 + rva: 0x109c + - name: "GetNameInfoW" + ordinal: 25 + rva: 0x10a0 + - name: "WSApSetPostRoutine" + ordinal: 26 + rva: 0x10a4 + - name: "FreeAddrInfoW" + ordinal: 27 + rva: 0x10a8 + - name: "WPUCompleteOverlappedRequest" + ordinal: 28 + rva: 0x10ac + - name: "WSAAccept" + ordinal: 29 + rva: 0x10b0 + - name: "WSAAddressToStringA" + ordinal: 30 + rva: 0x10b4 + - name: "WSAAddressToStringW" + ordinal: 31 + rva: 0x10b8 + - name: "WSACloseEvent" + ordinal: 32 + rva: 0x10bc + - name: "WSAConnect" + ordinal: 33 + rva: 0x10c0 + - name: "WSACreateEvent" + ordinal: 34 + rva: 0x10c4 + - name: "WSADuplicateSocketA" + ordinal: 35 + rva: 0x10c8 + - name: "WSADuplicateSocketW" + ordinal: 36 + rva: 0x10cc + - name: "WSAEnumNameSpaceProvidersA" + ordinal: 37 + rva: 0x10d0 + - name: "WSAEnumNameSpaceProvidersW" + ordinal: 38 + rva: 0x10d4 + - name: "WSAEnumNetworkEvents" + ordinal: 39 + rva: 0x10d8 + - name: "WSAEnumProtocolsA" + ordinal: 40 + rva: 0x10dc + - name: "WSAEnumProtocolsW" + ordinal: 41 + rva: 0x10e0 + - name: "WSAEventSelect" + ordinal: 42 + rva: 0x10e4 + - name: "WSAGetOverlappedResult" + ordinal: 43 + rva: 0x10e8 + - name: "WSAGetQOSByName" + ordinal: 44 + rva: 0x10ec + - name: "WSAGetServiceClassInfoA" + ordinal: 45 + rva: 0x10f0 + - name: "WSAGetServiceClassInfoW" + ordinal: 46 + rva: 0x10f4 + - name: "WSAGetServiceClassNameByClassIdA" + ordinal: 47 + rva: 0x10f8 + - name: "WSAGetServiceClassNameByClassIdW" + ordinal: 48 + rva: 0x10fc + - name: "WSAHtonl" + ordinal: 49 + rva: 0x1100 + - name: "WSAHtons" + ordinal: 50 + rva: 0x1104 + - name: "gethostbyaddr" + ordinal: 51 + rva: 0x1108 + - name: "gethostbyname" + ordinal: 52 + rva: 0x110c + - name: "getprotobyname" + ordinal: 53 + rva: 0x1110 + - name: "getprotobynumber" + ordinal: 54 + rva: 0x1114 + - name: "getservbyname" + ordinal: 55 + rva: 0x1118 + - name: "getservbyport" + ordinal: 56 + rva: 0x111c + - name: "gethostname" + ordinal: 57 + rva: 0x1120 + - name: "WSAInstallServiceClassA" + ordinal: 58 + rva: 0x1124 + - name: "WSAInstallServiceClassW" + ordinal: 59 + rva: 0x1128 + - name: "WSAIoctl" + ordinal: 60 + rva: 0x112c + - name: "WSAJoinLeaf" + ordinal: 61 + rva: 0x1130 + - name: "WSALookupServiceBeginA" + ordinal: 62 + rva: 0x1134 + - name: "WSALookupServiceBeginW" + ordinal: 63 + rva: 0x1138 + - name: "WSALookupServiceEnd" + ordinal: 64 + rva: 0x113c + - name: "WSALookupServiceNextA" + ordinal: 65 + rva: 0x1140 + - name: "WSALookupServiceNextW" + ordinal: 66 + rva: 0x1144 + - name: "WSANSPIoctl" + ordinal: 67 + rva: 0x1148 + - name: "WSANtohl" + ordinal: 68 + rva: 0x114c + - name: "WSANtohs" + ordinal: 69 + rva: 0x1150 + - name: "WSAProviderConfigChange" + ordinal: 70 + rva: 0x1154 + - name: "WSARecv" + ordinal: 71 + rva: 0x1158 + - name: "WSARecvDisconnect" + ordinal: 72 + rva: 0x115c + - name: "WSARecvFrom" + ordinal: 73 + rva: 0x1160 + - name: "WSARemoveServiceClass" + ordinal: 74 + rva: 0x1164 + - name: "WSAResetEvent" + ordinal: 75 + rva: 0x1168 + - name: "WSASend" + ordinal: 76 + rva: 0x116c + - name: "WSASendDisconnect" + ordinal: 77 + rva: 0x1170 + - name: "WSASendTo" + ordinal: 78 + rva: 0x1174 + - name: "WSASetEvent" + ordinal: 79 + rva: 0x1178 + - name: "WSASetServiceA" + ordinal: 80 + rva: 0x117c + - name: "WSASetServiceW" + ordinal: 81 + rva: 0x1180 + - name: "WSASocketA" + ordinal: 82 + rva: 0x1184 + - name: "WSASocketW" + ordinal: 83 + rva: 0x1188 + - name: "WSAStringToAddressA" + ordinal: 84 + rva: 0x118c + - name: "WSAStringToAddressW" + ordinal: 85 + rva: 0x1190 + - name: "WSAWaitForMultipleEvents" + ordinal: 86 + rva: 0x1194 + - name: "WSCDeinstallProvider" + ordinal: 87 + rva: 0x1198 + - name: "WSCEnableNSProvider" + ordinal: 88 + rva: 0x119c + - name: "WSCEnumProtocols" + ordinal: 89 + rva: 0x11a0 + - name: "WSCGetProviderPath" + ordinal: 90 + rva: 0x11a4 + - name: "WSCInstallNameSpace" + ordinal: 91 + rva: 0x11a8 + - name: "WSCInstallProvider" + ordinal: 92 + rva: 0x11ac + - name: "WSCUnInstallNameSpace" + ordinal: 93 + rva: 0x11b0 + - name: "WSCUpdateProvider" + ordinal: 94 + rva: 0x11b4 + - name: "WSCWriteNameSpaceOrder" + ordinal: 95 + rva: 0x11b8 + - name: "WSCWriteProviderOrder" + ordinal: 96 + rva: 0x11bc + - name: "freeaddrinfo" + ordinal: 97 + rva: 0x11c0 + - name: "getaddrinfo" + ordinal: 98 + rva: 0x11c4 + - name: "getnameinfo" + ordinal: 99 + rva: 0x11c8 + - name: "WSAAsyncSelect" + ordinal: 101 + rva: 0x11cc + - name: "WSAAsyncGetHostByAddr" + ordinal: 102 + rva: 0x11d0 + - name: "WSAAsyncGetHostByName" + ordinal: 103 + rva: 0x11d4 + - name: "WSAAsyncGetProtoByNumber" + ordinal: 104 + rva: 0x11d8 + - name: "WSAAsyncGetProtoByName" + ordinal: 105 + rva: 0x11dc + - name: "WSAAsyncGetServByPort" + ordinal: 106 + rva: 0x11e0 + - name: "WSAAsyncGetServByName" + ordinal: 107 + rva: 0x11e4 + - name: "WSACancelAsyncRequest" + ordinal: 108 + rva: 0x11e8 + - name: "WSASetBlockingHook" + ordinal: 109 + rva: 0x11ec + - name: "WSAUnhookBlockingHook" + ordinal: 110 + rva: 0x11f0 + - name: "WSAGetLastError" + ordinal: 111 + rva: 0x11f4 + - name: "WSASetLastError" + ordinal: 112 + rva: 0x11f8 + - name: "WSACancelBlockingCall" + ordinal: 113 + rva: 0x11fc + - name: "WSAIsBlocking" + ordinal: 114 + rva: 0x1200 + - name: "WSAStartup" + ordinal: 115 + rva: 0x1204 + - name: "WSACleanup" + ordinal: 116 + rva: 0x1208 + - name: "__WSAFDIsSet" + ordinal: 151 + rva: 0x120c + - name: "WEP" + ordinal: 500 + rva: 0x1210 + - name: "ord999" + ordinal: 999 + rva: 0x1214 +is_signed: false +overlay: + offset: 0x0 + size: 0x0 \ No newline at end of file From 63645746e6d825529d3e4fa20ac91de34e9b2592 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 17:03:25 +0200 Subject: [PATCH 3/7] fix: accuracy issues when computing time spent on each rule. --- lib/src/scanner/context.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/src/scanner/context.rs b/lib/src/scanner/context.rs index 44fb0eff..95ae7ff0 100644 --- a/lib/src/scanner/context.rs +++ b/lib/src/scanner/context.rs @@ -903,6 +903,20 @@ impl ScanContext<'_, '_> { ); } + #[cfg(feature = "rules-profiling")] + { + let time_spent = self + .clock + .delta_as_nanos(verification_start, self.clock.raw()); + + self.time_spent_in_pattern + .entry(*pattern_id) + .and_modify(|t| { + t.add_assign(time_spent); + }) + .or_insert(time_spent); + } + return; } @@ -1102,6 +1116,9 @@ impl ScanContext<'_, '_> { /// In case of timeout, this function returns [ScanError::Timeout] and sets /// the scan state to [ScanState::Timeout]. pub(crate) fn search_for_patterns(&mut self) -> Result<(), ScanError> { + #[cfg(any(feature = "rules-profiling", feature = "logging"))] + let scan_start = self.clock.raw(); + // Take ownership of the scan state, while searching for // the patterns, `self.scan_state` is left as `Idle`. let state = self.scan_state.take(); @@ -1137,9 +1154,6 @@ impl ScanContext<'_, '_> { } } - #[cfg(any(feature = "rules-profiling", feature = "logging"))] - let scan_start = self.clock.raw(); - // Verify the anchored pattern first. These are patterns that can // match at a single known offset within the data. self.verify_anchored_patterns(base, data); From 8f7ac9f492af4eb7cef13635aa6355787c89165a Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 17:20:45 +0200 Subject: [PATCH 4/7] refactor: optimize iteration for rule constraints The previous approach iterated over a range of pattern IDs and then performed a hash map lookup for each to retrieve filesize bounds and header constraints. This change modifies the `Rules` API to return the entire `FxHashMap`, enabling the scanner to directly iterate over the `(PatternId, Constraint)` pairs. This avoids redundant hash map lookups and improves performance in `ScanContext::update_rules_matching`. --- lib/src/compiler/rules.rs | 10 ++++------ lib/src/scanner/context.rs | 24 ++++++++++-------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/lib/src/compiler/rules.rs b/lib/src/compiler/rules.rs index 7df2d71f..eed86d5c 100644 --- a/lib/src/compiler/rules.rs +++ b/lib/src/compiler/rules.rs @@ -669,17 +669,15 @@ impl Rules { #[inline] pub(crate) fn filesize_bounds( &self, - pattern_id: PatternId, - ) -> Option<&FilesizeBounds> { - self.filesize_bounds.get(&pattern_id) + ) -> &FxHashMap { + &self.filesize_bounds } #[inline] pub(crate) fn header_constraints( &self, - pattern_id: PatternId, - ) -> Option<&HeaderConstraint> { - self.header_constraints.get(&pattern_id) + ) -> &FxHashMap { + &self.header_constraints } #[inline] diff --git a/lib/src/scanner/context.rs b/lib/src/scanner/context.rs index 95ae7ff0..d6efcbdc 100644 --- a/lib/src/scanner/context.rs +++ b/lib/src/scanner/context.rs @@ -1131,25 +1131,21 @@ impl ScanContext<'_, '_> { if !block_scanning_mode { let filesize = self.get_filesize(); - for pattern_id in 0..self.compiled_rules.num_patterns() { - let pattern_id = PatternId::from(pattern_id); - if let Some(bounds) = - self.compiled_rules.filesize_bounds(pattern_id) - && !bounds.contains(filesize) - { - self.tracker.disabled_patterns.insert(pattern_id); + for (pattern_id, bounds) in + self.compiled_rules.filesize_bounds() + { + if !bounds.contains(filesize) { + self.tracker.disabled_patterns.insert(*pattern_id); } } } if base == 0 { - for pattern_id in 0..self.compiled_rules.num_patterns() { - let pattern_id = PatternId::from(pattern_id); - if let Some(constraints) = - self.compiled_rules.header_constraints(pattern_id) - && !constraints.is_satisfied(data) - { - self.tracker.disabled_patterns.insert(pattern_id); + for (pattern_id, constraints) in + self.compiled_rules.header_constraints() + { + if !constraints.is_satisfied(data) { + self.tracker.disabled_patterns.insert(*pattern_id); } } } From f28330e13a52d7aeb2cf6b7f333075dbf81a3911 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 18:39:18 +0200 Subject: [PATCH 5/7] refactor: change return type for `filesize_bounds` and `header_constraints`. --- lib/src/compiler/rules.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/compiler/rules.rs b/lib/src/compiler/rules.rs index eed86d5c..0ccff08e 100644 --- a/lib/src/compiler/rules.rs +++ b/lib/src/compiler/rules.rs @@ -1,3 +1,4 @@ +use std::collections::hash_map; use std::fmt; use std::io::{BufWriter, Read, Write}; use std::ops::{Bound, RangeBounds}; @@ -669,15 +670,15 @@ impl Rules { #[inline] pub(crate) fn filesize_bounds( &self, - ) -> &FxHashMap { - &self.filesize_bounds + ) -> hash_map::Iter<'_, PatternId, FilesizeBounds> { + self.filesize_bounds.iter() } #[inline] pub(crate) fn header_constraints( &self, - ) -> &FxHashMap { - &self.header_constraints + ) -> hash_map::Iter<'_, PatternId, HeaderConstraint> { + self.header_constraints.iter() } #[inline] From c8e0f1033f4302165ffb634fd539a92d3d7a6c1f Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 19:31:31 +0200 Subject: [PATCH 6/7] style: run `cargo fmt`. --- lib/src/scanner/context.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/scanner/context.rs b/lib/src/scanner/context.rs index d6efcbdc..57fac190 100644 --- a/lib/src/scanner/context.rs +++ b/lib/src/scanner/context.rs @@ -1131,9 +1131,7 @@ impl ScanContext<'_, '_> { if !block_scanning_mode { let filesize = self.get_filesize(); - for (pattern_id, bounds) in - self.compiled_rules.filesize_bounds() - { + for (pattern_id, bounds) in self.compiled_rules.filesize_bounds() { if !bounds.contains(filesize) { self.tracker.disabled_patterns.insert(*pattern_id); } From 82adf904694cb9750746117ad1456a7a15fc8a7b Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 16 Jul 2026 21:46:58 +0200 Subject: [PATCH 7/7] chore: upgrade dependencies. --- Cargo.lock | 64 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 14 ++++++------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02f58522..9cbf740f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arbitrary" @@ -309,9 +309,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" dependencies = [ "serde_core", ] @@ -340,13 +340,13 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", "regex-automata", - "serde", + "serde_core", ] [[package]] @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" dependencies = [ "clap_builder", "clap_derive", @@ -475,9 +475,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstream", "anstyle", @@ -487,9 +487,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.5" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772" +checksum = "db8b397918185f0161ff3d6fcaa9e4bfc09b8367caf6e1d4a2848e5477ed027b" dependencies = [ "clap", ] @@ -854,7 +854,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crossterm_winapi", "derive_more", "document-features", @@ -905,9 +905,9 @@ dependencies = [ [[package]] name = "daachorse" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99251f238b74cd219a86fe6ea9328308ebb223fcbb5b8eb5aa400b847a41dded" +checksum = "5614204febbc33cc07a2806aa6440b904ac012b68eecc37f4493ea4a76455a3d" [[package]] name = "darling" @@ -1460,7 +1460,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "ignore", "walkdir", ] @@ -1993,7 +1993,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50ecbc2953846d9a3f8f6ed0700e66e49e5ad696eefb4e17fbaebbf457e1e752" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "magic-sys", ] @@ -2696,7 +2696,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -2725,7 +2725,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -2858,7 +2858,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2871,7 +2871,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3657,9 +3657,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "js-sys", "wasm-bindgen", @@ -3861,7 +3861,7 @@ version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f08c9adee0428b7bddf3890fc27e015ac4b761cc608c822667102b8bfd6995e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "hashbrown 0.16.1", "indexmap", "semver", @@ -3887,7 +3887,7 @@ checksum = "efb1ed5899dde98357cfdcf647a4614498798719793898245b4b34e663addabf" dependencies = [ "addr2line", "async-trait", - "bitflags 2.13.0", + "bitflags 2.13.1", "bumpalo", "cc", "cfg-if", @@ -4394,7 +4394,7 @@ dependencies = [ "anyhow", "base64", "bincode", - "bitflags 2.13.0", + "bitflags 2.13.1", "bitvec", "bstr", "codepage", @@ -4517,7 +4517,7 @@ dependencies = [ name = "yara-x-fmt" version = "1.19.0" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bstr", "globwalk", "goldenfile", @@ -4548,7 +4548,7 @@ name = "yara-x-ls" version = "1.19.0" dependencies = [ "async-lsp", - "bitflags 2.13.0", + "bitflags 2.13.1", "chrono", "console_error_panic_hook", "dashmap", @@ -4589,7 +4589,7 @@ version = "1.19.0" dependencies = [ "anyhow", "ascii_tree", - "bitflags 2.13.0", + "bitflags 2.13.1", "bstr", "env_logger", "globwalk", diff --git a/Cargo.toml b/Cargo.toml index 7d78701b..e6d31dd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,20 +31,20 @@ resolver = "3" [workspace.dependencies] -daachorse = "3.0.2" +daachorse = "3.0.3" annotate-snippets = "0.12.16" -anyhow = "1.0.102" +anyhow = "1.0.103" hex = "0.4.3" ascii_tree = "0.1.1" base64 = "0.22.1" bincode = "2.0.1" -bitflags = "2.13.0" +bitflags = "2.13.1" bitvec = "1.1.1" -bstr = "1.12.1" +bstr = "1.13.0" cbindgen = "0.29.4" chrono = "0.4.45" -clap = "4.6.1" -clap_complete = "4.6.5" +clap = "4.6.2" +clap_complete = "4.6.7" codepage = "0.1.2" const-oid = "0.9.6" crc32fast = "1.5.0" @@ -101,7 +101,7 @@ smallvec = "1.14.0" strum = "0.28.0" strum_macros = "0.28.0" thiserror = "2.0.18" -uuid = "1.23.3" +uuid = "1.24.0" walrus = "0.26.4" wasm-bindgen = "0.2.125" wasm-bindgen-test = "0.3.75"