From 0655263ed3fb71a9c5da92d906d1b0883ff0c129 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 22 Apr 2026 15:42:42 +0300 Subject: [PATCH] fix: add inline explanations to all type: ignore comments --- capa/features/extractors/dotnetfile.py | 9 ++++++--- capa/features/freeze/__init__.py | 16 ++++++---------- capa/rules/__init__.py | 12 ++++++++---- tests/test_freeze_static.py | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/capa/features/extractors/dotnetfile.py b/capa/features/extractors/dotnetfile.py index 192d9385..8c6c6ced 100644 --- a/capa/features/extractors/dotnetfile.py +++ b/capa/features/extractors/dotnetfile.py @@ -18,7 +18,9 @@ from pathlib import Path import dnfile import pefile +import dnfile.mdtable +import capa.features.extractors.common import capa.features.extractors.helpers from capa.features.file import Import, FunctionName from capa.features.common import ( @@ -143,7 +145,8 @@ def extract_file_arch(pe: dnfile.dnPE, **kwargs) -> Iterator[tuple[Arch, Address def extract_file_strings(pe: dnfile.dnPE, **kwargs) -> Iterator[tuple[String, Address]]: - yield from capa.features.extractors.common.extract_file_strings(pe.__data__) + if pe.__data__ is not None: + yield from capa.features.extractors.common.extract_file_strings(bytes(pe.__data__)) def extract_file_mixed_mode_characteristic_features( @@ -252,8 +255,8 @@ class DotnetFileFeatureExtractor(StaticFeatureExtractor): def extract_insn_features(self, f, bb, insn): raise NotImplementedError("DotnetFileFeatureExtractor can only be used to extract file features") - def is_library_function(self, va): + def is_library_function(self, addr): raise NotImplementedError("DotnetFileFeatureExtractor can only be used to extract file features") - def get_function_name(self, va): + def get_function_name(self, addr): raise NotImplementedError("DotnetFileFeatureExtractor can only be used to extract file features") diff --git a/capa/features/freeze/__init__.py b/capa/features/freeze/__init__.py index eb876d39..57df23b2 100644 --- a/capa/features/freeze/__init__.py +++ b/capa/features/freeze/__init__.py @@ -387,11 +387,10 @@ def dumps_static(extractor: StaticFeatureExtractor) -> str: bbaddr = Address.from_capa(bb.address) bbfeatures = [ BasicBlockFeature( - basic_block=bbaddr, # type: ignore[call-arg] + basic_block=bbaddr, # type: ignore[call-arg] # pydantic alias "basic block" (with space) not recognized by type checkers address=Address.from_capa(addr), feature=feature_from_capa(feature), ) - # type checkers are unable to recognise `basic_block` as an argument due to alias for feature, addr in extractor.extract_basic_block_features(f, bb) ] @@ -426,21 +425,19 @@ def dumps_static(extractor: StaticFeatureExtractor) -> str: FunctionFeatures( address=faddr, features=tuple(ffeatures), - basic_blocks=basic_blocks, # type: ignore[call-arg] + basic_blocks=basic_blocks, # type: ignore[call-arg] # pydantic alias "basic blocks" not recognized by type checkers ) - # type checkers are unable to recognise `basic_blocks` as an argument due to alias ) features = StaticFeatures( - global_=global_features, # type: ignore[call-arg] + global_=global_features, # type: ignore[call-arg] # pydantic alias "global" not recognized by type checkers file=tuple(file_features), functions=tuple(function_features), ) - # type checkers are unable to recognise `global_` as an argument due to alias freeze = Freeze( version=CURRENT_VERSION, - base_address=Address.from_capa(extractor.get_base_address()), # type: ignore[call-arg] + base_address=Address.from_capa(extractor.get_base_address()), # type: ignore[call-arg] # pydantic alias "base address" not recognized by type checkers sample_hashes=extractor.get_sample_hashes(), flavor="static", extractor=Extractor(name=extractor.__class__.__name__), @@ -536,11 +533,10 @@ def dumps_dynamic(extractor: DynamicFeatureExtractor) -> str: ) features = DynamicFeatures( - global_=global_features, # type: ignore[call-arg] + global_=global_features, # type: ignore[call-arg] # pydantic alias "global" not recognized by type checkers file=tuple(file_features), processes=tuple(process_features), ) - # type checkers are unable to recognise `global_` as an argument due to alias # workaround around mypy issue: https://github.com/python/mypy/issues/1424 get_base_addr = getattr(extractor, "get_base_address", None) @@ -548,7 +544,7 @@ def dumps_dynamic(extractor: DynamicFeatureExtractor) -> str: freeze = Freeze( version=CURRENT_VERSION, - base_address=Address.from_capa(base_addr), # type: ignore[call-arg] + base_address=Address.from_capa(base_addr), # type: ignore[call-arg] # pydantic alias "base address" not recognized by type checkers sample_hashes=extractor.get_sample_hashes(), flavor="dynamic", extractor=Extractor(name=extractor.__class__.__name__), diff --git a/capa/rules/__init__.py b/capa/rules/__init__.py index 1eca8804..51f7c033 100644 --- a/capa/rules/__init__.py +++ b/capa/rules/__init__.py @@ -148,7 +148,7 @@ class Scopes: raise ValueError("invalid rules class. at least one scope must be specified") @classmethod - def from_dict(self, scopes: dict[str, str]) -> "Scopes": + def from_dict(cls, scopes: dict[str, str]) -> "Scopes": # make local copy so we don't make changes outside of this routine. # we'll use the value None to indicate the scope is not supported. scopes_: dict[str, Optional[str]] = dict(scopes) @@ -774,9 +774,10 @@ def build_statements(d, scopes: Scopes): value, description = parse_description(arg, term) if term == "api": + assert isinstance(value, str) value = trim_dll_part(value) - feature = Feature(value, description=description) + feature = Feature(value, description=description) # type: ignore[call-arg] # Feature is a runtime union; constructor args vary per subclass else: # arg is string (which doesn't support inline descriptions), like: # @@ -786,7 +787,7 @@ def build_statements(d, scopes: Scopes): # this may become a problem (or not), so address it when encountered. feature = Feature(arg) else: - feature = Feature() + feature = Feature() # type: ignore[call-arg] # Feature is a runtime union; constructor args vary per subclass ensure_feature_valid_for_scopes(scopes, feature) count = d[key] @@ -853,6 +854,7 @@ def build_statements(d, scopes: Scopes): raise InvalidRule(f"unexpected {key} access {access}") value, description = parse_description(d[key], key, d.get("description")) + assert isinstance(value, str) try: feature = capa.features.insn.Property(value, access=access, description=description) except ValueError as e: @@ -867,6 +869,7 @@ def build_statements(d, scopes: Scopes): except ValueError: raise InvalidRule(f"unexpected COM type: {com_type_name}") value, description = parse_description(d[key], key, d.get("description")) + assert isinstance(value, str) return translate_com_feature(value, com_type) else: @@ -874,10 +877,11 @@ def build_statements(d, scopes: Scopes): value, description = parse_description(d[key], key, d.get("description")) if key == "api": + assert isinstance(value, str) value = trim_dll_part(value) try: - feature = Feature(value, description=description) + feature = Feature(value, description=description) # type: ignore[misc] # Feature is a runtime union; constructor args vary per subclass except ValueError as e: raise InvalidRule(str(e)) from e ensure_feature_valid_for_scopes(scopes, feature) diff --git a/tests/test_freeze_static.py b/tests/test_freeze_static.py index 28d5e0c4..00cb02be 100644 --- a/tests/test_freeze_static.py +++ b/tests/test_freeze_static.py @@ -26,6 +26,7 @@ import capa.features.common import capa.features.freeze import capa.features.basicblock import capa.features.extractors.null +import capa.features.freeze.features import capa.features.extractors.base_extractor from capa.features.address import Address, AbsoluteVirtualAddress from capa.features.extractors.base_extractor import BBHandle, SampleHashes, FunctionHandle