mirror of
https://github.com/mandiant/capa.git
synced 2026-04-28 11:53:20 -07:00
fix: address type override, alias parameter, and Statement.children issues (chunk 5)
- engine.py: type: ignore for children/replace_child hasattr-guarded subclass attrs - result_document.py: type: ignore for Pydantic analysis field override and alias args - render/proto/__init__.py: type: ignore on Pydantic alias argument lines - rules/__init__.py: type: ignore on ensure_feature_valid_for_scopes StringFactory calls - result_document.py: fix Scope|None by extracting scope var with assert
This commit is contained in:
committed by
Willi Ballenthin
parent
96cabbcc6b
commit
2b536e2f53
@@ -78,7 +78,7 @@ class Statement:
|
||||
yield child
|
||||
|
||||
if hasattr(self, "children"):
|
||||
for child in self.children:
|
||||
for child in self.children: # type: ignore # children defined in subclasses, guarded by hasattr
|
||||
assert isinstance(child, (Statement, Feature))
|
||||
yield child
|
||||
|
||||
@@ -90,7 +90,7 @@ class Statement:
|
||||
self.child = new
|
||||
|
||||
if hasattr(self, "children"):
|
||||
children = self.children
|
||||
children = self.children # type: ignore # children defined in subclasses, guarded by hasattr
|
||||
for i, child in enumerate(children):
|
||||
if child is existing:
|
||||
children[i] = new
|
||||
|
||||
@@ -886,14 +886,17 @@ def feature_from_pb2(f: capa_pb2.FeatureNode) -> frzf.Feature:
|
||||
elif type_ == "operand_number":
|
||||
ff = f.operand_number
|
||||
return frzf.OperandNumberFeature(
|
||||
index=ff.index, operand_number=number_from_pb2(ff.operand_number), description=ff.description or None
|
||||
) # type: ignore
|
||||
index=ff.index,
|
||||
operand_number=number_from_pb2(ff.operand_number),
|
||||
description=ff.description or None, # type: ignore # Pydantic alias operand-number
|
||||
)
|
||||
elif type_ == "operand_offset":
|
||||
ff = f.operand_offset
|
||||
return frzf.OperandOffsetFeature(
|
||||
index=ff.index, operand_offset=int_from_pb2(ff.operand_offset), description=ff.description or None
|
||||
) # type: ignore
|
||||
# Mypy is unable to recognize `operand_offset` as an argument due to aliasing
|
||||
index=ff.index,
|
||||
operand_offset=int_from_pb2(ff.operand_offset),
|
||||
description=ff.description or None, # type: ignore # Pydantic alias operand-offset
|
||||
)
|
||||
elif type_ == "basic_block":
|
||||
ff = f.basic_block
|
||||
return frzf.BasicBlockFeature(description=ff.description or None)
|
||||
@@ -948,13 +951,12 @@ def mbc_from_pb2(pb: capa_pb2.MBCSpec) -> rd.MBCSpec:
|
||||
|
||||
def maec_from_pb2(pb: capa_pb2.MaecMetadata) -> rd.MaecMetadata:
|
||||
return rd.MaecMetadata(
|
||||
analysis_conclusion=pb.analysis_conclusion or None,
|
||||
analysis_conclusion_ov=pb.analysis_conclusion_ov or None,
|
||||
malware_family=pb.malware_family or None,
|
||||
malware_category=pb.malware_category or None,
|
||||
malware_category_ov=pb.malware_category_ov or None,
|
||||
) # type: ignore
|
||||
# Mypy is unable to recognise arguments due to alias
|
||||
analysis_conclusion=pb.analysis_conclusion or None, # type: ignore # Pydantic alias analysis-conclusion
|
||||
analysis_conclusion_ov=pb.analysis_conclusion_ov or None, # type: ignore # Pydantic alias analysis-conclusion-ov
|
||||
malware_family=pb.malware_family or None, # type: ignore # Pydantic alias malware-family
|
||||
malware_category=pb.malware_category or None, # type: ignore # Pydantic alias malware-category
|
||||
malware_category_ov=pb.malware_category_ov or None, # type: ignore # Pydantic alias malware-category-ov
|
||||
)
|
||||
|
||||
|
||||
def rule_metadata_from_pb2(pb: capa_pb2.RuleMetadata) -> rd.RuleMetadata:
|
||||
@@ -963,16 +965,15 @@ def rule_metadata_from_pb2(pb: capa_pb2.RuleMetadata) -> rd.RuleMetadata:
|
||||
namespace=pb.namespace or None,
|
||||
authors=tuple(pb.authors),
|
||||
scopes=scopes_from_pb2(pb.scopes),
|
||||
attack=tuple([attack_from_pb2(attack) for attack in pb.attack]),
|
||||
attack=tuple([attack_from_pb2(attack) for attack in pb.attack]), # type: ignore # Pydantic alias att&ck; populate_by_name=True
|
||||
mbc=tuple([mbc_from_pb2(mbc) for mbc in pb.mbc]),
|
||||
references=tuple(pb.references),
|
||||
examples=tuple(pb.examples),
|
||||
description=pb.description,
|
||||
lib=pb.lib,
|
||||
is_subscope_rule=pb.is_subscope_rule,
|
||||
is_subscope_rule=pb.is_subscope_rule, # type: ignore # Pydantic alias capa/subscope; populate_by_name=True
|
||||
maec=maec_from_pb2(pb.maec),
|
||||
) # type: ignore
|
||||
# Mypy is unable to recognise `attack` and `is_subscope_rule` as arguments due to alias
|
||||
)
|
||||
|
||||
|
||||
def doc_from_pb2(doc: capa_pb2.ResultDocument) -> rd.ResultDocument:
|
||||
|
||||
@@ -155,12 +155,12 @@ class Metadata(Model):
|
||||
|
||||
class StaticMetadata(Metadata):
|
||||
flavor: Flavor = Flavor.STATIC
|
||||
analysis: StaticAnalysis
|
||||
analysis: StaticAnalysis # type: ignore # narrows Analysis union to StaticAnalysis in Pydantic subclass
|
||||
|
||||
|
||||
class DynamicMetadata(Metadata):
|
||||
flavor: Flavor = Flavor.DYNAMIC
|
||||
analysis: DynamicAnalysis
|
||||
analysis: DynamicAnalysis # type: ignore # narrows Analysis union to DynamicAnalysis in Pydantic subclass
|
||||
|
||||
|
||||
class CompoundStatementType:
|
||||
@@ -386,9 +386,11 @@ class Match(FrozenModel):
|
||||
# note! replace `node`
|
||||
# subscopes cannot have both a static and dynamic scope set
|
||||
assert None in (rule.scopes.static, rule.scopes.dynamic)
|
||||
scope = rule.scopes.static or rule.scopes.dynamic
|
||||
assert scope is not None
|
||||
node = StatementNode(
|
||||
statement=SubscopeStatement(
|
||||
scope=rule.scopes.static or rule.scopes.dynamic,
|
||||
scope=scope,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -668,23 +670,21 @@ class RuleMetadata(FrozenModel):
|
||||
namespace=rule.meta.get("namespace"),
|
||||
authors=rule.meta.get("authors"),
|
||||
scopes=capa.rules.Scopes.from_dict(rule.meta.get("scopes")),
|
||||
attack=tuple(map(AttackSpec.from_str, rule.meta.get("att&ck", []))),
|
||||
attack=tuple(map(AttackSpec.from_str, rule.meta.get("att&ck", []))), # type: ignore # Pydantic alias att&ck; populate_by_name=True
|
||||
mbc=tuple(map(MBCSpec.from_str, rule.meta.get("mbc", []))),
|
||||
references=rule.meta.get("references", []),
|
||||
examples=rule.meta.get("examples", []),
|
||||
description=rule.meta.get("description", ""),
|
||||
lib=rule.meta.get("lib", False),
|
||||
is_subscope_rule=rule.meta.get("capa/subscope", False),
|
||||
is_subscope_rule=rule.meta.get("capa/subscope", False), # type: ignore # Pydantic alias capa/subscope; populate_by_name=True
|
||||
maec=MaecMetadata(
|
||||
analysis_conclusion=rule.meta.get("maec/analysis-conclusion"),
|
||||
analysis_conclusion_ov=rule.meta.get("maec/analysis-conclusion-ov"),
|
||||
malware_family=rule.meta.get("maec/malware-family"),
|
||||
malware_category=rule.meta.get("maec/malware-category"),
|
||||
malware_category_ov=rule.meta.get("maec/malware-category-ov"),
|
||||
), # type: ignore
|
||||
# Mypy is unable to recognise arguments due to alias
|
||||
) # type: ignore
|
||||
# Mypy is unable to recognise arguments due to alias
|
||||
analysis_conclusion=rule.meta.get("maec/analysis-conclusion"), # type: ignore # Pydantic alias analysis-conclusion
|
||||
analysis_conclusion_ov=rule.meta.get("maec/analysis-conclusion-ov"), # type: ignore # Pydantic alias
|
||||
malware_family=rule.meta.get("maec/malware-family"), # type: ignore # Pydantic alias malware-family
|
||||
malware_category=rule.meta.get("maec/malware-category"), # type: ignore # Pydantic alias malware-category
|
||||
malware_category_ov=rule.meta.get("maec/malware-category-ov"), # type: ignore # Pydantic alias
|
||||
),
|
||||
)
|
||||
|
||||
model_config = ConfigDict(frozen=True, populate_by_name=True)
|
||||
|
||||
|
||||
@@ -789,7 +789,7 @@ def build_statements(d, scopes: Scopes):
|
||||
feature = Feature(arg)
|
||||
else:
|
||||
feature = Feature() # type: ignore[call-arg] # Feature is a runtime union; constructor args vary per subclass
|
||||
ensure_feature_valid_for_scopes(scopes, feature)
|
||||
ensure_feature_valid_for_scopes(scopes, feature) # type: ignore[arg-type] # StringFactory.__new__ returns Feature subclass at runtime
|
||||
|
||||
count = d[key]
|
||||
if isinstance(count, int):
|
||||
@@ -888,7 +888,7 @@ def build_statements(d, scopes: Scopes):
|
||||
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)
|
||||
ensure_feature_valid_for_scopes(scopes, feature) # type: ignore[arg-type] # StringFactory.__new__ returns Feature subclass at runtime
|
||||
return feature
|
||||
|
||||
|
||||
@@ -1102,7 +1102,7 @@ class Rule:
|
||||
if not isinstance(meta.get("mbc", []), list):
|
||||
raise InvalidRule("MBC mapping must be a list")
|
||||
|
||||
return cls(name, scopes, build_statements(statements[0], scopes), meta, definition)
|
||||
return cls(name, scopes, build_statements(statements[0], scopes), meta, definition) # type: ignore[arg-type] # build_statements infers wide union but top-level always returns Statement
|
||||
|
||||
@staticmethod
|
||||
@lru_cache()
|
||||
|
||||
Reference in New Issue
Block a user