140 lines
3.4 KiB
Python
140 lines
3.4 KiB
Python
"""
|
|
©AngelaMos | 2026
|
|
severity.py
|
|
"""
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
from app.models.LogEvent import Severity
|
|
|
|
|
|
CRITICAL_PATTERNS = [
|
|
re.compile(r"privilege.?escalat",
|
|
re.IGNORECASE),
|
|
re.compile(r"root.?compromise",
|
|
re.IGNORECASE),
|
|
re.compile(r"data.?exfiltrat",
|
|
re.IGNORECASE),
|
|
re.compile(r"ransomware",
|
|
re.IGNORECASE),
|
|
re.compile(r"command.?and.?control",
|
|
re.IGNORECASE),
|
|
re.compile(r"c2.?beacon",
|
|
re.IGNORECASE),
|
|
]
|
|
|
|
HIGH_PATTERNS = [
|
|
re.compile(r"brute.?force",
|
|
re.IGNORECASE),
|
|
re.compile(r"lateral.?movement",
|
|
re.IGNORECASE),
|
|
re.compile(r"reverse.?shell",
|
|
re.IGNORECASE),
|
|
re.compile(r"malware",
|
|
re.IGNORECASE),
|
|
re.compile(r"exploit",
|
|
re.IGNORECASE),
|
|
re.compile(r"unauthorized.?access",
|
|
re.IGNORECASE),
|
|
]
|
|
|
|
MEDIUM_PATTERNS = [
|
|
re.compile(r"login.?fail",
|
|
re.IGNORECASE),
|
|
re.compile(r"authentication.?fail",
|
|
re.IGNORECASE),
|
|
re.compile(r"suspicious",
|
|
re.IGNORECASE),
|
|
re.compile(r"port.?scan",
|
|
re.IGNORECASE),
|
|
re.compile(r"denied",
|
|
re.IGNORECASE),
|
|
re.compile(r"blocked",
|
|
re.IGNORECASE),
|
|
]
|
|
|
|
LOW_PATTERNS = [
|
|
re.compile(r"warning",
|
|
re.IGNORECASE),
|
|
re.compile(r"policy.?violation",
|
|
re.IGNORECASE),
|
|
re.compile(r"anomal",
|
|
re.IGNORECASE),
|
|
]
|
|
|
|
SEVERITY_TIERS = [
|
|
(Severity.CRITICAL,
|
|
CRITICAL_PATTERNS),
|
|
(Severity.HIGH,
|
|
HIGH_PATTERNS),
|
|
(Severity.MEDIUM,
|
|
MEDIUM_PATTERNS),
|
|
(Severity.LOW,
|
|
LOW_PATTERNS),
|
|
]
|
|
|
|
HIGH_SEVERITY_EVENT_TYPES = frozenset(
|
|
{
|
|
"privilege_escalation",
|
|
"data_exfiltration",
|
|
"c2_communication",
|
|
"reverse_shell",
|
|
}
|
|
)
|
|
|
|
MEDIUM_SEVERITY_EVENT_TYPES = frozenset(
|
|
{
|
|
"login_failure",
|
|
"port_scan",
|
|
"firewall_deny",
|
|
"ids_alert",
|
|
}
|
|
)
|
|
|
|
|
|
def classify(normalized: dict[str, Any]) -> str:
|
|
"""
|
|
Determine severity from event type and content pattern matching
|
|
"""
|
|
event_type = normalized.get("event_type", "")
|
|
|
|
if event_type in HIGH_SEVERITY_EVENT_TYPES:
|
|
return Severity.HIGH
|
|
if event_type in MEDIUM_SEVERITY_EVENT_TYPES:
|
|
return Severity.MEDIUM
|
|
|
|
searchable = _build_searchable_text(normalized)
|
|
|
|
for severity, patterns in SEVERITY_TIERS:
|
|
for pattern in patterns:
|
|
if pattern.search(searchable):
|
|
return severity
|
|
|
|
return Severity.INFO
|
|
|
|
|
|
def _build_searchable_text(normalized: dict[str, Any]) -> str:
|
|
"""
|
|
Concatenate relevant fields into a single string for pattern matching
|
|
"""
|
|
parts = [
|
|
str(normalized.get("event_type",
|
|
"")),
|
|
str(normalized.get("message",
|
|
"")),
|
|
str(normalized.get("normalized",
|
|
{}).get("message",
|
|
"")),
|
|
str(normalized.get("normalized",
|
|
{}).get("signature_name",
|
|
"")),
|
|
str(normalized.get("normalized",
|
|
{}).get("classification",
|
|
"")),
|
|
str(normalized.get("normalized",
|
|
{}).get("command_line",
|
|
"")),
|
|
]
|
|
return " ".join(parts)
|