Initial curated EventLens source from 0d80d3b38dfb817285481665b9e71e4b8a0656a0
This commit is contained in:
commit
663ea8c829
85 files changed
+12395
No files matched your search
@@ -0,0 +1,83 @@
|
||||
from datetime import UTC, datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
|
||||
Category = Literal["system", "service", "network", "authentication", "application", "crash", "other"]
|
||||
ParseStatus = Literal["complete", "partial", "failed"]
|
||||
TimestampSource = Literal["event", "record", "missing", "invalid"]
|
||||
|
||||
|
||||
class NormalizedEvent(BaseModel):
|
||||
id: str = Field(max_length=128)
|
||||
timestamp: datetime | None = None
|
||||
timestamp_source: TimestampSource = "event"
|
||||
timestamp_valid: bool = True
|
||||
provider: str = Field(default="", max_length=256)
|
||||
event_id: int | None = Field(default=None, ge=0, le=4_294_967_295)
|
||||
level: str = Field(default="information", max_length=32)
|
||||
channel: str = Field(default="", max_length=256)
|
||||
computer: str = Field(default="", max_length=256)
|
||||
category: Category = "other"
|
||||
summary: str = Field(default="", max_length=512)
|
||||
fields: dict[str, str] = Field(default_factory=dict, max_length=200)
|
||||
|
||||
@field_validator("timestamp")
|
||||
@classmethod
|
||||
def normalize_naive_timestamp(cls, value: datetime | None) -> datetime | None:
|
||||
if value is not None and value.tzinfo is None:
|
||||
return value.replace(tzinfo=UTC)
|
||||
return value
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_timestamp_metadata(self) -> "NormalizedEvent":
|
||||
if self.timestamp is None:
|
||||
self.timestamp_valid = False
|
||||
if self.timestamp_source in {"event", "record"}:
|
||||
self.timestamp_source = "missing"
|
||||
elif self.timestamp_source in {"missing", "invalid"}:
|
||||
self.timestamp_valid = False
|
||||
else:
|
||||
self.timestamp_valid = True
|
||||
return self
|
||||
|
||||
|
||||
class IncidentCluster(BaseModel):
|
||||
id: str = Field(max_length=128)
|
||||
rule_id: str = Field(max_length=128)
|
||||
start: datetime | None = None
|
||||
end: datetime | None = None
|
||||
title: str = Field(max_length=256)
|
||||
category: Category
|
||||
severity: str = Field(max_length=32)
|
||||
reasons: list[str] = Field(max_length=8)
|
||||
evidence_event_ids: list[str]
|
||||
original_count: int = Field(ge=1)
|
||||
compressed_count: int = Field(ge=1)
|
||||
|
||||
|
||||
class ParseIssue(BaseModel):
|
||||
code: str = Field(max_length=64)
|
||||
record_id: int | None = None
|
||||
message: str = Field(max_length=256)
|
||||
skipped_record: bool = False
|
||||
|
||||
|
||||
class ParseStats(BaseModel):
|
||||
parser: str = "evtx-rs"
|
||||
status: ParseStatus
|
||||
parsed_events: int = Field(ge=0)
|
||||
records_seen: int = Field(ge=0)
|
||||
skipped_records: int = Field(ge=0)
|
||||
issue_count: int = Field(ge=0)
|
||||
truncated: bool
|
||||
truncation_reason: str | None = Field(default=None, max_length=64)
|
||||
duration_ms: int = Field(ge=0)
|
||||
issues: list[ParseIssue] = Field(default_factory=list)
|
||||
|
||||
|
||||
class AnalysisResponse(BaseModel):
|
||||
source: str
|
||||
events: list[NormalizedEvent]
|
||||
incidents: list[IncidentCluster]
|
||||
parse: ParseStats | None = None
|
||||
Reference in new issue
Block a user