mirror of
https://github.com/marcredhat/SIEM-toolkit-patched
synced 2026-06-10 21:31:19 +00:00
Health Score: cap MITRE Coverage at 100% by canonicalising tactics
STAR rules sometimes label tactics with non-canonical names (e.g. 'Stealth',
'Defense Impairment') which were counted as distinct tactics on top of the
14 canonical ATT&CK Enterprise ones, producing percentages > 100%
(observed 15/14 = 107.1% on Purple AI tenant).
Fix in get_health_score():
- Restrict covered_tactics to the 14 canonical ATT&CK Enterprise tactics.
- Map known STAR aliases ('Stealth', 'Defense Impairment') -> 'Defense Evasion'.
- Derive TOTAL_TACTICS from the canonical set (single source of truth).
Result: tactics_covered = 14, mitre_pct = 100.0 (was 15 / 107.1).
This commit is contained in:
@@ -1098,7 +1098,21 @@ def _compute_health(db) -> dict:
|
|||||||
parser_pct = round((covered_sources / total_sources * 100) if total_sources else 0.0, 1)
|
parser_pct = round((covered_sources / total_sources * 100) if total_sources else 0.0, 1)
|
||||||
|
|
||||||
# --- MITRE coverage ---
|
# --- MITRE coverage ---
|
||||||
TOTAL_TACTICS = 14 # standard ATT&CK Enterprise tactic count
|
# Standard ATT&CK Enterprise tactics (14).
|
||||||
|
CANONICAL_TACTICS = frozenset({
|
||||||
|
"Reconnaissance", "Resource Development", "Initial Access", "Execution",
|
||||||
|
"Persistence", "Privilege Escalation", "Defense Evasion", "Credential Access",
|
||||||
|
"Discovery", "Lateral Movement", "Collection", "Command and Control",
|
||||||
|
"Exfiltration", "Impact",
|
||||||
|
})
|
||||||
|
# SentinelOne STAR rules sometimes label tactics with non-canonical names.
|
||||||
|
# Map them to canonical ATT&CK so we don't over-count and exceed 100%.
|
||||||
|
TACTIC_ALIASES = {
|
||||||
|
"Stealth": "Defense Evasion",
|
||||||
|
"Defense Impairment": "Defense Evasion",
|
||||||
|
}
|
||||||
|
TOTAL_TACTICS = len(CANONICAL_TACTICS)
|
||||||
|
|
||||||
rules = db.query(ParsedRule).filter_by(rule_type="library").all()
|
rules = db.query(ParsedRule).filter_by(rule_type="library").all()
|
||||||
total_rules = len(rules)
|
total_rules = len(rules)
|
||||||
covered_tactics: set = set()
|
covered_tactics: set = set()
|
||||||
@@ -1114,7 +1128,10 @@ def _compute_health(db) -> dict:
|
|||||||
if tactics or techniques:
|
if tactics or techniques:
|
||||||
rules_with_mitre += 1
|
rules_with_mitre += 1
|
||||||
for t in tactics:
|
for t in tactics:
|
||||||
if t and t != "Uncategorized":
|
if not t or t == "Uncategorized":
|
||||||
|
continue
|
||||||
|
t = TACTIC_ALIASES.get(t, t)
|
||||||
|
if t in CANONICAL_TACTICS:
|
||||||
covered_tactics.add(t)
|
covered_tactics.add(t)
|
||||||
for tech in techniques:
|
for tech in techniques:
|
||||||
k = tech.get("id") or tech.get("name")
|
k = tech.get("id") or tech.get("name")
|
||||||
|
|||||||
Reference in New Issue
Block a user