rules: add scope terms "unsupported" and "unspecified"

closes #1744
This commit is contained in:
Willi Ballenthin
2023-08-22 12:58:06 +00:00
parent 9489927bed
commit 4ab240e990
2 changed files with 154 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(
[