From e74cbc1485db6217fa56700b6fc274382f1fcbf6 Mon Sep 17 00:00:00 2001 From: Sukesh Kumar Date: Wed, 12 Aug 2026 13:00:24 +0530 Subject: [PATCH 1/2] rules: anchor features offset to section header in rule formatter --- capa/rules/__init__.py | 5 ++++- tests/test_fmt.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/capa/rules/__init__.py b/capa/rules/__init__.py index ef4e372c70..434ee79186 100644 --- a/capa/rules/__init__.py +++ b/capa/rules/__init__.py @@ -1374,7 +1374,10 @@ def move_to_end(m, k): # see #263 # only do this for the features section, so the meta description doesn't get reformatted # assumes features section always exists - features_offset = doc.find("features") + # anchor on the section header rather than the bare word "features", + # since a rule's namespace (e.g. `impact/features/persistence`) + # may contain that substring and mislead the offset. see #3134 + features_offset = doc.find("\n features:") doc = doc[:features_offset] + doc[features_offset:].replace(" description:", " description:") # for negative hex numbers, yaml dump outputs: diff --git a/tests/test_fmt.py b/tests/test_fmt.py index 05b1fafcd7..20587b4766 100644 --- a/tests/test_fmt.py +++ b/tests/test_fmt.py @@ -148,3 +148,29 @@ def test_rule_reformat_string_description(): rule = capa.rules.Rule.from_yaml(src) assert rule.to_yaml() == src + + +def test_rule_reformat_namespace_with_features(): + # regression test for #3134 + # a namespace containing the substring "features" + # (e.g. `impact/features/persistence`) must not be mistaken for the + # `features:` section; otherwise the meta `description` gets + # incorrectly re-indented and the emitted YAML becomes invalid. + src = textwrap.dedent(""" + rule: + meta: + name: test rule + namespace: impact/features/persistence + authors: + - user@domain.com + description: this is a description + scopes: + static: function + dynamic: process + features: + - and: + - string: foo + description: bar + """).lstrip() + + assert capa.rules.Rule.from_yaml(src).to_yaml() == src From 016e2f78d001996e5c36ce463ea03f104a2ec7d1 Mon Sep 17 00:00:00 2001 From: Sukesh Kumar Date: Wed, 12 Aug 2026 19:29:23 +0530 Subject: [PATCH 2/2] docs: add capafmt namespace fix to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09dfb040bb..4a64144989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - ### Bug Fixes +- fix: prevent capafmt from corrupting rules whose namespace contains `features` @SkxOverKill #3143 - fix lots of linter errors identified by pyright @williballenthin #3052 - fix: render_default always returns empty string @williballenthin #3012 - fix: elf.py vdso_guess exception handler clobbers symtab_guess @williballenthin #3013