42 lines
2.3 KiB
Python
42 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import Counter, defaultdict
|
|
|
|
from .logs import THREAT_ACTIONS, is_utm_event
|
|
from .models import LogEvent
|
|
|
|
|
|
def _entity(event: LogEvent) -> str:
|
|
return event.src_ip or event.fields.get("user") or event.fields.get("username") or event.fields.get("hostname") or event.fields.get("source") or "unknown"
|
|
|
|
|
|
def build_event_context(events: list[LogEvent], *, source_limit: int = 30, sample_limit: int = 15) -> dict[str, object]:
|
|
grouped: dict[str, list[LogEvent]] = defaultdict(list)
|
|
field_presence: Counter[str] = Counter()
|
|
for event in events:
|
|
grouped[_entity(event)].append(event)
|
|
field_presence.update(event.fields.keys())
|
|
|
|
source_profiles = []
|
|
for entity, source_events in grouped.items():
|
|
actions = Counter(event.action or "unknown" for event in source_events)
|
|
destinations = {event.dst_ip for event in source_events if event.dst_ip}
|
|
source_profiles.append({
|
|
"entity": entity, "events": len(source_events), "utm_events": sum(is_utm_event(event) for event in source_events),
|
|
"deny_or_threat_actions": sum(event.action in THREAT_ACTIONS for event in source_events),
|
|
"distinct_destinations": len(destinations), "top_actions": [action for action, _ in actions.most_common(3)],
|
|
})
|
|
source_profiles.sort(key=lambda item: (int(item["utm_events"]) + int(item["deny_or_threat_actions"]), int(item["events"])), reverse=True)
|
|
|
|
suspicious = [event for event in events if is_utm_event(event) or event.action in THREAT_ACTIONS or event.severity in {"critical", "high", "alert", "emergency"}]
|
|
samples = [{
|
|
"entity": _entity(event), "type": event.fields.get("type", ""), "subtype": event.subtype,
|
|
"action": event.action, "severity": event.severity, "dst": event.dst_ip or "", "service": event.fields.get("service", ""),
|
|
"policyid": event.fields.get("policyid", ""), "timestamp": event.fields.get("eventtime", event.fields.get("timestamp", "")),
|
|
} for event in suspicious[:sample_limit]]
|
|
return {
|
|
"entities_total": len(grouped), "source_profiles": source_profiles[:source_limit],
|
|
"field_coverage": [{"field": field, "events": count} for field, count in field_presence.most_common(30)],
|
|
"security_event_samples": samples,
|
|
}
|