Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
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