Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion capa/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_fmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading