Skip to content

Commit

Permalink
Merge pull request #253 from nasbench/add-dangling-cond-validator
Browse files Browse the repository at this point in the history
add new validator - DanglingCondition
  • Loading branch information
thomaspatzke authored Aug 9, 2024
2 parents d8f5124 + 0bcd476 commit 7e65b81
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 2 deletions.
61 changes: 59 additions & 2 deletions sigma/validators/core/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def condition_referenced_ids(
self, cond: ConditionItem, detections: SigmaDetections
) -> Set[str]:
"""
Return detection item identifierd referenced by condition.
Return detection item identifier referenced by condition.
:param cond: Condition to analyze.
:type cond: ConditionItem
Expand Down Expand Up @@ -64,14 +64,71 @@ def validate(self, rule: SigmaRule) -> List[SigmaValidationIssue]:
return [DanglingDetectionIssue([rule], name) for name in detection_names - referenced_ids]


@dataclass
class DanglingConditionIssue(SigmaValidationIssue):
description: ClassVar[str] = (
"Rule defines a condition that contains references to an unknown detection definition"
)
severity: ClassVar[SigmaValidationIssueSeverity] = SigmaValidationIssueSeverity.HIGH
condition_name: str


class DanglingConditionValidator(SigmaRuleValidator):
"""Check for non existing detection definitions referenced from the condition."""

def condition_unknown_referenced_ids(
self, cond: ConditionItem, detections: SigmaDetections
) -> Set[str]:
"""
Return set of unknown item identifier referenced by condition.
:param cond: Condition to analyze.
:type cond: ConditionItem
:param detections: Detections referenced from condition.
:type detections: SigmaDetections
:return: Set of unknown referenced detection identifiers.
:rtype: Set[str]
"""
if isinstance(cond, ConditionSelector): # Resolve all referenced ids and return
resolved_detections = {
cond.identifier for cond in cond.resolve_referenced_detections(detections)
}
if (
resolved_detections == set()
): # If the function returns an empty set, it means that there was no corresponding detection identifier for the condition identifier.
return {cond.pattern}
else:
return {}
elif isinstance(cond, ConditionItem): # Traverse into subconditions
ids = set()
for arg in cond.args:
ids.update(self.condition_unknown_referenced_ids(arg, detections))
return ids
else: # Fallback if something different is encountered: return empty set.
return set()

def validate(self, rule: SigmaRule) -> List[SigmaValidationIssue]:
if isinstance(rule, SigmaCorrelationRule):
return [] # Correlation rules do not have detections

unknown_detection_refs = set()
for condition in rule.detection.parsed_condition:
parsed_condition = condition.parse(False)
unknown_detection_refs.update(
self.condition_unknown_referenced_ids(parsed_condition, rule.detection)
)

return [DanglingConditionIssue([rule], name) for name in unknown_detection_refs]


@dataclass
class ThemConditionWithSingleDetectionIssue(SigmaValidationIssue):
description: ClassVar[str] = "Rule refers to 'them' but has only one condition"
severity: ClassVar[SigmaValidationIssueSeverity] = SigmaValidationIssueSeverity.LOW


class ThemConditionWithSingleDetectionValidator(SigmaRuleValidator):
"""Detect conditions refering to 'them' with only one detection."""
"""Detect conditions referring to 'them' with only one detection."""

def validate(self, rule: SigmaRule) -> List[SigmaValidationIssue]:
if isinstance(rule, SigmaCorrelationRule):
Expand Down
86 changes: 86 additions & 0 deletions tests/test_validators_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,36 @@
AllOfThemConditionIssue,
AllOfThemConditionValidator,
DanglingDetectionIssue,
DanglingConditionIssue,
DanglingDetectionValidator,
DanglingConditionValidator,
ThemConditionWithSingleDetectionIssue,
ThemConditionWithSingleDetectionValidator,
)
from .test_correlations import correlation_rule


def test_validator_dangling_condition():
validator = DanglingConditionValidator()
rule = SigmaRule.from_yaml(
"""
title: Test
status: test
logsource:
category: test
detection:
selection:
field1: val1
filter_main_1:
field1: val1
filter_main_optional_1:
field2: val2
condition: selection and not 1 of filter_main_* and not 1 of filter_optional_*
"""
)
assert validator.validate(rule) == [DanglingConditionIssue([rule], "filter_optional_*")]


def test_validator_dangling_detection():
validator = DanglingDetectionValidator()
rule = SigmaRule.from_yaml(
Expand Down Expand Up @@ -60,6 +83,27 @@ def test_validator_dangling_detection_valid():
assert validator.validate(rule) == []


def test_validator_dangling_condition_valid():
validator = DanglingConditionValidator()
rule = SigmaRule.from_yaml(
"""
title: Test
status: test
logsource:
category: test
detection:
referenced1:
field1: val1
referenced2:
field2: val2
referenced3:
field3: val3
condition: (referenced1 or referenced2) and referenced3
"""
)
assert validator.validate(rule) == []


def test_validator_dangling_detection_valid_x_of_wildcard():
validator = DanglingDetectionValidator()
rule = SigmaRule.from_yaml(
Expand All @@ -81,6 +125,27 @@ def test_validator_dangling_detection_valid_x_of_wildcard():
assert validator.validate(rule) == []


def test_validator_dangling_condition_valid_x_of_wildcard():
validator = DanglingConditionValidator()
rule = SigmaRule.from_yaml(
"""
title: Test
status: test
logsource:
category: test
detection:
referenced1:
field1: val1
referenced2:
field2: val2
referenced3:
field3: val3
condition: 1 of referenced*
"""
)
assert validator.validate(rule) == []


def test_validator_dangling_detection_valid_x_of_them():
validator = DanglingDetectionValidator()
rule = SigmaRule.from_yaml(
Expand All @@ -102,6 +167,27 @@ def test_validator_dangling_detection_valid_x_of_them():
assert validator.validate(rule) == []


def test_validator_dangling_condition_valid_x_of_them():
validator = DanglingConditionValidator()
rule = SigmaRule.from_yaml(
"""
title: Test
status: test
logsource:
category: test
detection:
referenced1:
field1: val1
referenced2:
field2: val2
referenced3:
field3: val3
condition: 1 of them
"""
)
assert validator.validate(rule) == []


def test_validator_them_condition_with_single_detection():
validator = ThemConditionWithSingleDetectionValidator()
rule = SigmaRule.from_yaml(
Expand Down

0 comments on commit 7e65b81

Please sign in to comment.