fix: correct capa/subscope-rule key in RuleMetadata.from_capa

`RuleMetadata.from_capa` used `rule.meta.get("capa/subscope", False)` and
`Field(False, alias="capa/subscope")`, but the actual key set by
`_extract_subscope_rules_rec` is `"capa/subscope-rule"`. This caused
`is_subscope_rule` to always be `False` in every `RuleMetadata` instance,
making downstream filters in `render/utils.py`, `render/vverbose.py`, and
`scripts/import-to-ida.py` ineffective (though subscope rules are already
excluded from `ResultDocument` before reaching those callers).
This commit is contained in:
Willi Ballenthin
2026-04-22 17:29:38 +03:00
committed by Willi Ballenthin
parent 1ef6298b45
commit eb81901d71
5 changed files with 33 additions and 4 deletions
+1
View File
@@ -32,6 +32,7 @@
- fix: loader.py reads entire file for magic byte check @williballenthin #3029
- fix: freeze/__init__.py: logically impossible condition @williballenthin #3030
- fix: EXTENSIONS_ELF never referenced @williballenthin #3031
- fix: correct capa/subscope-rule key in RuleMetadata so is_subscope_rule is no longer always False @williballenthin
- fix: remove unreachable backports.functools_lru_cache fallback and dead dependency @williballenthin
- fix: Scopes.from_dict uses cls instead of self so subclasses return the correct type @williballenthin
+1 -1
View File
@@ -971,7 +971,7 @@ def rule_metadata_from_pb2(pb: capa_pb2.RuleMetadata) -> rd.RuleMetadata:
examples=tuple(pb.examples),
description=pb.description,
lib=pb.lib,
is_subscope_rule=pb.is_subscope_rule, # type: ignore # Pydantic alias capa/subscope; populate_by_name=True
is_subscope_rule=pb.is_subscope_rule, # type: ignore # Pydantic alias capa/subscope-rule; populate_by_name=True
maec=maec_from_pb2(pb.maec),
)
+2 -2
View File
@@ -660,7 +660,7 @@ class RuleMetadata(FrozenModel):
description: str
lib: bool = Field(False, alias="lib")
is_subscope_rule: bool = Field(False, alias="capa/subscope")
is_subscope_rule: bool = Field(False, alias="capa/subscope-rule")
maec: MaecMetadata
@classmethod
@@ -676,7 +676,7 @@ class RuleMetadata(FrozenModel):
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), # type: ignore # Pydantic alias capa/subscope; populate_by_name=True
is_subscope_rule=rule.meta.get("capa/subscope-rule", False), # type: ignore # Pydantic alias capa/subscope-rule; populate_by_name=True
maec=MaecMetadata(
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
+1 -1
View File
@@ -102,7 +102,7 @@ def load_analysis(bv):
for rule in doc["rules"].values():
if rule["meta"].get("lib"):
continue
if rule["meta"].get("capa/subscope"):
if rule["meta"].get("capa/subscope-rule"):
continue
if rule["meta"]["scopes"].get("static") != "function":
continue
+28
View File
@@ -13,6 +13,7 @@
# limitations under the License.
import copy
import textwrap
import pytest
import fixtures
@@ -296,3 +297,30 @@ def test_rdoc_to_capa():
meta, capabilites = rd.to_capa()
assert isinstance(meta, rdoc.Metadata)
assert isinstance(capabilites, Capabilities)
def test_rule_metadata_is_subscope_rule_alias():
rule = capa.rules.Rule.from_yaml(
textwrap.dedent("""
rule:
meta:
name: test rule
scopes:
static: function
dynamic: process
authors:
- test
features:
- api: CreateFile
""")
)
meta = rdoc.RuleMetadata.from_capa(rule)
assert meta.is_subscope_rule is False
raw = meta.model_dump(by_alias=True)
assert "capa/subscope-rule" in raw
assert raw["capa/subscope-rule"] is False
raw["capa/subscope-rule"] = True
meta_true = rdoc.RuleMetadata.model_validate(raw)
assert meta_true.is_subscope_rule is True