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
4 changes: 4 additions & 0 deletions sigma/rule/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ def from_dict(cls, val: list[dict[str, str]]) -> "SigmaRelated":

list_ret: list[SigmaRelatedItem] = []
for v in val:
if not isinstance(v, dict):
raise sigma_exceptions.SigmaRelatedError(
"Sigma related items must be maps with an id and type field"
)
if "id" not in v.keys():
raise sigma_exceptions.SigmaRelatedError("Sigma related must have an id field")
elif "type" not in v.keys():
Expand Down
22 changes: 18 additions & 4 deletions sigma/rule/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,21 @@ def get_rule_as_date(name: str, exception_class: type[SigmaError]) -> date | Non
# Rule level validation
rule_level = rule.get("level")
if rule_level is not None:
try:
rule_level = SigmaLevel[rule_level.upper()]
except KeyError:
if not isinstance(rule_level, str):
errors.append(
sigma_exceptions.SigmaLevelError(
f"'{ rule_level }' is not a valid Sigma rule level", source=source
"Sigma rule level must be a string", source=source
)
)
else:
try:
rule_level = SigmaLevel[rule_level.upper()]
except KeyError:
errors.append(
sigma_exceptions.SigmaLevelError(
f"'{ rule_level }' is not a valid Sigma rule level", source=source
)
)

# Rule status validation
rule_status = rule.get("status")
Expand Down Expand Up @@ -248,6 +255,13 @@ def get_rule_as_date(name: str, exception_class: type[SigmaError]) -> date | Non
)
else:
for tag in tags:
if not isinstance(tag, str):
errors.append(
sigma_exceptions.SigmaTagError(
"Sigma rule tags must be a list of strings", source=source
)
)
continue
try:
rule_tags.append(SigmaRuleTag.from_str(tag))
except sigma_exceptions.SigmaValueError as e:
Expand Down
68 changes: 68 additions & 0 deletions tests/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,74 @@ def test_sigmarule_collect_errors():
}


@pytest.mark.parametrize(
("rule", "expected_error"),
[
(
"""
title: Test
level: 123
logsource:
product: windows
detection:
selection_1:
fieldA: valueA
condition: selection_1
""",
sigma_exceptions.SigmaLevelError,
),
(
"""
title: Test
tags:
- 123
logsource:
product: windows
detection:
selection_1:
fieldA: valueA
condition: selection_1
""",
sigma_exceptions.SigmaTagError,
),
(
"""
title: Test
related:
- foo
logsource:
product: windows
detection:
selection_1:
fieldA: valueA
condition: selection_1
""",
sigma_exceptions.SigmaRelatedError,
),
],
)
def test_sigmarule_meta_field_type_errors_collected(rule, expected_error):
"""Non-string level, non-string tag and non-map related items must be reported
as validation errors, not crash with an AttributeError."""
parsed = SigmaRule.from_yaml(rule, collect_errors=True)
assert expected_error in {error.__class__ for error in parsed.errors}


def test_sigmarule_meta_field_type_errors_raised():
"""Without collect_errors the first recognized error is raised as a SigmaError."""
with pytest.raises(sigma_exceptions.SigmaLevelError):
SigmaRule.from_yaml("""
title: Test
level: 123
logsource:
product: windows
detection:
selection_1:
fieldA: valueA
condition: selection_1
""")


def test_sigmarule_no_logsource():
with pytest.raises(
sigma_exceptions.SigmaLogsourceError, match="must have a log source.*test.yml"
Expand Down