Implemented the next multi-source detection layer in this repository.
This commit is contained in:
59
src/fgai/entities.py
Normal file
59
src/fgai/entities.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .models import LogEvent
|
||||
|
||||
|
||||
ENTITY_FIELDS: dict[str, tuple[str, ...]] = {
|
||||
"ip": ("srcip", "src_ip", "source_ip", "client_ip", "remote_addr", "remote_ip", "ip", "ipaddress", "winlog_event_data_ipaddress", "event_data_ipaddress"),
|
||||
"user": ("username", "user", "user_name", "account", "account_name", "targetusername", "subjectusername", "xauthuser", "winlog_event_data_targetusername", "winlog_event_data_subjectusername"),
|
||||
"host": ("hostname", "host", "computer", "computer_name", "workstation", "device_name", "winlog_computer_name", "agent_name"),
|
||||
}
|
||||
|
||||
|
||||
def entity_type(value: str) -> str:
|
||||
try:
|
||||
ipaddress.ip_address(value)
|
||||
return "ip"
|
||||
except ValueError:
|
||||
return "entity"
|
||||
|
||||
|
||||
def event_entities(event: LogEvent) -> list[dict[str, str]]:
|
||||
"""Return normalized identities shared across network, endpoint, DNS, and web logs."""
|
||||
identities: list[dict[str, str]] = []
|
||||
seen: set[tuple[str, str]] = set()
|
||||
for kind, fields in ENTITY_FIELDS.items():
|
||||
for field in fields:
|
||||
value = str(event.fields.get(field, "")).strip()
|
||||
if not value or value in {"-", "unknown", "n/a"}:
|
||||
continue
|
||||
key = (kind, value.lower() if kind != "ip" else value)
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
identities.append({"entity": value, "entity_type": kind, "field": field})
|
||||
return identities
|
||||
|
||||
|
||||
def profile_entity(event: LogEvent, field: str) -> str:
|
||||
return str(event.fields.get(field.lower(), "")).strip()
|
||||
|
||||
|
||||
def sample_timeline(events: Iterable[LogEvent], *, limit: int = 20) -> list[dict[str, str]]:
|
||||
samples = [
|
||||
{
|
||||
"stream": event.fields.get("fgai_stream", "local_syslog"),
|
||||
"timestamp": event.fields.get("eventtime", event.fields.get("timestamp", "")),
|
||||
"type": event.fields.get("type", ""),
|
||||
"subtype": event.subtype,
|
||||
"action": event.action,
|
||||
"severity": event.severity,
|
||||
"destination": event.dst_ip or event.fields.get("query_domain", event.fields.get("url", "")),
|
||||
"service": event.fields.get("service", event.fields.get("query_type", "")),
|
||||
"context": event.fields.get("query_domain", event.fields.get("qh", event.fields.get("url", event.fields.get("message", event.fields.get("msg", "")))))[:240],
|
||||
}
|
||||
for event in events
|
||||
]
|
||||
return sorted(samples, key=lambda item: item["timestamp"])[-limit:]
|
||||
Reference in New Issue
Block a user