Skip to content

Commit 9bbcbc1

Browse files
authored
Refractor SigmahqStatusToHighValidator for regression log (#73)
* Refractor SigmahqStatusToHighValidator for regression log * lint with black * Update sigmahq_status_to_high_validator pytests
1 parent 02718ec commit 9bbcbc1

File tree

2 files changed

+180
-72
lines changed

2 files changed

+180
-72
lines changed

sigma/validators/sigmahq/status.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,24 @@ class SigmahqStatusToHighIssue(SigmaValidationIssue):
5757
class SigmahqStatusToHighValidator(SigmaRuleValidator):
5858
"""Checks if a new rule has a valid status regarding its age"""
5959

60-
min_days: int = 60
60+
min_days_for_nolog_rule: int = 60
61+
min_days_for_log_rule: int = 0
6162

6263
def validate(self, rule: SigmaRule | SigmaCorrelationRule) -> List[SigmaValidationIssue]:
63-
if rule.date is not None and rule.status is not None:
64-
if rule.status > SigmaStatus.EXPERIMENTAL:
65-
if (datetime.now().date() - rule.date).days <= self.min_days:
66-
custom_keys = list(rule.custom_attributes.keys())
67-
if "regression_tests_path" not in custom_keys:
68-
return [SigmahqStatusToHighIssue([rule])]
64+
if rule.date is None or rule.status is None:
65+
return []
66+
67+
custom_keys = list(rule.custom_attributes.keys())
68+
max_status = (
69+
SigmaStatus.TEST if "regression_tests_path" in custom_keys else SigmaStatus.EXPERIMENTAL
70+
)
71+
min_days = (
72+
self.min_days_for_log_rule
73+
if "regression_tests_path" in custom_keys
74+
else self.min_days_for_nolog_rule
75+
)
76+
delta_days = (datetime.now().date() - rule.date).days
77+
78+
if rule.status > max_status and delta_days <= min_days:
79+
return [SigmahqStatusToHighIssue([rule])]
6980
return []
Lines changed: 162 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,146 @@
1-
from datetime import datetime
2-
3-
from sigma.rule import SigmaRule
1+
from datetime import date, timedelta
2+
import pytest
3+
from sigma.rule import SigmaRule, SigmaStatus
44
from sigma.correlations import SigmaCorrelationRule
55
from sigma.validators.sigmahq.status import (
66
SigmahqStatusToHighIssue,
77
SigmahqStatusToHighValidator,
88
)
99

10+
# Constants for test parameters with min_days configuration
11+
TEST_PARAMS = [
12+
# (min_nolog, min_log, days_ago, status, has_regression_tests, expected_has_issue)
13+
# Default minimum days (60 for non-log, 15 for log)
14+
(60, 15, 1, SigmaStatus.STABLE, False, True), # New STABLE rule fails
15+
(60, 15, 60, SigmaStatus.STABLE, False, True), # Exactly at min_nolog=60 fails
16+
(60, 15, 61, SigmaStatus.STABLE, False, False), # Just over min_nolog=60 passes
17+
(30, 15, 1, SigmaStatus.STABLE, False, True), # New STABLE rule fails with min_nolog=30
18+
(30, 15, 29, SigmaStatus.STABLE, False, True), # Just under min_nolog=30 fails
19+
(30, 15, 30, SigmaStatus.STABLE, False, True), # Exactly at min_nolog=30 fails
20+
(60, 15, 29, SigmaStatus.STABLE, True, False), # Regression test rule passes
21+
(60, 15, 14, SigmaStatus.STABLE, True, True), # Log rule with regression under min_log=15
22+
(30, 15, 29, SigmaStatus.TEST, True, False), # TEST status always passes
23+
]
1024

11-
def test_validator_SigmahqStatusToHigh():
12-
validator = SigmahqStatusToHighValidator()
13-
detection_rule = SigmaRule.from_yaml(
14-
"""
15-
title: Test
16-
description: Test
17-
status: stable
18-
date: 1975-01-01
19-
logsource:
20-
category: test
21-
detection:
22-
sel:
23-
candle|exists: true
24-
condition: sel
25-
"""
25+
26+
def create_test_rule(days_ago, status, has_regression_tests):
27+
"""Helper function to create test rules."""
28+
date_str = (date.today() - timedelta(days=days_ago)).strftime("%Y-%m-%d")
29+
30+
yaml_content = f"""
31+
title: Test Rule
32+
status: {status.name.lower()}
33+
date: {date_str}
34+
logsource:
35+
category: test
36+
product: windows
37+
detection:
38+
sel:
39+
candle|exists: true
40+
condition: sel
41+
"""
42+
43+
if has_regression_tests:
44+
yaml_content += "\nregression_tests_path: regression/rule/test_rule.yml"
45+
46+
return SigmaRule.from_yaml(yaml_content)
47+
48+
49+
def create_correlation_rule(days_ago, status, has_regression_tests):
50+
"""Helper function to create correlation test rules."""
51+
date_str = (date.today() - timedelta(days=days_ago)).strftime("%Y-%m-%d")
52+
53+
yaml_content = f"""
54+
title: Test Correlation
55+
id: 12345678-1234-1234-1234-123456789012
56+
status: {status.name.lower()}
57+
date: {date_str}
58+
logsource:
59+
category: correlation
60+
product: windows
61+
correlation:
62+
type: temporal
63+
rules:
64+
- 5638f7c0-ac70-491d-8465-2a65075e0d86
65+
timespan: 5m
66+
group-by:
67+
- ComputerName
68+
"""
69+
70+
if has_regression_tests:
71+
yaml_content += "\nregression_tests_path: regression/rule/test_rule.yml"
72+
73+
return SigmaCorrelationRule.from_yaml(yaml_content)
74+
75+
76+
@pytest.mark.parametrize(
77+
"min_nolog, min_log, days_ago, status, has_regression_tests, expected_has_issue", TEST_PARAMS
78+
)
79+
def test_status_validation_detection(
80+
min_nolog, min_log, days_ago, status, has_regression_tests, expected_has_issue
81+
):
82+
"""Test validation scenarios for detection rules with configurable minimum days."""
83+
rule = create_test_rule(days_ago, status, has_regression_tests)
84+
85+
validator = SigmahqStatusToHighValidator(
86+
min_days_for_nolog_rule=min_nolog, min_days_for_log_rule=min_log
2687
)
27-
detection_rule.date = datetime.now().date()
28-
assert validator.validate(detection_rule) == [SigmahqStatusToHighIssue([detection_rule])]
2988

89+
if expected_has_issue:
90+
assert validator.validate(rule) == [SigmahqStatusToHighIssue([rule])]
91+
else:
92+
assert validator.validate(rule) == []
3093

31-
def test_validator_SigmahqStatusToHigh_valid():
32-
validator = SigmahqStatusToHighValidator()
33-
detection_rule = SigmaRule.from_yaml(
34-
"""
35-
title: Test
36-
description: Test
37-
status: stable
38-
date: 1975-01-01
39-
logsource:
40-
category: test
41-
detection:
42-
sel:
43-
candle|exists: true
44-
condition: sel
45-
"""
94+
95+
@pytest.mark.parametrize(
96+
"min_nolog, min_log, days_ago, status, has_regression_tests, expected_has_issue", TEST_PARAMS
97+
)
98+
def test_status_validation_correlation(
99+
min_nolog, min_log, days_ago, status, has_regression_tests, expected_has_issue
100+
):
101+
"""Test validation scenarios for correlation rules with configurable minimum days."""
102+
rule = create_correlation_rule(days_ago, status, has_regression_tests)
103+
104+
validator = SigmahqStatusToHighValidator(
105+
min_days_for_nolog_rule=min_nolog, min_days_for_log_rule=min_log
46106
)
47-
assert validator.validate(detection_rule) == []
107+
if expected_has_issue:
108+
assert validator.validate(rule) == [SigmahqStatusToHighIssue([rule])]
109+
else:
110+
assert validator.validate(rule) == []
111+
48112

113+
def test_rules_without_date():
114+
"""Test that rules without dates always pass validation."""
115+
# Test with different minimum days configurations
116+
validators = [
117+
SigmahqStatusToHighValidator(min_days_for_nolog_rule=60, min_days_for_log_rule=15),
118+
SigmahqStatusToHighValidator(min_days_for_nolog_rule=30, min_days_for_log_rule=15),
119+
]
49120

50-
def test_validator_SigmahqStatusToHigh_with_regression_valid():
51-
validator = SigmahqStatusToHighValidator()
121+
# Test detection rule without date
52122
detection_rule = SigmaRule.from_yaml(
53123
"""
54-
title: Test
55-
description: Test
56-
status: test
57-
date: 1975-01-01
58-
logsource:
59-
category: test
60-
detection:
61-
sel:
62-
candle|exists: true
63-
condition: sel
64-
regression_tests_path: regression/rule/test_rule.yml
65-
"""
124+
title: Rule Without Date
125+
status: stable
126+
logsource:
127+
category: test
128+
detection:
129+
sel:
130+
candle|exists: true
131+
condition: sel
132+
"""
66133
)
67-
detection_rule.date = datetime.now().date()
68-
assert validator.validate(detection_rule) == []
69134

70-
71-
def test_validator_SigmahqStatusToHigh_correlation():
72-
validator = SigmahqStatusToHighValidator()
135+
# Test correlation rule without date
73136
correlation_rule = SigmaCorrelationRule.from_yaml(
74137
"""
75-
title: Test Correlation
76-
id: 0e95725d-7320-415d-80f7-004da920fc11
138+
title: Correlation Rule Without Date
139+
id: 12345678-1234-1234-1234-123456789012
77140
status: stable
141+
logsource:
142+
category: correlation
143+
product: windows
78144
correlation:
79145
type: temporal
80146
rules:
@@ -84,17 +150,44 @@ def test_validator_SigmahqStatusToHigh_correlation():
84150
- ComputerName
85151
"""
86152
)
87-
correlation_rule.date = datetime.now().date()
88-
assert validator.validate(correlation_rule) == [SigmahqStatusToHighIssue([correlation_rule])]
153+
154+
# All validators should pass rules without dates
155+
for validator in validators:
156+
assert validator.validate(detection_rule) == []
157+
assert validator.validate(correlation_rule) == []
89158

90159

91-
def test_validator_SigmahqStatusToHigh_correlation_valid():
92-
validator = SigmahqStatusToHighValidator()
160+
def test_rules_without_status():
161+
"""Test that rules without dates always pass validation."""
162+
# Test with different minimum days configurations
163+
validators = [
164+
SigmahqStatusToHighValidator(min_days_for_nolog_rule=60, min_days_for_log_rule=15),
165+
SigmahqStatusToHighValidator(min_days_for_nolog_rule=30, min_days_for_log_rule=15),
166+
]
167+
168+
# Test detection rule without date
169+
detection_rule = SigmaRule.from_yaml(
170+
"""
171+
title: Rule Without Status
172+
date: 2030-01-01
173+
logsource:
174+
category: test
175+
detection:
176+
sel:
177+
candle|exists: true
178+
condition: sel
179+
"""
180+
)
181+
182+
# Test correlation rule without date
93183
correlation_rule = SigmaCorrelationRule.from_yaml(
94184
"""
95-
title: Test Correlation
96-
id: 0e95725d-7320-415d-80f7-004da920fc11
97-
status: test
185+
title: Correlation Rule Without Status
186+
id: 12345678-1234-1234-1234-123456789012
187+
date: 2030-01-01
188+
logsource:
189+
category: correlation
190+
product: windows
98191
correlation:
99192
type: temporal
100193
rules:
@@ -104,4 +197,8 @@ def test_validator_SigmahqStatusToHigh_correlation_valid():
104197
- ComputerName
105198
"""
106199
)
107-
assert validator.validate(correlation_rule) == []
200+
201+
# All validators should pass rules without dates
202+
for validator in validators:
203+
assert validator.validate(detection_rule) == []
204+
assert validator.validate(correlation_rule) == []

0 commit comments

Comments
 (0)