first add
This commit is contained in:
119
src/fgai/logs.py
Normal file
119
src/fgai/logs.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shlex
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
from .models import LogEvent
|
||||
|
||||
UTM_SUBTYPES = {
|
||||
"ips",
|
||||
"virus",
|
||||
"av",
|
||||
"anomaly",
|
||||
"ddos",
|
||||
"dos",
|
||||
"webfilter",
|
||||
"app-ctrl",
|
||||
"application",
|
||||
"waf",
|
||||
"dns",
|
||||
}
|
||||
|
||||
THREAT_ACTIONS = {
|
||||
"blocked",
|
||||
"block",
|
||||
"dropped",
|
||||
"drop",
|
||||
"reset",
|
||||
"reset-client",
|
||||
"reset-server",
|
||||
"detected",
|
||||
"deny",
|
||||
"quarantine",
|
||||
}
|
||||
|
||||
SEVERITY_SCORE = {
|
||||
"emergency": 6,
|
||||
"alert": 6,
|
||||
"critical": 5,
|
||||
"high": 4,
|
||||
"warning": 3,
|
||||
"medium": 3,
|
||||
"notice": 2,
|
||||
"low": 1,
|
||||
"information": 1,
|
||||
"info": 1,
|
||||
}
|
||||
|
||||
|
||||
def parse_log_line(line: str) -> LogEvent:
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
return LogEvent(raw=line, fields={})
|
||||
|
||||
if stripped.startswith("{"):
|
||||
try:
|
||||
data = json.loads(stripped)
|
||||
except json.JSONDecodeError:
|
||||
data = {}
|
||||
else:
|
||||
return LogEvent(raw=line, fields={str(k).lower(): str(v) for k, v in data.items()})
|
||||
|
||||
fields: dict[str, str] = {}
|
||||
try:
|
||||
parts = shlex.split(stripped)
|
||||
except ValueError:
|
||||
parts = stripped.split()
|
||||
|
||||
for part in parts:
|
||||
if "=" not in part:
|
||||
continue
|
||||
key, value = part.split("=", 1)
|
||||
fields[key.strip().lower()] = value.strip().strip('"')
|
||||
|
||||
return LogEvent(raw=line, fields=fields)
|
||||
|
||||
|
||||
def read_events(path: str | Path) -> list[LogEvent]:
|
||||
with Path(path).open("r", encoding="utf-8", errors="replace") as handle:
|
||||
return [event for line in handle if (event := parse_log_line(line)).fields]
|
||||
|
||||
|
||||
def is_utm_event(event: LogEvent) -> bool:
|
||||
subtype = event.subtype
|
||||
event_type = event.fields.get("type", "").lower()
|
||||
return event_type == "utm" or subtype in UTM_SUBTYPES
|
||||
|
||||
|
||||
def event_score(event: LogEvent) -> int:
|
||||
score = SEVERITY_SCORE.get(event.severity, 0)
|
||||
if is_utm_event(event):
|
||||
score += 2
|
||||
if event.action in THREAT_ACTIONS:
|
||||
score += 2
|
||||
if event.fields.get("attack") or event.fields.get("attackid") or event.fields.get("signature"):
|
||||
score += 1
|
||||
return score
|
||||
|
||||
|
||||
def summarize_events(events: Iterable[LogEvent]) -> dict[str, int]:
|
||||
summary = {
|
||||
"total": 0,
|
||||
"utm": 0,
|
||||
"threat_actions": 0,
|
||||
"with_src_ip": 0,
|
||||
"critical_or_high": 0,
|
||||
}
|
||||
for event in events:
|
||||
summary["total"] += 1
|
||||
if is_utm_event(event):
|
||||
summary["utm"] += 1
|
||||
if event.action in THREAT_ACTIONS:
|
||||
summary["threat_actions"] += 1
|
||||
if event.src_ip:
|
||||
summary["with_src_ip"] += 1
|
||||
if event.severity in {"critical", "high", "alert", "emergency"}:
|
||||
summary["critical_or_high"] += 1
|
||||
return summary
|
||||
Reference in New Issue
Block a user