Skip to content

Commit d829b14

Browse files
[Bug] Fix Tag Navigator Generation (#2875)
* bug fix for tag navigator generation * addressing flake errors * added unit test to ensure prefix exists * updated unit test case sensitivity * moved expected tags to definitions.py * removed expected prefixes * revert downloadable updates JSON file
1 parent b4c84e8 commit d829b14

File tree

4 files changed

+78
-62
lines changed

4 files changed

+78
-62
lines changed

detection_rules/navigator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"""Create summary documents for a rule package."""
77

8+
from functools import reduce
89
from collections import defaultdict
910
from dataclasses import dataclass, field, fields
1011
from pathlib import Path
@@ -17,6 +18,7 @@
1718
from .attack import CURRENT_ATTACK_VERSION
1819
from .mixins import MarshmallowDataclassMixin
1920
from .rule import TOMLRule
21+
from .schemas import definitions
2022

2123

2224
_DEFAULT_PLATFORMS = [
@@ -186,6 +188,8 @@ def _update_indexes(self, rule: TOMLRule, tactic: str, technique_id: str):
186188
def _update_tags(self, rule: TOMLRule, tactic: str, technique_id: str):
187189
for tag in rule.contents.data.get('tags', []):
188190
value = rule.id
191+
expected_prefixes = set([tag.split(":")[0] + ":" for tag in definitions.EXPECTED_RULE_TAGS])
192+
tag = reduce(lambda s, substr: s.replace(substr, ''), expected_prefixes, tag).lstrip()
189193
layer_key = tag.replace(' ', '-').lower()
190194
self.add_rule_to_technique(rule, 'tags', tactic, technique_id, value, layer_key=layer_key)
191195

detection_rules/schemas/definitions.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,66 @@
5454
'4434b91a-94ca-4a89-83cb-a37cdc0532b7': 'Alerts Involving a Single Host Timeline'
5555
}
5656

57+
EXPECTED_RULE_TAGS = [
58+
'Data Source: Active Directory',
59+
'Data Source: Amazon Web Services',
60+
'Data Source: AWS',
61+
'Data Source: APM',
62+
'Data Source: Azure',
63+
'Data Source: CyberArk PAS',
64+
'Data Source: Elastic Defend',
65+
'Data Source: Elastic Defend for Containers',
66+
'Data Source: Elastic Endgame',
67+
'Data Source: GCP',
68+
'Data Source: Google Cloud Platform',
69+
'Data Source: Google Workspace',
70+
'Data Source: Kubernetes',
71+
'Data Source: Microsoft 365',
72+
'Data Source: Okta',
73+
'Data Source: PowerShell Logs',
74+
'Data Source: Sysmon Only',
75+
'Data Source: Zoom',
76+
'Domain: Cloud',
77+
'Domain: Container',
78+
'Domain: Endpoint',
79+
'OS: Linux',
80+
'OS: macOS',
81+
'OS: Windows',
82+
'Resources: Investigation Guide',
83+
'Rule Type: Higher-Order Rule',
84+
'Rule Type: Machine Learning',
85+
'Rule Type: ML',
86+
'Tactic: Collection',
87+
'Tactic: Command and Control',
88+
'Tactic: Credential Access',
89+
'Tactic: Defense Evasion',
90+
'Tactic: Discovery',
91+
'Tactic: Execution',
92+
'Tactic: Exfiltration',
93+
'Tactic: Impact',
94+
'Tactic: Initial Access',
95+
'Tactic: Lateral Movement',
96+
'Tactic: Persistence',
97+
'Tactic: Privilege Escalation',
98+
'Tactic: Reconnaissance',
99+
'Tactic: Resource Development',
100+
'Threat: BPFDoor',
101+
'Threat: Cobalt Strike',
102+
'Threat: Lightning Framework',
103+
'Threat: Orbit',
104+
'Threat: Rootkit',
105+
'Threat: TripleCross',
106+
'Use Case: Active Directory Monitoring',
107+
'Use Case: Asset Visibility',
108+
'Use Case: Configuration Audit',
109+
'Use Case: Guided Onboarding',
110+
'Use Case: Identity and Access Audit',
111+
'Use Case: Log Auditing',
112+
'Use Case: Network Security Monitoring',
113+
'Use Case: Threat Detection',
114+
'Use Case: Vulnerability'
115+
]
116+
57117

58118
NonEmptyStr = NewType('NonEmptyStr', str, validate=validate.Length(min=1))
59119
TimeUnits = Literal['s', 'm', 'h']

rules/cross-platform/guided_onboarding_sample_rule.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ references = ["https://www.elastic.co/guide/en/security/current/prebuilt-rules.h
4444
risk_score = 21
4545
rule_id = "a198fbbd-9413-45ec-a269-47ae4ccf59ce"
4646
severity = "low"
47-
tags = ["Use case: Guided Onboarding", "Data Source: APM", "OS: Windows", "Data Source: Elastic Endgame"]
47+
tags = ["Use Case: Guided Onboarding", "Data Source: APM", "OS: Windows", "Data Source: Elastic Endgame"]
4848
timestamp_override = "event.ingested"
4949
type = "threshold"
5050

tests/test_all_rules.py

Lines changed: 13 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -242,67 +242,7 @@ class TestRuleTags(BaseRuleTest):
242242
def test_casing_and_spacing(self):
243243
"""Ensure consistent and expected casing for controlled tags."""
244244

245-
expected_tags = [
246-
'Data Source: Active Directory',
247-
'Data Source: Amazon Web Services',
248-
'Data Source: AWS',
249-
'Data Source: APM',
250-
'Data Source: Azure',
251-
'Data Source: CyberArk PAS',
252-
'Data Source: Elastic Defend',
253-
'Data Source: Elastic Defend for Containers',
254-
'Data Source: Elastic Endgame',
255-
'Data Source: GCP',
256-
'Data Source: Google Cloud Platform',
257-
'Data Source: Google Workspace',
258-
'Data Source: Kubernetes',
259-
'Data Source: Microsoft 365',
260-
'Data Source: Okta',
261-
'Data Source: PowerShell Logs',
262-
'Data Source: Sysmon Only',
263-
'Data Source: Zoom',
264-
'Domain: Cloud',
265-
'Domain: Container',
266-
'Domain: Endpoint',
267-
'OS: Linux',
268-
'OS: macOS',
269-
'OS: Windows',
270-
'Resources: Investigation Guide',
271-
'Rule Type: Higher-Order Rule',
272-
'Rule Type: Machine Learning',
273-
'Rule Type: ML',
274-
'Tactic: Collection',
275-
'Tactic: Command and Control',
276-
'Tactic: Credential Access',
277-
'Tactic: Defense Evasion',
278-
'Tactic: Discovery',
279-
'Tactic: Execution',
280-
'Tactic: Exfiltration',
281-
'Tactic: Impact',
282-
'Tactic: Initial Access',
283-
'Tactic: Lateral Movement',
284-
'Tactic: Persistence',
285-
'Tactic: Privilege Escalation',
286-
'Tactic: Reconnaissance',
287-
'Tactic: Resource Development',
288-
'Threat: BPFDoor',
289-
'Threat: Cobalt Strike',
290-
'Threat: Lightning Framework',
291-
'Threat: Orbit',
292-
'Threat: Rootkit',
293-
'Threat: TripleCross',
294-
'Use Case: Active Directory Monitoring',
295-
'Use Case: Asset Visibility',
296-
'Use Case: Configuration Audit',
297-
'Use case: Guided Onboarding',
298-
'Use Case: Identity and Access Audit',
299-
'Use Case: Log Auditing',
300-
'Use Case: Network Security Monitoring',
301-
'Use Case: Threat Detection',
302-
'Use Case: Vulnerability',
303-
]
304-
305-
expected_case = {t.casefold(): t for t in expected_tags}
245+
expected_case = {t.casefold(): t for t in definitions.EXPECTED_RULE_TAGS}
306246

307247
for rule in self.all_rules:
308248
rule_tags = rule.contents.data.tags
@@ -468,6 +408,18 @@ def test_investigation_guide_tag(self):
468408
err_msg = '\n'.join(invalid)
469409
self.fail(f'Rules with missing Investigation tag:\n{err_msg}')
470410

411+
def test_tag_prefix(self):
412+
"""Ensure all tags have a prefix from an expected list."""
413+
invalid = []
414+
415+
for rule in self.all_rules:
416+
rule_tags = rule.contents.data.tags
417+
expected_prefixes = set([tag.split(":")[0] + ":" for tag in definitions.EXPECTED_RULE_TAGS])
418+
[invalid.append(f"{self.rule_str(rule)}-{tag}") for tag in rule_tags
419+
if not any(prefix in tag for prefix in expected_prefixes)]
420+
if invalid:
421+
self.fail(f'Rules with invalid tags:\n{invalid}')
422+
471423

472424
class TestRuleTimelines(BaseRuleTest):
473425
"""Test timelines in rules are valid."""

0 commit comments

Comments
 (0)