Merge pull request #1748 from mandiant/feat/issue-1744

rules: add scope terms "unsupported" and "unspecified"
This commit is contained in:
Willi Ballenthin
2023-08-22 15:59:57 +02:00
committed by GitHub
3 changed files with 156 additions and 2 deletions

View File

@@ -143,13 +143,27 @@ class Scopes:
# mark non-specified scopes as invalid
if "static" not in scopes:
scopes["static"] = None
raise InvalidRule("static scope must be provided")
if "dynamic" not in scopes:
scopes["dynamic"] = None
raise InvalidRule("dynamic scope must be provided")
# check the syntax of the meta `scopes` field
if sorted(scopes) != ["dynamic", "static"]:
raise InvalidRule("scope flavors can be either static or dynamic")
if scopes["static"] == "unsupported":
scopes["static"] = None
if scopes["dynamic"] == "unsupported":
scopes["dynamic"] = None
# unspecified is used to indicate a rule is yet to be migrated.
# TODO(williballenthin): this scope term should be removed once all rules have been migrated.
# https://github.com/mandiant/capa/issues/1747
if scopes["static"] == "unspecified":
scopes["static"] = None
if scopes["dynamic"] == "unspecified":
scopes["dynamic"] = None
if (not scopes["static"]) and (not scopes["dynamic"]):
raise InvalidRule("invalid scopes value. At least one scope must be specified")

View File

@@ -388,6 +388,7 @@ def test_rules_flavor_filtering():
name: static rule
scopes:
static: function
dynamic: unsupported
features:
- api: CreateFileA
"""
@@ -400,6 +401,7 @@ def test_rules_flavor_filtering():
meta:
name: dynamic rule
scopes:
static: unsupported
dynamic: thread
features:
- api: CreateFileA
@@ -417,6 +419,142 @@ def test_rules_flavor_filtering():
assert len(dynamic_rules) == 1
def test_meta_scope_keywords():
for static_scope in sorted(capa.rules.STATIC_SCOPES):
for dynamic_scope in sorted(capa.rules.DYNAMIC_SCOPES):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
f"""
rule:
meta:
name: test rule
scopes:
static: {static_scope}
dynamic: {dynamic_scope}
features:
- or:
- format: pe
"""
)
)
# its also ok to specify "unsupported"
for static_scope in sorted(capa.rules.STATIC_SCOPES):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
f"""
rule:
meta:
name: test rule
scopes:
static: {static_scope}
dynamic: unsupported
features:
- or:
- format: pe
"""
)
)
for dynamic_scope in sorted(capa.rules.DYNAMIC_SCOPES):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
f"""
rule:
meta:
name: test rule
scopes:
static: unsupported
dynamic: {dynamic_scope}
features:
- or:
- format: pe
"""
)
)
# its also ok to specify "unspecified"
for static_scope in sorted(capa.rules.STATIC_SCOPES):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
f"""
rule:
meta:
name: test rule
scopes:
static: {static_scope}
dynamic: unspecified
features:
- or:
- format: pe
"""
)
)
for dynamic_scope in sorted(capa.rules.DYNAMIC_SCOPES):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
f"""
rule:
meta:
name: test rule
scopes:
static: unspecified
dynamic: {dynamic_scope}
features:
- or:
- format: pe
"""
)
)
# but at least one scope must be specified
with pytest.raises(capa.rules.InvalidRule):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scopes: {}
features:
- or:
- format: pe
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scopes:
static: unsupported
dynamic: unsupported
features:
- or:
- format: pe
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
_ = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scopes:
static: unspecified
dynamic: unspecified
features:
- or:
- format: pe
"""
)
)
def test_lib_rules():
rules = capa.rules.RuleSet(
[

View File

@@ -22,6 +22,7 @@ def test_rule_scope_instruction():
name: test rule
scopes:
static: instruction
dynamic: unsupported
features:
- and:
- mnemonic: mov
@@ -40,6 +41,7 @@ def test_rule_scope_instruction():
name: test rule
scopes:
static: instruction
dynamic: unsupported
features:
- characteristic: embedded pe
"""