diff --git a/capa/rules.py b/capa/rules.py index 6b9eba58..81df0c80 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -624,7 +624,25 @@ class Rule(object): continue meta[key] = value - return ostream.getvalue().decode("utf-8").rstrip("\n") + "\n" + doc = ostream.getvalue().decode("utf-8").rstrip("\n") + "\n" + # when we have something like: + # + # and: + # - string: foo + # description: bar + # + # we want the `description` horizontally aligned with the start of the `string` (like above). + # however, ruamel will give us (which I don't think is even valid yaml): + # + # and: + # - string: foo + # description: bar + # + # tweaking `ruamel.indent()` doesn't quite give us the control we want. + # so, add the two extra spaces that we've determined we need through experimentation. + # see #263 + doc = doc.replace(" description:", " description:") + return doc def get_rules_with_scope(rules, scope): diff --git a/tests/test_fmt.py b/tests/test_fmt.py index 1ca4725a..92bd4ffa 100644 --- a/tests/test_fmt.py +++ b/tests/test_fmt.py @@ -92,6 +92,8 @@ def test_rule_reformat_order(): def test_rule_reformat_meta_update(): + # test updating the rule content after parsing + rule = textwrap.dedent( """ rule: @@ -112,3 +114,24 @@ def test_rule_reformat_meta_update(): rule = capa.rules.Rule.from_yaml(rule) rule.name = "test rule" assert rule.to_yaml() == EXPECTED + + +def test_rule_reformat_string_description(): + # the `description` should be aligned with the preceding feature name. + # see #263 + src = textwrap.dedent( + """ + rule: + meta: + name: test rule + author: user@domain.com + scope: function + features: + - and: + - string: foo + description: bar + """ + ).lstrip() + + rule = capa.rules.Rule.from_yaml(src) + assert rule.to_yaml() == src