0
Raw Zeek conn.log
Tab-separated log with #-prefixed header block defining field names, separator, and log type
ts   uid   id.orig_h   id.orig_p   id.resp_h   id.resp_p   proto   service   duration   orig_bytes   resp_bytes   orig_pkts   resp_pkts   conn_state
raw TSV rows
1
Parse log structure ZeekLogParser
Read # header directives for separator, field names, and log type Split each data row by separator, zip with field names into a key-value record
dict per row
2
Normalize and enrich AssetEnricher
Resolve field name variants — id.orig_h, src_ip, orig_h all map to a single src_ip Type-cast strings to int ports, float duration, int byte and packet counts Look up each IP in the asset registry for Purdue level, zone, and architecture role
enriched dict
3
Detect and score DetectionEngine
Rule matching (R001–R018) → generates SIEMAlert per match Dedup: skip if same (rule_id, src, dst) seen within 300s window
Severity scoring — 3-factor additive model
score_asset = ASSET_CRITICALITY[dst_purdue]   0–3
L0/L1/L2 = 3    L3/L3.5 = 2    L4 = 1    External = 0

score_conf  = CONFIDENCE[confidence_key]   1–3
KNOWN_ATTACKER_IP = 3    PROTOCOL_ANOMALY = 2    HEURISTIC = 1

score_zone  = ZONE_CONTEXT[zone_key]   0–2
OT_FIELD_TARGETED = 2    FIREWALL_CROSSED = 2    PURDUE_VIOLATED = 1    SAME_ZONE = 0

score_total = asset + conf + zone    (range 0–8)
CRITICAL ≥ 8 HIGH ≥ 6 MEDIUM ≥ 4 LOW ≥ 2 INFO < 2
SIEMAlert
4
Kill chain correlation KillChainTracker
Group alerts by attacker IP within time-windowed chain profiles ransomware R010 → R014 → R016 → R017 → R018    600s window, ≥ 2 stages ddos R010 → R001 → R008 → R009    300s window, ≥ 3 stages to qualify
Incident
OUT
Output
SIEMAlert Normalized fields, rule ID, MITRE ICS technique, severity score, description Incident Chain type, attacker IP, stages reached, victim IPs, narrative, duration → NDJSON alert stream   │   NDJSON incident stream   │   Summary report