From 5cd8eeb9f0bee0499bd691615bbe860fd7796dc4 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Wed, 16 Sep 2026 20:34:15 -0400 Subject: [PATCH 1/2] fix: align ESET filter and rule contracts --- filters/antivirus/esmc-eset.yml | 128 ++++++++++++++---- filters/audits/eset.md | 42 ++++++ .../testdata/filter-contracts/eset.json | 55 ++++++++ .../esmc-eset/exploit_detection_events.yml | 2 +- 4 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 filters/audits/eset.md create mode 100644 plugins/alerts/testdata/filter-contracts/eset.json diff --git a/filters/antivirus/esmc-eset.yml b/filters/antivirus/esmc-eset.yml index b270dd182..1205b5024 100644 --- a/filters/antivirus/esmc-eset.yml +++ b/filters/antivirus/esmc-eset.yml @@ -59,11 +59,6 @@ pipeline: from: - log.action to: action - - rename: - from: - - log.event - to: actionResult - where: '!regexMatch("log.result", "(?i)\b(?:denied|blocked|failed)\b")' - rename: from: - log.sourceaddress @@ -86,47 +81,29 @@ pipeline: to: target.port # Adding action result - - add: - function: 'string' - params: - key: actionResult - value: 'denied' - where: regexMatch("log.event", "(?i)\bdenied\b") - - add: - function: 'string' - params: - key: actionResult - value: 'blocked' - where: regexMatch("log.event", "(?i)\bblocked\b") - - add: - function: 'string' - params: - key: actionResult - value: 'failed' - where: regexMatch("log.event", "(?i)\bfailed\b") # Adding severity field based on log.severity - add: - function: 'string' + function: string params: key: severity - value: 'low' + value: info where: oneOf("log.severity", ["INFO", "Info"]) - add: - function: 'string' + function: string params: key: severity - value: 'medium' + value: warning where: oneOf("log.severity", ["WARNING", "Warning"]) - add: - function: 'string' + function: string params: key: severity - value: 'high' + value: error where: oneOf("log.severity", ["ERROR", "Error"]) # Adding geolocation to origin.ip @@ -149,4 +126,95 @@ pipeline: - delete: fields: - log.jsonMessage - - log.severity \ No newline at end of file + - log.severity + + # Normalize explicit outcomes; an unknown outcome remains unset. + - add: + function: string + params: + key: actionResult + value: success + where: regexMatch("log.result", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$") + - add: + function: string + params: + key: actionResult + value: failure + where: regexMatch("log.result", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("log.result", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") + - add: + function: string + params: + key: actionResult + value: denied + where: regexMatch("action", "(?i)^(deny|denied|block|blocked)$") || regexMatch("log.event", "(?i)^(deny|denied|block|blocked)$") + + # Keep addresses in IP fields and retain other source values under log. + - rename: + from: + - origin.ip + to: log.unparsedOriginIp + where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"])) + - rename: + from: + - target.ip + to: log.unparsedTargetIp + where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"])) + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) diff --git a/filters/audits/eset.md b/filters/audits/eset.md new file mode 100644 index 000000000..bb3f4e117 --- /dev/null +++ b/filters/audits/eset.md @@ -0,0 +1,42 @@ +# ESET normalization and rule review + +Separate event type from actionResult and classify actual vendor outcomes. + +This draft targets UTMStack `v11`. It contains 1 filter changes +and 1 rule changes for this technology only. Review covered +1 filter configurations and 13 matching shipped rule files. +Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. + +## Contract and validation + +- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries. +- Checked documented pipeline ordering, rename/move behavior, open vendor log fields, + event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping. +- Strict SDK configuration decoding and actual CEL compilation pass for this scope. +- 2 synthetic normalization cases pass, including SDK Event conversion and any + trigger predicate assertions recorded in the manifest. +- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied. + +The shared alert-contract PR supplies the reusable Go runner for the manifest in +`plugins/alerts/testdata/filter-contracts/eset.json`. Apply that support before running `go test ./...` in `plugins/alerts`. + +The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime. + +The model starts from synthetic extraction results. It does not run complex grok, +JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch +queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must +still be checked in staging before rollout. No customer false-positive reduction +has been measured and no production rollout is included. + + + +## References + +- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) + +`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom +`log.*` fields are accepted. Existing textual protocol casing and vendor action names +are preserved unless a concrete consumer mismatch requires correction. diff --git a/plugins/alerts/testdata/filter-contracts/eset.json b/plugins/alerts/testdata/filter-contracts/eset.json new file mode 100644 index 000000000..d8042d96f --- /dev/null +++ b/plugins/alerts/testdata/filter-contracts/eset.json @@ -0,0 +1,55 @@ +{ + "technology": "ESET", + "filters": [ + "filters/antivirus/esmc-eset.yml" + ], + "rules": [ + "rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml", + "rules/antivirus/esmc-eset/botnet_communication_attempts.yml", + "rules/antivirus/esmc-eset/eset_agent_tampering.yml", + "rules/antivirus/esmc-eset/eset_console_abuse.yml", + "rules/antivirus/esmc-eset/eset_quarantine_failures.yml", + "rules/antivirus/esmc-eset/exploit_detection_events.yml", + "rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml", + "rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml", + "rules/antivirus/esmc-eset/network_attack_detection.yml", + "rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml", + "rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml", + "rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml", + "rules/antivirus/esmc-eset/suspicious_process_behavior.yml" + ], + "fixtures": [ + { + "name": "ESET event name is not an outcome", + "filter": "antivirus/esmc-eset.yml", + "input": { + "log": { + "event": "HIPS_Event" + } + }, + "expected": { + "log.event": "HIPS_Event" + }, + "absent": [ + "actionResult" + ], + "rules": {} + }, + { + "name": "ESET actual blocked result", + "filter": "antivirus/esmc-eset.yml", + "input": { + "log": { + "event": "HIPS_Event", + "result": "Blocked" + } + }, + "expected": { + "actionResult": "denied", + "log.event": "HIPS_Event" + }, + "absent": [], + "rules": {} + } + ] +} diff --git a/rules/antivirus/esmc-eset/exploit_detection_events.yml b/rules/antivirus/esmc-eset/exploit_detection_events.yml index 035889c31..f2e1f9969 100644 --- a/rules/antivirus/esmc-eset/exploit_detection_events.yml +++ b/rules/antivirus/esmc-eset/exploit_detection_events.yml @@ -26,7 +26,7 @@ description: | where: | (contains("log.jsonMessage", "exploit") || oneOf("log.msgType", ["Exploit_Blocked", "Exploit"])) && - equals("actionResult", "blocked") && + oneOf("actionResult", ["denied", "blocked"]) && oneOf("log.severity", ["medium", "high"]) groupBy: - lastEvent.log.jsonMessage From 5ef2bb4d5bcd5527f0c3dd90bb7fa0e4650feb75 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 15:57:15 -0400 Subject: [PATCH 2/2] fix(eset): align JSON parsing and endpoint detection contracts --- filters/antivirus/esmc-eset.yml | 720 +++++-- filters/audits/eset.md | 148 +- plugins/alerts/eset_consumers_test.go | 195 ++ plugins/alerts/eset_contract_test.go | 435 ++++ plugins/alerts/eset_history_test.go | 308 +++ plugins/alerts/testdata/eset_raw.json | 1852 +++++++++++++++++ .../testdata/filter-contracts/eset.json | 71 +- .../advanced_heuristic_detection_triggers.yml | 36 +- .../botnet_communication_attempts.yml | 36 +- .../botnet_inbound_communication_attempts.yml | 41 + .../esmc-eset/eset_agent_tampering.yml | 28 +- .../esmc-eset/eset_console_abuse.yml | 60 +- .../esmc-eset/eset_quarantine_failures.yml | 33 +- .../esmc-eset/exploit_detection_events.yml | 23 +- .../host_intrusion_prevention_triggers.yml | 21 +- .../machine_learning_detection_anomalies.yml | 23 +- .../esmc-eset/network_attack_detection.yml | 23 +- ...registry_modification_attempts_blocked.yml | 21 +- .../suspicious_encrypted_file_detection.yml | 21 +- ...suspicious_powershell_activity_blocked.yml | 21 +- .../esmc-eset/suspicious_process_behavior.yml | 27 +- 21 files changed, 3825 insertions(+), 318 deletions(-) create mode 100644 plugins/alerts/eset_consumers_test.go create mode 100644 plugins/alerts/eset_contract_test.go create mode 100644 plugins/alerts/eset_history_test.go create mode 100644 plugins/alerts/testdata/eset_raw.json create mode 100644 rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml diff --git a/filters/antivirus/esmc-eset.yml b/filters/antivirus/esmc-eset.yml index 1205b5024..fa1b31ef9 100644 --- a/filters/antivirus/esmc-eset.yml +++ b/filters/antivirus/esmc-eset.yml @@ -1,220 +1,666 @@ -# ESET filter version 3.0.3 -# Based in docs -# -# Documentations -# 1- https://help.eset.com/esmc_admin/70/en-US/events-exported-to-json-format.html#custom_keys -# -# Implementation -# 1. Parsing headers of syslog the message -# 2. Parsing RAW field log +# ESET filter version 3.1.0 +# JSON export contract: https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html +# Original vendor keys are sanitized by JSON extraction (event_type -> log.eventtype). +# Header host identifies the management server, never the managed endpoint. +# Only explicit decisions set actionResult; remediation and event classes do not. pipeline: - dataTypes: - antivirus-esmc-eset steps: - # Parsing RAW field log - grok: + source: raw patterns: + - pattern: ^\s* + - fieldName: log.jsonMessage + pattern: \{(?s:.*)\} + - pattern: \s*$ + - grok: + source: raw + patterns: + - pattern: ^(?:<[0-9]{1,3}>)? + - fieldName: log.deviceTime + pattern: '[A-Z][a-z]{2}\s+[0-9]{1,2}\s+[0-9]{1,2}:[0-9]{2}:[0-9]{2}' + - pattern: \s+ + - fieldName: log.headHostname + pattern: '[^\s]+' + - pattern: \s+ + - fieldName: log.serviceName + pattern: ERAServer + - pattern: \[ + - fieldName: log.pid + pattern: '[0-9]+' + - pattern: \]:\s* + - fieldName: log.jsonMessage + pattern: \{(?s:.*)\} + - pattern: \s*$ + where: '!exists("log.jsonMessage")' + - grok: + source: raw + patterns: + - pattern: ^< - fieldName: log.priority - pattern: '\<{{.data}}\>' - - fieldName: log.msgType - pattern: '{{.integer}}' + pattern: '[0-9]{1,3}' + - pattern: '>' + - fieldName: log.syslogVersion + pattern: '[1-9][0-9]*' + - pattern: \s+ - fieldName: log.deviceTime - pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' + pattern: \S+ + - pattern: \s+ - fieldName: log.headHostname - pattern: '{{.hostname}}' + pattern: \S+ + - pattern: \s+ - fieldName: log.serviceName - pattern: '{{.hostname}}' + pattern: ERAServer + - pattern: \s+ - fieldName: log.pid - pattern: '{{.integer}}' - - fieldName: log.restHead - pattern: '\-{{.data}}\-' + pattern: (?:[0-9]+|-) + - pattern: \s+\S+\s+(?:-|\[[^\r\n]*\])\s+ - fieldName: log.jsonMessage - pattern: '{{.greedy}}' - - # Remove inecesarie caracters - - trim: - function: prefix - substring: '<' - fields: - - log.priority - - trim: - function: suffix - substring: '>' - fields: - - log.priority - - # Parsing jsonMessage field log + pattern: \{(?s:.*)\} + - pattern: \s*$ + where: '!exists("log.jsonMessage")' - json: source: log.jsonMessage - + where: exists("log.jsonMessage") + - delete: + fields: + - log.jsonMessage + - log.eventType + - log.endpointKey + - log.endpointKeyType + - log.correlationCandidate + - log.esetOccurredTime + - grok: + source: log.eventtype + patterns: + - fieldName: log.eventType + pattern: (?s)^.+$ + where: regexMatch("log.eventtype", "(?s)^.+$") + - grok: + source: log.sourceuuid + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("log.endpointKey") + && (regexMatch("log.sourceuuid", "^[^\\s].*$") && !oneOf("log.sourceuuid", ["-","unknown"]))) && regexMatch("log.sourceuuid", + "(?s)^.+$") + - add: + function: string + params: + key: log.endpointKeyType + value: uuid + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: log.hostname + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("log.endpointKey") + && (regexMatch("log.hostname", "^[^\\s].*$") && !oneOf("log.hostname", ["-","unknown"]))) && regexMatch("log.hostname", + "(?s)^.+$") + - add: + function: string + params: + key: log.endpointKeyType + value: host + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: log.ipv4 + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("log.endpointKey") + && ((inCIDR("log.ipv4","0.0.0.0/0") || inCIDR("log.ipv4","::/0")) && !inCIDR("log.ipv4","0.0.0.0/32") && + !inCIDR("log.ipv4","::/128"))) && regexMatch("log.ipv4", "(?s)^.+$") + - add: + function: string + params: + key: log.endpointKeyType + value: ip + where: exists("log.endpointKey") && !exists("log.endpointKeyType") + - grok: + source: log.ipv6 + patterns: + - fieldName: log.endpointKey + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("log.endpointKey") + && ((inCIDR("log.ipv6","0.0.0.0/0") || inCIDR("log.ipv6","::/0")) && !inCIDR("log.ipv6","0.0.0.0/32") && + !inCIDR("log.ipv6","::/128"))) && regexMatch("log.ipv6", "(?s)^.+$") + - add: + function: string + params: + key: log.endpointKeyType + value: ip + where: exists("log.endpointKey") && !exists("log.endpointKeyType") - rename: from: - - log.ipv4 - to: target.ip + - log.sourceaddress + to: origin.ip + where: (equals("log.eventType","FirewallAggregated_Event") || equals("log.eventType","FilteredWebsites_Event")) + && (inCIDR("log.sourceaddress","0.0.0.0/0") || inCIDR("log.sourceaddress","::/0")) && !inCIDR("log.sourceaddress","0.0.0.0/32") + && !inCIDR("log.sourceaddress","::/128") - rename: from: - - log.hostname - to: target.host + - log.targetaddress + to: target.ip + where: (equals("log.eventType","FirewallAggregated_Event") || equals("log.eventType","FilteredWebsites_Event")) + && (inCIDR("log.targetaddress","0.0.0.0/0") || inCIDR("log.targetaddress","::/0")) && !inCIDR("log.targetaddress","0.0.0.0/32") + && !inCIDR("log.targetaddress","::/128") - rename: from: - - log.action - to: action + - log.ipv4 + to: origin.ip + where: (equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && !exists("origin.ip") && (inCIDR("log.ipv4","0.0.0.0/0") || inCIDR("log.ipv4","::/0")) + && !inCIDR("log.ipv4","0.0.0.0/32") && !inCIDR("log.ipv4","::/128") - rename: from: - - log.sourceaddress + - log.ipv6 to: origin.ip + where: (equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && !exists("origin.ip") && (inCIDR("log.ipv6","0.0.0.0/0") || inCIDR("log.ipv6","::/0")) + && !inCIDR("log.ipv6","0.0.0.0/32") && !inCIDR("log.ipv6","::/128") + - grok: + source: log.hostname + patterns: + - fieldName: origin.host + pattern: (?s)^.+$ + where: (equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && regexMatch("log.hostname", "(?s)^.+$") + - grok: + source: log.osname + patterns: + - fieldName: origin.operatingSystem + pattern: (?s)^.+$ + where: (equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && regexMatch("log.osname", "(?s)^.+$") - rename: from: - - log.protocol - to: protocol + - log.ipv4 + to: target.ip + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true)) && !exists("target.ip") + && (inCIDR("log.ipv4","0.0.0.0/0") || inCIDR("log.ipv4","::/0")) && !inCIDR("log.ipv4","0.0.0.0/32") && !inCIDR("log.ipv4","::/128") - rename: from: - - log.sourceport - to: origin.port + - log.ipv6 + to: target.ip + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true)) && !exists("target.ip") + && (inCIDR("log.ipv6","0.0.0.0/0") || inCIDR("log.ipv6","::/0")) && !inCIDR("log.ipv6","0.0.0.0/32") && !inCIDR("log.ipv6","::/128") + - grok: + source: log.hostname + patterns: + - fieldName: target.host + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true)) && regexMatch("log.hostname", + "(?s)^.+$") + - grok: + source: log.osname + patterns: + - fieldName: target.operatingSystem + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true)) && regexMatch("log.osname", + "(?s)^.+$") + - grok: + source: log.action + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"])) && regexMatch("log.action", + "(?s)^.+$") + - grok: + source: log.actiontaken + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","BlockedFiles_Event","FilteredWebsites_Event"])) + && regexMatch("log.actiontaken", "(?s)^.+$") + - grok: + source: log.username + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || equals("log.eventType","FilteredWebsites_Event")) && regexMatch("log.username", "(?s)^.+$") + - grok: + source: log.user + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: (equals("log.eventType","Audit_Event")) && regexMatch("log.user", "(?s)^.+$") + - grok: + source: log.account + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", true) == false) && regexMatch("log.account", + "(?s)^.+$") + - grok: + source: log.account + patterns: + - fieldName: target.user + pattern: (?s)^.+$ + where: (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true) && regexMatch("log.account", + "(?s)^.+$") + - grok: + source: log.target + patterns: + - fieldName: target.user + pattern: (?s)^.+$ + where: (equals("log.eventType","Audit_Event") && equalsIgnoreCase("log.domain","Native user") && equalsIgnoreCase("action","Login + attempt")) && regexMatch("log.target", "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^(?:.*[\\/])? + - fieldName: origin.process + pattern: '[^\\/]+$' + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && regexMatch("log.processname", "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^ + - fieldName: origin.path + pattern: '[A-Za-z]:[\\/](?:[^\\/\r\n]+[\\/])*' + - pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && regexMatch("log.processname", "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^ + - fieldName: origin.path + pattern: /(?:[^/\r\n]+/)* + - pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","Audit_Event","EnterpriseInspectorAlert_Event","BlockedFiles_Event"]) + || equals("log.eventType","FilteredWebsites_Event") || (equals("log.eventType","FirewallAggregated_Event") + && safe("log.inbound", true) == false)) && regexMatch("log.processname", "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^(?:.*[\\/])? + - fieldName: target.process + pattern: '[^\\/]+$' + where: (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true) && regexMatch("log.processname", + "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^ + - fieldName: target.path + pattern: '[A-Za-z]:[\\/](?:[^\\/\r\n]+[\\/])*' + - pattern: '[^\\/\r\n]+$' + where: (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true) && regexMatch("log.processname", + "(?s)^.+$") + - grok: + source: log.processname + patterns: + - pattern: ^ + - fieldName: target.path + pattern: /(?:[^/\r\n]+/)* + - pattern: '[^\\/\r\n]+$' + where: (equals("log.eventType","FirewallAggregated_Event") && safe("log.inbound", false) == true) && regexMatch("log.processname", + "(?s)^.+$") + - grok: + source: log.application + patterns: + - pattern: ^(?:.*[\\/])? + - fieldName: origin.process + pattern: '[^\\/]+$' + where: (oneOf("log.eventType", ["HipsAggregated_Event","HIPSAggregated_Event"])) && regexMatch("log.application", + "(?s)^.+$") + - grok: + source: log.application + patterns: + - pattern: ^ + - fieldName: origin.path + pattern: '[A-Za-z]:[\\/](?:[^\\/\r\n]+[\\/])*' + - pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["HipsAggregated_Event","HIPSAggregated_Event"])) && regexMatch("log.application", + "(?s)^.+$") + - grok: + source: log.application + patterns: + - pattern: ^ + - fieldName: origin.path + pattern: /(?:[^/\r\n]+/)* + - pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["HipsAggregated_Event","HIPSAggregated_Event"])) && regexMatch("log.application", + "(?s)^.+$") + - grok: + source: log.objecturi + patterns: + - fieldName: target.url + pattern: (?s)^.+$ + where: ((oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","BlockedFiles_Event","FilteredWebsites_Event"])) + && regexMatch("log.objecturi", "(?i)^https?://[^\\s]+$")) && regexMatch("log.objecturi", "(?s)^.+$") + - grok: + source: log.hash + patterns: + - fieldName: target.sha1 + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event","BlockedFiles_Event","FilteredWebsites_Event"]) && regexMatch("log.hash", + "^[0-9a-fA-F]{40}$")) && regexMatch("log.hash", "(?s)^.+$") + - grok: + source: log.occurred + patterns: + - fieldName: log.esetOccurredTime + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("deviceTime") + && !exists("log.esetOccurredTime") && regexMatch("log.occurred", "^(?:(?:0[1-9]|[12][0-9]|3[01])-(?:Jan|Mar|May|Jul|Aug|Oct|Dec)-[1-9][0-9]{3}|(?:0[1-9]|[12][0-9]|30)-(?:Apr|Jun|Sep|Nov)-[1-9][0-9]{3}|(?:0[1-9]|1[0-9]|2[0-8])-Feb-[1-9][0-9]{3}|29-Feb-(?:[1-9][0-9](?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)) + (?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$")) && regexMatch("log.occurred", "(?s)^.+$") + - grok: + source: log.occured + patterns: + - fieldName: log.esetOccurredTime + pattern: (?s)^.+$ + where: (oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && !exists("deviceTime") + && !exists("log.esetOccurredTime") && regexMatch("log.occured", "^(?:(?:0[1-9]|[12][0-9]|3[01])-(?:Jan|Mar|May|Jul|Aug|Oct|Dec)-[1-9][0-9]{3}|(?:0[1-9]|[12][0-9]|30)-(?:Apr|Jun|Sep|Nov)-[1-9][0-9]{3}|(?:0[1-9]|1[0-9]|2[0-8])-Feb-[1-9][0-9]{3}|29-Feb-(?:[1-9][0-9](?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)) + (?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$")) && regexMatch("log.occured", "(?s)^.+$") + - reformat: + fields: + - log.esetOccurredTime + function: time + fromFormat: 02-Jan-2006 15:04:05 + toFormat: '2006-01-02T15:04:05Z' + where: exists("log.esetOccurredTime") - rename: from: - - log.targetaddress - to: target.ip + - log.esetOccurredTime + to: deviceTime + where: exists("log.esetOccurredTime") + - grok: + source: log.target + patterns: + - pattern: ^ + - fieldName: target.path + pattern: '[A-Za-z]:[\\/](?:[^\\/\r\n]+[\\/])*' + - fieldName: target.filename + pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["HipsAggregated_Event","HIPSAggregated_Event"])) && regexMatch("log.target", + "(?s)^.+$") && !contains("log.target", "%") + - grok: + source: log.target + patterns: + - pattern: ^ + - fieldName: target.path + pattern: /(?:[^/\r\n]+/)* + - fieldName: target.filename + pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["HipsAggregated_Event","HIPSAggregated_Event"])) && regexMatch("log.target", + "(?s)^.+$") && !contains("log.target", "%") + - grok: + source: log.objecturi + patterns: + - pattern: ^file:/// + - fieldName: target.path + pattern: '[A-Za-z]:/(?:[^/\r\n]+/)*' + - fieldName: target.filename + pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["Threat_Event","BlockedFiles_Event"])) && regexMatch("log.objecturi", "(?s)^.+$") + && !contains("log.objecturi", "%") && !contains("log.objecturi", ["?","#"]) + - grok: + source: log.objecturi + patterns: + - pattern: ^file:// + - fieldName: target.path + pattern: /(?:[^/:\r\n]+/)* + - fieldName: target.filename + pattern: '[^\\/\r\n]+$' + where: (oneOf("log.eventType", ["Threat_Event","BlockedFiles_Event"])) && regexMatch("log.objecturi", "(?s)^.+$") + && !contains("log.objecturi", "%") && !contains("log.objecturi", ["?","#"]) + - grok: + source: log.threatname + patterns: + - fieldName: target.malware + pattern: (?s)^.+$ + where: (equals("log.eventType","Threat_Event")) && regexMatch("log.threatname", "(?s)^.+$") + - grok: + source: log.threattype + patterns: + - fieldName: target.malwareType + pattern: (?s)^.+$ + where: (equals("log.eventType","Threat_Event")) && regexMatch("log.threattype", "(?s)^.+$") + - grok: + source: log.threatname + patterns: + - fieldName: target.cve + pattern: ^CVE-[0-9]{4}-[0-9]{4,} + - pattern: (?:[.].*)?$ + where: equals("log.eventType","FirewallAggregated_Event") + - rename: + from: + - log.sourceport + to: origin.port + where: equals("log.eventType","FirewallAggregated_Event") && (safe("log.sourceport", "") == "" || regexMatch("log.sourceport", + "^[0-9]+$")) && safe("log.sourceport", -1.0) >= 0.0 && safe("log.sourceport", -1.0) <= 65535.0 && safe("log.sourceport", + -1.0) == double(int(safe("log.sourceport", -1.0))) + - cast: + fields: + - origin.port + to: int + where: exists("origin.port") - rename: from: - log.targetport to: target.port - - # Adding action result - - - - # Adding severity field based on log.severity + where: equals("log.eventType","FirewallAggregated_Event") && (safe("log.targetport", "") == "" || regexMatch("log.targetport", + "^[0-9]+$")) && safe("log.targetport", -1.0) >= 0.0 && safe("log.targetport", -1.0) <= 65535.0 && safe("log.targetport", + -1.0) == double(int(safe("log.targetport", -1.0))) + - cast: + fields: + - target.port + to: int + where: exists("target.port") + - grok: + source: log.protocol + patterns: + - fieldName: protocol + pattern: (?s)^.+$ + where: (equals("log.eventType","FirewallAggregated_Event")) && regexMatch("log.protocol", "(?s)^.+$") + - add: + function: string + params: + key: protocol + value: ICMP + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",1) + - add: + function: string + params: + key: protocol + value: TCP + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",6) + - add: + function: string + params: + key: protocol + value: UDP + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",17) + - add: + function: string + params: + key: protocol + value: GRE + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",47) + - add: + function: string + params: + key: protocol + value: ESP + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",50) + - add: + function: string + params: + key: protocol + value: AH + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",51) + - add: + function: string + params: + key: protocol + value: ICMPV6 + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",58) + - add: + function: string + params: + key: protocol + value: SCTP + where: equals("log.eventType","FirewallAggregated_Event") && equals("log.protocol",132) + - rename: + from: + - protocol + to: log.ipProtocolNumber + where: exists("protocol") && greaterOrEqual("protocol",0) - add: function: string params: key: severity value: info - where: oneOf("log.severity", ["INFO", "Info"]) - + where: oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && regexMatch("log.severity", + "(?i)^(information|notice|info)$") - add: function: string params: key: severity value: warning - where: oneOf("log.severity", ["WARNING", "Warning"]) - + where: oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && regexMatch("log.severity", + "(?i)^(warning)$") - add: function: string params: key: severity value: error - where: oneOf("log.severity", ["ERROR", "Error"]) - - # Adding geolocation to origin.ip - - dynamic: - plugin: com.utmstack.geolocation - params: - source: origin.ip - destination: origin.geolocation - where: exists("origin.ip") - - # Adding geolocation to target.ip - - dynamic: - plugin: com.utmstack.geolocation + where: oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && regexMatch("log.severity", + "(?i)^(error)$") + - add: + function: string params: - source: target.ip - destination: target.geolocation - where: exists("target.ip") - - # Removing unused fields - - delete: - fields: - - log.jsonMessage - - log.severity - - # Normalize explicit outcomes; an unknown outcome remains unset. + key: severity + value: critical + where: oneOf("log.eventType", ["Threat_Event", "FirewallAggregated_Event", "HipsAggregated_Event", "HIPSAggregated_Event", + "Audit_Event", "FilteredWebsites_Event", "EnterpriseInspectorAlert_Event", "BlockedFiles_Event"]) && regexMatch("log.severity", + "(?i)^(critical|fatal)$") - add: function: string params: key: actionResult value: success - where: regexMatch("log.result", "(?i)^(success|succeeded|successful|ok|done|accepted|accept|allowed|allow|permitted|permit|passed|pass|true)$") + where: equals("log.eventType","Audit_Event") && regexMatch("log.result", "(?i)^(success|succeeded|successful|ok)$") - add: function: string params: key: actionResult value: failure - where: regexMatch("log.result", "(?i)^(failure|failed|fail|error|invalid|timeout|false)$") + where: equals("log.eventType","Audit_Event") && regexMatch("log.result", "(?i)^(failure|failed|fail|error|invalid|timeout)$") - add: function: string params: key: actionResult value: denied - where: regexMatch("log.result", "(?i)^(denied|deny|blocked|block|dropped|drop|rejected|reject|forbidden|unauthorized|quarantined)$") + where: equals("log.eventType","Audit_Event") && regexMatch("log.result", "(?i)^(denied|deny|blocked|block|rejected|reject|unauthorized)$") - add: function: string params: key: actionResult value: denied - where: regexMatch("action", "(?i)^(deny|denied|block|blocked)$") || regexMatch("log.event", "(?i)^(deny|denied|block|blocked)$") - - # Keep addresses in IP fields and retain other source values under log. - - rename: - from: - - origin.ip - to: log.unparsedOriginIp - where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"])) - - rename: - from: - - target.ip - to: log.unparsedTargetIp - where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"])) + where: (oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","BlockedFiles_Event","FilteredWebsites_Event"]) + || equals("log.eventType","FirewallAggregated_Event")) && regexMatch("action", "(?i)^(deny|denied|block|blocked|prevented)$") - add: function: string params: - key: protocol - value: ICMP - where: equals("protocol",1) - - add: - function: string - params: - key: protocol - value: TCP - where: equals("protocol",6) - - add: - function: string - params: - key: protocol - value: UDP - where: equals("protocol",17) + key: actionResult + value: failure + where: oneOf("log.eventType", ["Threat_Event","HipsAggregated_Event","HIPSAggregated_Event","BlockedFiles_Event","FilteredWebsites_Event"]) + && regexMatch("log.actionerror", "(?s)^.+$") - add: function: string params: - key: protocol - value: GRE - where: equals("protocol",47) + key: log.correlationCandidate.heuristicRemediation + value: match + where: '(equals("log.eventType", "Threat_Event") && + + (regexMatch("log.threatname", "(?i)(heuristic|newheur|suspicious behavior)") || regexMatch("log.threattype", + "(?i)(heuristic|newheur|suspicious behavior)") || regexMatch("log.scannerid", "(?i)(heuristic|newheur|suspicious + behavior)")) && + + regexMatch("action", "(?i)^(cleaned|deleted|quarantined|blocked|denied|prevented)$") && + + (!exists("log.actionerror") || equals("log.actionerror", "")) && + + !equals("log.threathandled", false)) && + + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + + exists("log.endpointKey") && !equals("log.endpointKey", "") + + ' - add: function: string params: - key: protocol - value: ESP - where: equals("protocol",50) + key: log.correlationCandidate.consoleAuthenticationFailure + value: match + where: '(equals("log.eventType", "Audit_Event") && + + equalsIgnoreCase("log.domain", "Native user") && + + equalsIgnoreCase("action", "Login attempt") && oneOf("actionResult", ["failure", "denied"]) && + + exists("target.user") && !oneOf("target.user", ["", "unknown", "-"])) && + + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + + exists("log.endpointKey") && !equals("log.endpointKey", "") + + ' - add: function: string params: - key: protocol - value: AH - where: equals("protocol",51) - - add: - function: string + key: log.correlationCandidate.quarantineFailure + value: match + where: '(equals("log.eventType", "Threat_Event") && equals("actionResult", "failure") && + + regexMatch("log.actionerror", "(?s).+") && + + (regexMatch("action", "(?i)(quarantin|clean)") || regexMatch("log.actionerror", "(?i)(quarantin|clean)"))) + && + + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + + exists("log.endpointKey") && !equals("log.endpointKey", "") + + ' + - dynamic: + plugin: com.utmstack.geolocation params: - key: protocol - value: ICMPV6 - where: equals("protocol",58) - - add: - function: string + source: origin.ip + destination: origin.geolocation + where: exists("origin.ip") + - dynamic: + plugin: com.utmstack.geolocation params: - key: protocol - value: SCTP - where: equals("protocol",132) - - rename: - from: - - protocol - to: log.ipProtocolNumber - where: exists("protocol") && greaterOrEqual("protocol",0) + source: target.ip + destination: target.geolocation + where: exists("target.ip") diff --git a/filters/audits/eset.md b/filters/audits/eset.md index bb3f4e117..4cae10672 100644 --- a/filters/audits/eset.md +++ b/filters/audits/eset.md @@ -1,42 +1,134 @@ -# ESET normalization and rule review +# ESET JSON export and detection contracts -Separate event type from actionResult and classify actual vendor outcomes. +This draft targets the official UTMStack v11 repository. It reviews the ESET +filter and all thirteen existing consumers, with separate inbound/outbound botnet +handling. The target alerts module pins go-sdk v1.1.31; its protobuf and official +wiki define the standard. No customer configuration was changed and no alert was +created during this review. -This draft targets UTMStack `v11`. It contains 1 filter changes -and 1 rule changes for this technology only. Review covered -1 filter configurations and 13 matching shipped rule files. -Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. +## Evidence and limits -## Contract and validation +Fresh read-only counts over the retained ESET source index pattern returned zero +records on thirty v11 instances. One instance remained unreachable. The query +failures were not counted as zero; one stale container cache was refreshed before +its successful retry. No ESET customer raw/normalized pair or deployed filter +could be compared. This is documented-format and offline contract evidence, +not a measured recovery of live detections or reduction in false positives. -- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries. -- Checked documented pipeline ordering, rename/move behavior, open vendor log fields, - event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping. -- Strict SDK configuration decoding and actual CEL compilation pass for this scope. -- 2 synthetic normalization cases pass, including SDK Event conversion and any - trigger predicate assertions recorded in the manifest. -- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied. +The substantive primary reference is ESET PROTECT On-Prem 13.1's JSON-export +specification. The prior 7.0 and 11.0 URLs now redirect to end-of-life notices and +cannot substantiate historical event variants. ESET explicitly says values vary +by endpoint application/version and its lists are not exhaustive. The public +fixtures use fabricated identities and documented field shapes; detection-label +examples are labeled synthetic, not claimed as observed vendor values. -The shared alert-contract PR supplies the reusable Go runner for the manifest in -`plugins/alerts/testdata/filter-contracts/eset.json`. Apply that support before running `go test ./...` in `plugins/alerts`. +## Producer corrections -The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime. +- Recognize bounded bare JSON, the documented ERAServer RFC3164 wrapper and the + existing RFC5424 family. Message text containing an embedded header is not an + outer envelope. The syslog version is not the ESET event class. +- Extract the vendor JSON with SDK key sanitization, then expose `log.eventType` + from `event_type`. Retain vendor severity, event detail and protected `raw` for + investigation; remove the temporary JSON string after extraction. Consumers use structured detection attributes rather than + searching a serialized object for attack words. +- Validate original address fields before standard promotion and geolocation. + Invalid and semantically unspecified addresses remain vendor data. IPv6-only + reporting endpoints and integer transport-port bounds are covered. +- Keep the management server's header identity separate from the managed endpoint. + Firewall source/destination fields retain their physical roles; the `inbound` + boolean determines which side receives the reporting endpoint's host, account + and process. Missing direction does not justify pairing a local hostname with + a remote IP. Filtered website records describe a local client and remote target. +- Map documented user, executable, operating-system, object URL and valid SHA-1 + fields where their roles are established. An Inspect alarm hash is not assumed + to identify a file. Static management groups remain vendor fields rather than + being mislabeled as security roles. +- Map unambiguous absolute executable/object paths into directory and basename + fields. Keep registry targets, percent-encoded file URIs, remote authorities and + URI query/fragment variants under vendor fields. Threat detection name/type map + to malware metadata; a bounded firewall CVE signature maps to the destination + vulnerability field. `occurred` and the examples' `occured` alias become UTC + device time through a guarded temporary field, without reformatting an existing + ingress timestamp as vendor text. +- Preserve native action detail. Explicit blocking means `denied`; audit results + describe the audited operation; remediation errors mean `failure`. A handled + detection, deleted file or event class does not establish successful malicious + execution or a network connection. Vendor severity is retained while standard + severity follows Information/Notice, Warning, Error and Critical/Fatal. -The model starts from synthetic extraction results. It does not run complex grok, -JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch -queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must -still be checked in staging before rollout. No customer false-positive reduction -has been measured and no production rollout is included. +## Consumer corrections +The original consumers mixed a numeric header field with event classes, read a +removed JSON body or severity, and used management-relay identity for endpoint +history. The revised consumers use the produced canonical class, structured +vendor detection attributes and standard action/severity fields. All three +history rules count their own eligible candidates, scoped by collector and a +namespaced managed-endpoint identity; console login history also scopes the +account. Ordinary traffic or unrelated audit events cannot satisfy those counts. +Botnet consumers choose the remote side as adversary separately for inbound and +outbound events without reversing the filter's network fields. Missing direction +is an explicit coverage gap. The generic network rule excludes that detector +family to avoid duplicate alerts with contradictory adversary attribution. +Blocked tampering or exploitation is described as an +attempt, not proof that protection was disabled or compromise succeeded. Normal +policy changes and successful console tasks are not evidence of console abuse; +that consumer detects repeated console authentication failures. + +The generic HIPS rule requires a security-specific operation or detector label; +an ordinary blocked operation under a restrictive policy is insufficient. Specific +registry and PowerShell rules describe blocked activity, which still requires +context and policy tuning rather than proving malicious intent. + +The heuristic, machine-learning, botnet and behavior labels remain detection +heuristics over documented fields. Their completeness across product versions is +not established by this review. An unknown event class remains available under +vendor fields; unsupported historical classes are not declared impossible. + +## Verification + +- 155 fabricated raw cases exercise all fourteen consumers with explicit positive + and negative expectations, SDK Event conversion and exact history-marker parity. + Header, JSON, address, type, calendar, URI and direction boundaries are included. + Malformed JSON is a model error control, not proof of the closed engine's error + handling. The temporary JSON body is absent and protected raw remains unchanged. +- Independent SDK/model retests cover opposite-side botnet duplication, an ordinary + HIPS policy block, an unrelated agent label, and file-URI query/fragment boundaries. +- The shared manifest contains two isolated empty/unknown-class negative controls. + Its limited normalization model does not execute the anchored extraction used + here; raw and positive-consumer proof comes from the standalone source suite. + +- Actual SDK history queries pass against an isolated loopback mock for all three + consumers: below/at threshold, inside/expired window, unrelated history, cleared + forged markers, collector/endpoint/account separation, missing placeholders and + UUID-to-host-to-IP fallback. Explicit denied/rejected login results are included. +- SDK Event/Alert wire-contract checks cover all fourteen positive consumers, + produced grouping fields and physical/adversary direction. They assign sides + according to rule metadata and do not execute the closed correlation service. +- The final shared-runner overlay passes 182 test/subtest records, with no failures + or skips. `git diff --check` passes. These counts include parent test records and + are not unique events or generated alerts. + +Stage the filter and consumers together. New history markers require up to one +hour to populate the longest window. Thresholds count matching indexed documents, +not underlying occurrences represented by vendor aggregation counts. Endpoint +identity uses namespaced UUID, hostname, then valid reported IP; the relay hostname +is never its fallback. Optional detector/file grouping terms can be missing, in +which case the known endpoint forms a broader group. Shared grouping #2590 remains +a separate runtime dependency and rollout. + +Before production approval, replay native records through the closed collector +and EventProcessor, verify actual stored field names, history and alert output, +measure parsing cost and alert volume, and review saved searches that consume +legacy fields/outcomes. Dynamic geolocation, production history and publication +were not exercised here. No source-only offline result establishes that an alert +fired or that every ESET application/version is covered. ## References -- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) -- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [ESET JSON export fields, semantics and examples](https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html) +- [SDK v1.1.31 protobuf](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [SDK field sanitization](https://github.com/threatwinds/go-sdk/blob/v1.1.31/utils/fields.go) - [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) - [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) - -`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom -`log.*` fields are accepted. Existing textual protocol casing and vendor action names -are preserved unless a concrete consumer mismatch requires correction. diff --git a/plugins/alerts/eset_consumers_test.go b/plugins/alerts/eset_consumers_test.go new file mode 100644 index 000000000..a06fca03c --- /dev/null +++ b/plugins/alerts/eset_consumers_test.go @@ -0,0 +1,195 @@ +package main + +// SDK Event/Alert wire-contract tests from synthetic ESET JSON. Alert side +// selection is asserted according to the rule metadata; this is not execution +// of the closed correlation service and does not prove live alert creation. +// lastEvent is the indexed alias resolved from events[0] by the companion fix. +import ( + "encoding/json" + "strings" + "testing" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "github.com/tidwall/gjson" +) + +func esetAlertWire(t *testing.T, rule *plugins.Rule, out string) string { + t.Helper() + event := new(plugins.Event) + if err := utils.StringToProtoMessage(&out, event); err != nil { + t.Fatal(err) + } + alert := &plugins.Alert{Events: []*plugins.Event{event}} + switch rule.Adversary { + case "origin": + alert.Adversary, alert.Target = event.Origin, event.Target + case "target": + alert.Adversary, alert.Target = event.Target, event.Origin + default: + t.Fatalf("unsupported actor side %q", rule.Adversary) + } + wire, err := utils.ProtoMessageToString(alert) + if err != nil { + t.Fatal(err) + } + return *wire +} + +func esetGroupIdentity(t *testing.T, rule *plugins.Rule, out string) string { + t.Helper() + wire := esetAlertWire(t, rule, out) + var parts []string + fields := append(append([]string{}, rule.GroupBy...), rule.DeduplicateBy...) + for _, field := range fields { + path := strings.Replace(field, "lastEvent.", "events.0.", 1) + value := gjson.Get(wire, path) + if !value.Exists() || value.String() == "" { + // These vendor-specific detector details are documented optional. + // The managed endpoint namespace/key and collector remain required. + if field == "lastEvent.log.rulename" || field == "lastEvent.log.ruleid" || field == "lastEvent.log.threatname" || field == "lastEvent.log.target" { + continue + } + t.Errorf("positive raw event cannot resolve alert identity field %s", field) + } + parts = append(parts, field+"="+value.String()) + } + if len(parts) == 0 { + t.Fatal("positive rule has no resolved identity fields") + } + return strings.Join(parts, "\x00") +} + +func TestESETAlertFieldContracts(t *testing.T) { + cfg, rules, cache := esetConfig(t), esetRules(t), plugins.NewCELCache("eset-alert-fields") + covered := map[string]bool{} + for _, fixture := range esetFixtures(t) { + if fixture.ParseError || len(fixture.Matches) == 0 { + continue + } + out := esetParse(t, cfg, fixture.Raw, fixture.DataSource, cache) + for _, name := range fixture.Matches { + rule := rules[name] + if rule == nil { + t.Fatalf("unknown fixture rule %q", name) + } + if yes, err := cache.Eval(rule.Where, out); err != nil || !yes { + t.Fatalf("%s must match %s before alert checks: %v %v", fixture.Name, name, yes, err) + } + esetGroupIdentity(t, rule, out) + covered[name] = true + } + } + if len(rules) != 14 || len(covered) != len(rules) { + t.Fatalf("all14 consumers require raw positive identity coverage: rules=%d covered=%v", len(rules), covered) + } +} + +func TestESETDirectionalActorsAndPolicyBoundaries(t *testing.T) { + cfg, rules, cache := esetConfig(t), esetRules(t), plugins.NewCELCache("eset-consumer-boundaries") + parse := func(m map[string]any) string { + b, err := json.Marshal(m) + if err != nil { + t.Fatal(err) + } + return esetParse(t, cfg, string(b), "eset-test-relay", cache) + } + matches := func(out, name string, want bool) { + t.Helper() + rule := rules[name] + if rule == nil { + t.Fatalf("rule missing: %s", name) + } + if yes, err := cache.Eval(rule.Where, out); err != nil || yes != want { + t.Fatalf("%s match=%v want=%v: %v", name, yes, want, err) + } + } + base := func(kind string) map[string]any { + return map[string]any{"event_type": kind, "hostname": "HOST-A", "source_uuid": "c1c09d6e-24bd-4b45-b1c6-f3c227c9df8e", "ipv4": "192.0.2.10", "severity": "Warning"} + } + for _, inbound := range []bool{false, true} { + event := base("FirewallAggregated_Event") + event["inbound"], event["threat_name"], event["event"], event["action"] = inbound, "Botnet.CnC.Generic", "Botnet communication detected", "blocked" + event["source_address"], event["target_address"] = "192.0.2.10", "198.51.100.9" + event["account"], event["process_name"] = "LAB\\user", `C:\Synthetic\client.exe` + name, other := "botnet_communication_attempts", "botnet_inbound_communication_attempts" + if inbound { + event["source_address"], event["target_address"] = "198.51.100.9", "192.0.2.10" + name, other = other, name + } + out := parse(event) + matches(out, name, true) + matches(out, other, false) + matches(out, "network_attack_detection", false) + if gjson.Get(out, "origin.ip").String() != event["source_address"] || gjson.Get(out, "target.ip").String() != event["target_address"] { + t.Fatal("physical endpoints were swapped") + } + wire := esetAlertWire(t, rules[name], out) + if gjson.Get(wire, "adversary.ip").String() != "198.51.100.9" || gjson.Get(wire, "target.ip").String() != "192.0.2.10" || gjson.Get(wire, "target.host").String() != "HOST-A" { + t.Fatal("botnet alert did not retain remote adversary and managed target") + } + if gjson.Get(wire, "adversary.host").Exists() { + t.Fatal("managed hostname must not be copied to the remote botnet side") + } + if !inbound { + before := esetAlertWire(t, &plugins.Rule{Adversary: "origin"}, out) + if gjson.Get(before, "adversary.ip").String() != "192.0.2.10" { + t.Fatal("old origin-role control must select the managed client") + } + } + for _, invalid := range []any{nil, "false", "true", 0, 1} { + event["inbound"] = invalid + neutral := parse(event) + matches(neutral, name, false) + matches(neutral, other, false) + matches(neutral, "network_attack_detection", false) + if gjson.Get(neutral, "origin.host").Exists() || gjson.Get(neutral, "target.host").Exists() { + t.Fatal("unknown direction must not assign managed hostname to a network side") + } + } + delete(event, "inbound") + neutral := parse(event) + matches(neutral, name, false) + matches(neutral, other, false) + } + policy := base("HipsAggregated_Event") + policy["application"], policy["operation"], policy["target"], policy["action"], policy["rule_name"] = `C:\Synthetic\utility.exe`, "Read file", `C:\Data\ordinary.txt`, "blocked", "Restrictive file access policy" + out := parse(policy) + matches(out, "host_intrusion_prevention_triggers", false) + matches(out, "suspicious_process_behavior", false) + // Specific activity rules are intentionally factual policy-block alerts; + // they do not claim that these benign examples establish malware. + policy["application"] = `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe` + out = parse(policy) + matches(out, "suspicious_powershell_activity_blocked", true) + matches(out, "host_intrusion_prevention_triggers", false) + policy["application"], policy["operation"], policy["target"] = `C:\Synthetic\utility.exe`, "Write registry value", `HKLM\Software\Synthetic` + out = parse(policy) + matches(out, "registry_modification_attempts_blocked", true) + matches(out, "host_intrusion_prevention_triggers", false) + policy["operation"], policy["rule_name"] = "Attempt to run a suspicious object", "Suspicious application launch" + out = parse(policy) + matches(out, "host_intrusion_prevention_triggers", true) + inspect := base("EnterpriseInspectorAlert_Event") + inspect["rulename"] = "Backup agent disabled" + out = parse(inspect) + matches(out, "eset_agent_tampering", false) + // Incidental tokens in filenames, account names or arbitrary descriptions + // must not reclassify an unrelated structured antivirus detection. + threat := base("Threat_Event") + threat["threat_name"], threat["scanner_id"], threat["action_taken"] = "Synthetic.Other", "Real-time file system protection", "Detected" + threat["object_uri"] = "file:///C:/NewHeur-machine-learning-botnet-ransomware.encrypted" + threat["username"] = "registry exploit quarantine failed" + out = parse(threat) + for name := range rules { + matches(out, name, false) + } + audit := base("Audit_Event") + audit["domain"], audit["action"], audit["target"], audit["result"], audit["user"] = "Native user", "Login attempt", "AttemptedAdmin", "Failure", "AuditingActor" + out = parse(audit) + matches(out, "eset_console_abuse", true) + wire := esetAlertWire(t, rules["eset_console_abuse"], out) + if gjson.Get(wire, "target.user").String() != "AttemptedAdmin" || gjson.Get(wire, "adversary.user").String() != "AuditingActor" { + t.Fatal("attempted account and supplied audit actor must remain distinct") + } +} diff --git a/plugins/alerts/eset_contract_test.go b/plugins/alerts/eset_contract_test.go new file mode 100644 index 000000000..31020b84c --- /dev/null +++ b/plugins/alerts/eset_contract_test.go @@ -0,0 +1,435 @@ +package main + +// Offline ESET extraction model, not the closed EventProcessor. +// Explicit YAML grok/JSON/rename/cast/add/reformat/delete are modeled. JSON +// key sanitization uses SDK utilities; malformed JSON is an explicit model error. +// CEL and Event serialization use SDK v1.1.31. Separate history tests exercise +// actual SDK query behavior. External geolocation and the closed executor are not run. +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" + "testing" + "text/template" + "time" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "github.com/tidwall/gjson" + "google.golang.org/protobuf/encoding/protojson" +) + +type esetFixture struct { + Name string `json:"name"` + DataSource string `json:"dataSource"` + ID *string `json:"id,omitempty"` + Raw string `json:"raw"` + Ingress map[string]any `json:"ingress,omitempty"` + ParseError bool `json:"parseError,omitempty"` + Expected map[string]any `json:"expected"` + Absent []string `json:"absent"` + Matches []string `json:"matches"` +} + +func esetPut(m map[string]any, path string, value any, remove bool) { + p := strings.Split(path, ".") + for _, k := range p[:len(p)-1] { + n, ok := m[k].(map[string]any) + if !ok { + if remove { + return + } + n = map[string]any{} + m[k] = n + } + m = n + } + if remove { + delete(m, p[len(p)-1]) + } else { + m[p[len(p)-1]] = value + } +} +func esetGet(m map[string]any, p string) (any, bool) { + var v any = m + for _, k := range strings.Split(p, ".") { + n, ok := v.(map[string]any) + if !ok { + return nil, false + } + v, ok = n[k] + if !ok { + return nil, false + } + } + return v, true +} +func esetConfig(t *testing.T) *plugins.Config { + t.Helper() + b, e := utils.ReadPbYaml("../../filters/antivirus/esmc-eset.yml") + if e != nil { + t.Fatal(e) + } + c := new(plugins.Config) + if e = protojson.Unmarshal(b, c); e != nil { + t.Fatal(e) + } + return c +} +func esetRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regexp { + t.Helper() + var pattern strings.Builder + for i, p := range g.Patterns { + if p.FieldName != "" { + fmt.Fprintf(&pattern, "(?P%s)", i, p.Pattern) + } else { + pattern.WriteString("(?:" + p.Pattern + ")") + } + } + // ESET uses explicit YAML expressions, with no approximated built-in grok aliases. + pats := map[string]string{} + for k, v := range cfg.Patterns { + pats[k] = v + } + tmpl, e := template.New("grok").Option("missingkey=error").Parse(pattern.String()) + if e != nil { + t.Fatal(e) + } + var b bytes.Buffer + if e = tmpl.Execute(&b, pats); e != nil { + t.Fatal(e) + } + r, e := regexp.Compile(b.String()) + if e != nil { + t.Fatal(e) + } + return r +} +func esetParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache) string { + return esetParseEvent(t, cfg, raw, dataSource, "synthetic-ingress-event", cache) +} +func esetParseEvent(t *testing.T, cfg *plugins.Config, raw, dataSource, id string, cache *plugins.CELCache) string { + return esetParseInput(t, cfg, raw, dataSource, id, nil, cache) +} +func esetParseInput(t *testing.T, cfg *plugins.Config, raw, dataSource, id string, ingress map[string]any, cache *plugins.CELCache) string { + t.Helper() + draft := map[string]any{"raw": raw, "dataType": "antivirus-esmc-eset", "dataSource": dataSource, "log": map[string]any{}} + for key, value := range ingress { + draft[key] = value + } + if id != "" { + draft["id"] = id + } + for _, stage := range cfg.Pipeline { + matched := false + for _, dataType := range stage.DataTypes { + if dataType == "antivirus-esmc-eset" { + matched = true + } + } + if !matched { + continue + } + for _, s := range stage.Steps { + b, e := protojson.Marshal(s) + if e != nil { + t.Fatal(e) + } + var step map[string]map[string]any + if e = json.Unmarshal(b, &step); e != nil { + t.Fatal(e) + } + for kind, body := range step { + if w, ok := body["where"].(string); ok && w != "" { + snapshot, err := json.Marshal(draft) + if err != nil { + t.Fatal(err) + } + match, e := cache.Eval(w, string(snapshot)) + if e != nil { + t.Fatal(e) + } + if !match { + continue + } + } + switch kind { + case "grok": + g := s.Grok + src := g.Source + if src == "" { + src = "raw" + } + v, ok := esetGet(draft, src) + if !ok { + continue + } + str, ok := v.(string) + if !ok { + t.Fatalf("non-string grok source %s", src) + } + r := esetRegex(t, g, cfg) + m := r.FindStringSubmatch(str) + if m == nil { + continue + } + for i, p := range g.Patterns { + if p.FieldName != "" { + esetPut(draft, p.FieldName, m[r.SubexpIndex(fmt.Sprintf("f%d", i))], false) + } + } + case "rename": + for _, p := range s.Rename.From { + if v, ok := esetGet(draft, p); ok { + esetPut(draft, s.Rename.To, v, false) + esetPut(draft, p, nil, true) + break + } + } + case "trim": + for _, p := range s.Trim.Fields { + if v, ok := esetGet(draft, p); ok { + str, ok := v.(string) + if !ok { + continue + } + switch s.Trim.Function { + case "prefix": + str = strings.TrimPrefix(str, s.Trim.Substring) + case "suffix": + str = strings.TrimSuffix(str, s.Trim.Substring) + default: + t.Fatalf("unsupported trim %s", s.Trim.Function) + } + esetPut(draft, p, str, false) + } + } + case "add": + if s.Add.Function != "string" { + t.Fatalf("unsupported add function %s", s.Add.Function) + } + esetPut(draft, s.Add.Params["key"].GetStringValue(), s.Add.Params["value"].AsInterface(), false) + case "delete": + for _, p := range s.Delete.Fields { + esetPut(draft, p, nil, true) + } + case "dynamic": + if s.Dynamic.Plugin != "com.utmstack.geolocation" { + t.Fatalf("unsupported dynamic plugin %s", s.Dynamic.Plugin) + } + field := s.Dynamic.Params["source"].GetStringValue() + v, ok := esetGet(draft, field) + if !ok { + t.Fatalf("missing dynamic source %s", field) + } + ip := net.ParseIP(fmt.Sprint(v)) + if ip == nil || ip.IsUnspecified() { + t.Fatalf("invalid address reaches geolocation: %s", field) + } + // The external geolocation service is not executed. + case "json": + source, ok := esetGet(draft, s.Json.Source) + if !ok { + continue + } + str, ok := source.(string) + if !ok { + t.Fatalf("JSON source is not a string") + } + var parsed map[string]any + if e := json.Unmarshal([]byte(str), &parsed); e != nil { + return "MODEL_JSON_ERROR" + } + for key, value := range esetSanitizeJSON(parsed) { + esetPut(draft, "log."+key, value, false) + } + case "reformat": + if s.Reformat.Function != "time" { + t.Fatalf("unsupported reformat %s", s.Reformat.Function) + } + for _, field := range s.Reformat.Fields { + if value, ok := esetGet(draft, field); ok { + str, ok := value.(string) + if !ok { + t.Fatalf("non-string timestamp %s", field) + } + parsed, err := time.Parse(s.Reformat.FromFormat, str) + if err != nil { + t.Fatalf("invalid guarded timestamp %s: %v", field, err) + } + esetPut(draft, field, parsed.Format(s.Reformat.ToFormat), false) + } + } + case "cast": + for _, field := range s.Cast.Fields { + if value, ok := esetGet(draft, field); ok { + switch s.Cast.To { + case "string": + esetPut(draft, field, utils.CastString(value), false) + case "int": + esetPut(draft, field, utils.CastInt64(value), false) + default: + t.Fatalf("unsupported cast %s", s.Cast.To) + } + } + } + case "drop": + return "" + default: + t.Fatalf("unsupported filter step %s", kind) + } + } + } + } + b, e := json.Marshal(draft) + if e != nil { + t.Fatal(e) + } + in := string(b) + ev := new(plugins.Event) + if e = utils.StringToProtoMessage(&in, ev); e != nil { + t.Fatal(e) + } + out, e := utils.ProtoMessageToString(ev) + if e != nil { + t.Fatal(e) + } + return *out +} +func esetRules(t *testing.T) map[string]*plugins.Rule { + t.Helper() + paths, e := filepath.Glob("../../rules/antivirus/esmc-eset/*.yml") + if e != nil { + t.Fatal(e) + } + out := map[string]*plugins.Rule{} + for _, p := range paths { + b, e := utils.ReadPbYaml(p) + if e != nil { + t.Fatal(e) + } + r := new(plugins.Rule) + if e = protojson.Unmarshal(b, r); e != nil { + t.Fatal(e) + } + r.Normalize() + out[strings.TrimSuffix(filepath.Base(p), ".yml")] = r + } + return out +} + +func esetSanitizeJSON(input map[string]any) map[string]any { + out := map[string]any{} + for key, value := range input { + utils.SanitizeField(&key) + if nested, ok := value.(map[string]any); ok { + value = esetSanitizeJSON(nested) + } + out[key] = value + } + return out +} + +func esetFixtures(t *testing.T) []esetFixture { + t.Helper() + b, e := os.ReadFile("testdata/eset_raw.json") + if e != nil { + t.Fatal(e) + } + var cases []esetFixture + if e = json.Unmarshal(b, &cases); e != nil { + t.Fatal(e) + } + return cases +} + +func TestESETRawContracts(t *testing.T) { + cfg, rules, cache := esetConfig(t), esetRules(t), plugins.NewCELCache("eset-raw") + if len(rules) != 14 { + t.Fatalf("expected14ESETconsumers, got%d", len(rules)) + } + positives, negatives := map[string]int{}, map[string]int{} + for _, f := range esetFixtures(t) { + t.Run(f.Name, func(t *testing.T) { + id := "synthetic-ingress-event" + if f.ID != nil { + id = *f.ID + } + out := esetParseInput(t, cfg, f.Raw, f.DataSource, id, f.Ingress, cache) + if f.ParseError { + if out != "MODEL_JSON_ERROR" { + t.Error("expected malformed JSON model error") + } + return + } + if out == "MODEL_JSON_ERROR" { + t.Fatal("unexpected malformed JSON model error") + } + for field, want := range f.Expected { + got := gjson.Get(out, field) + if !got.Exists() || !reflect.DeepEqual(got.Value(), want) { + t.Errorf("%s got %v want %v", field, got.Value(), want) + } + } + for _, field := range f.Absent { + if gjson.Get(out, field).Exists() { + t.Errorf("unexpected %s", field) + } + } + if gjson.Get(out, "raw").String() != f.Raw { + t.Error("raw changed") + } + expected := map[string]bool{} + for _, n := range f.Matches { + expected[n] = true + } + for name, r := range rules { + if f.Matches == nil { + t.Fatal("fixture requires explicit predicate expectations") + } + yes, e := cache.Eval(r.Where, out) + if e != nil { + t.Fatalf("%s CEL %v", name, e) + } + if yes { + positives[name]++ + } else { + negatives[name]++ + } + if yes != expected[name] { + t.Errorf("%s match=%v want=%v", name, yes, expected[name]) + } + marker := map[string]string{"advanced_heuristic_detection_triggers": "heuristicRemediation", "eset_console_abuse": "consoleAuthenticationFailure", "eset_quarantine_failures": "quarantineFailure"}[name] + if marker != "" && (gjson.Get(out, "log.correlationCandidate."+marker).String() == "match") != yes { + t.Errorf("%s candidate mismatch", name) + } + + if yes { + for _, search := range r.Correlation { + for _, term := range search.With { + v := term.Value.GetStringValue() + if strings.HasPrefix(v, "{{.") { + p := strings.TrimSuffix(strings.TrimPrefix(v, "{{."), "}}") + if !gjson.Get(out, p).Exists() { + t.Errorf("%s missing history %s", name, p) + } + } + } + } + + } + } + }) + } + for name := range rules { + if positives[name] == 0 || negatives[name] == 0 { + t.Errorf("%s missing positive/negative raw predicate coverage", name) + } + } +} diff --git a/plugins/alerts/eset_history_test.go b/plugins/alerts/eset_history_test.go new file mode 100644 index 000000000..0867f14c8 --- /dev/null +++ b/plugins/alerts/eset_history_test.go @@ -0,0 +1,308 @@ +package main + +// Synthetic ESET JSON exercises the documented field contract, including +// explicitly labeled compatibility assumptions for non-exhaustive vendor values. +// Parsing uses the bounded offline model; CEL, mapping/placeholder resolution and +// threshold decisions use SDK1.1.31 against an isolated loopback OpenSearch mock. +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "strings" + "testing" + "time" + + sdkos "github.com/threatwinds/go-sdk/os" + "github.com/threatwinds/go-sdk/plugins" + "github.com/tidwall/gjson" +) + +func TestESETSDKHistory(t *testing.T) { + if os.Getenv("UTM_ESET_HISTORY_CHILD") != "1" { + command := exec.Command(os.Args[0], "-test.run=^TestESETSDKHistory$") + command.Env = append(os.Environ(), "UTM_ESET_HISTORY_CHILD=1") + if out, err := command.CombinedOutput(); err != nil { + t.Fatalf("isolated history test: %v\n%s", err, out) + } + return + } + cfg, rules, cache := esetConfig(t), esetRules(t), plugins.NewCELCache("eset-history") + const collector = "eset-test-relay" + const endpoint = "20cc734b-379b-4021-8c7f-f21d1e470f29" + var history []string + queries := 0 + var expectedTerms map[string]string + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if strings.HasSuffix(r.URL.Path, "/_mapping") { + // Text-with-keyword and exact IP mappings both exercise SDK lookup. + _, _ = io.WriteString(w, `{"v11-log-antivirus-esmc-eset-test":{"mappings":{"properties":{"@timestamp":{"type":"date"},"dataSource":{"type":"text","fields":{"keyword":{"type":"keyword"}}},"target":{"properties":{"user":{"type":"text","fields":{"keyword":{"type":"keyword"}}}}},"log":{"properties":{"endpointKey":{"type":"text","fields":{"keyword":{"type":"keyword"}}},"endpointKeyType":{"type":"keyword"},"correlationCandidate":{"properties":{"heuristicRemediation":{"type":"keyword"},"consoleAuthenticationFailure":{"type":"keyword"},"quarantineFailure":{"type":"keyword"}}}}}}}}`) + return + } + if r.URL.Path != "/v11-log-antivirus-esmc-eset-*/_search" { + t.Errorf("unexpected request %s", r.URL.Path) + http.Error(w, "unsupported request", http.StatusBadRequest) + return + } + queries++ + body, err := io.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + query := string(body) + clauses := append(gjson.Get(query, "query.bool.must").Array(), gjson.Get(query, "query.bool.filter").Array()...) + if len(clauses) != len(expectedTerms)+1 { + t.Errorf("expected exact identity/candidate scopes and time range, got %s", query) + } + expected := map[string]string{} + for field, value := range expectedTerms { + expected[field] = value + } + for _, clause := range clauses { + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + path := strings.TrimSuffix(field, ".keyword") + want, exists := expected[path] + if !exists || value.Get("value").String() != want { + t.Errorf("unexpected history term %s", term.Raw) + } + delete(expected, path) + } + } + } + if len(expected) != 0 { + t.Errorf("history omitted scopes: %v", expected) + } + hits := []map[string]any{} + for _, doc := range history { + match := true + for _, clause := range clauses { + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + actual := gjson.Get(doc, strings.TrimSuffix(field, ".keyword")) + if !actual.Exists() || actual.String() != value.Get("value").String() { + match = false + } + } + } else if span := clause.Get("range"); span.Exists() { + for field, bounds := range span.Map() { + stamp, err := time.Parse(time.RFC3339Nano, gjson.Get(doc, field).String()) + if err != nil { + t.Error(err) + } + cutoff, err := time.Parse(time.RFC3339Nano, bounds.Get("gte").String()) + if err != nil { + t.Error(err) + } + if field != "@timestamp" || !bounds.Get("gte").Exists() || stamp.Before(cutoff) { + match = false + } + } + } else { + t.Errorf("unsupported history clause %s", clause.Raw) + match = false + } + } + if match { + hits = append(hits, map[string]any{"_id": fmt.Sprint(len(hits)), "_index": "v11-log-antivirus-esmc-eset-test", "_source": map[string]any{}}) + } + } + _ = json.NewEncoder(w).Encode(map[string]any{"took": 1, "hits": map[string]any{"total": map[string]any{"value": len(hits), "relation": "eq"}, "hits": hits}}) + })) + defer server.Close() + if err := sdkos.Connect([]string{server.URL}, "", ""); err != nil { + t.Fatal(err) + } + mutate := func(doc, field string, value any) string { + var m map[string]any + if err := json.Unmarshal([]byte(doc), &m); err != nil { + t.Fatal(err) + } + parts := strings.Split(field, ".") + node := m + for _, part := range parts[:len(parts)-1] { + next, ok := node[part].(map[string]any) + if !ok { + next = map[string]any{} + node[part] = next + } + node = next + } + if value == nil { + delete(node, parts[len(parts)-1]) + } else { + node[parts[len(parts)-1]] = value + } + b, err := json.Marshal(m) + if err != nil { + t.Fatal(err) + } + return string(b) + } + + rawDocument := func(event map[string]any) string { + b, err := json.Marshal(event) + if err != nil { + t.Fatal(err) + } + return string(b) + } + base := func(eventType string) map[string]any { + return map[string]any{"event_type": eventType, "source_uuid": endpoint, "hostname": "HOST-A", "ipv4": "192.0.2.10", "severity": "Warning"} + } + heuristic := base("Threat_Event") + heuristic["threat_name"], heuristic["scanner_id"], heuristic["action_taken"], heuristic["threat_handled"] = "Synthetic/NewHeur.Test", "Real-time file system protection", "Deleted", true + console := base("Audit_Event") + console["domain"], console["action"], console["target"], console["user"], console["result"] = "Native user", "Login attempt", "Administrator", "", "Failure" + quarantine := base("Threat_Event") + quarantine["threat_name"], quarantine["action_taken"], quarantine["action_error"], quarantine["threat_handled"] = "Synthetic/Test", "Quarantine", "Quarantine failed: access denied", false + // NewHeur, Failure and remediation strings below are compatibility test + // values, not observations or an exhaustive ESET export vocabulary. + cases := []struct { + rule, marker, within string + count uint64 + event map[string]any + }{ + {"advanced_heuristic_detection_triggers", "heuristicRemediation", "30m", 3, heuristic}, + {"eset_console_abuse", "consoleAuthenticationFailure", "30m", 10, console}, + {"eset_quarantine_failures", "quarantineFailure", "1h", 5, quarantine}, + } + for _, test := range cases { + t.Run(test.rule, func(t *testing.T) { + rule := rules[test.rule] + if rule == nil || len(rule.Correlation) != 1 { + t.Fatal("required rule/history request missing") + } + search := rule.Correlation[0] + n := 4 + if test.rule == "eset_console_abuse" { + n++ + } + if search.Count != test.count || search.Within != test.within || len(search.With) != n { + t.Fatal("history threshold or scope changed") + } + expectedTerms = map[string]string{"dataSource": collector, "log.endpointKeyType": "uuid", "log.endpointKey": endpoint, "log.correlationCandidate." + test.marker: "match"} + if test.rule == "eset_console_abuse" { + expectedTerms["target.user"] = "Administrator" + } + parse := func(raw string) string { return esetParse(t, cfg, raw, collector, cache) } + raw := rawDocument(test.event) + out := parse(raw) + marker := "log.correlationCandidate." + test.marker + if yes, err := cache.Eval(rule.Where, out); err != nil || !yes || gjson.Get(out, marker).String() != "match" { + t.Fatalf("synthetic documented-field positive did not produce candidate: %v %v", yes, err) + } + stamp := time.Now().Add(-time.Minute).UTC().Format(time.RFC3339Nano) + prior := mutate(out, "@timestamp", stamp) + check := func(name, doc string, count uint64, want bool) { + t.Run(name, func(t *testing.T) { + history = nil + for i := uint64(0); i < count; i++ { + history = append(history, doc) + } + match, _, err := search.Execute(&out) + if err != nil || match != want { + t.Fatalf("history got %v want %v: %v", match, want, err) + } + }) + } + check("below_threshold", prior, test.count-1, false) + check("at_threshold", prior, test.count, true) + duration, err := time.ParseDuration(test.within) + if err != nil { + t.Fatal(err) + } + check("inside_window", mutate(prior, "@timestamp", time.Now().Add(-duration+10*time.Second).UTC().Format(time.RFC3339Nano)), test.count, true) + check("expired", mutate(prior, "@timestamp", time.Now().Add(-duration-10*time.Second).UTC().Format(time.RFC3339Nano)), test.count, false) + check("different_collector", mutate(prior, "dataSource", "other-relay"), test.count, false) + check("different_endpoint", mutate(prior, "log.endpointKey", "OTHER-ENDPOINT"), test.count, false) + check("different_identity_namespace", mutate(prior, "log.endpointKeyType", "host"), test.count, false) + check("unmarked_history", mutate(prior, marker, nil), test.count, false) + if test.rule == "eset_console_abuse" { + check("different_attempted_account", mutate(prior, "target.user", "OtherAdmin"), test.count, false) + } + changes := []map[string]any{} + switch test.rule { + case "advanced_heuristic_detection_triggers": + changes = []map[string]any{{"threat_name": "Synthetic/Other", "object_uri": "file:///C:/NewHeur-Suspicious-behavior.exe"}, {"action_taken": "Detected"}, {"action_error": "File busy"}, {"threat_handled": false}, {"event_type": "Audit_Event"}} + case "eset_console_abuse": + changes = []map[string]any{{"result": "Success"}, {"action": "Policy assigned", "detail": "administrator login failed"}, {"domain": "Client task"}, {"target": nil}, {"event_type": "Threat_Event"}} + case "eset_quarantine_failures": + changes = []map[string]any{{"action_error": ""}, {"action_taken": "Blocked", "action_error": "File busy", "object_uri": "file:///C:/quarantine-failed.exe"}, {"event_type": "Audit_Event"}} + } + for i, change := range changes { + modified := raw + for field, value := range change { + modified = mutate(modified, field, value) + } + // Attempt to supply the marker in raw JSON; the producer must + // clear it and recompute from actual class/action/identity. + modified = mutate(modified, "correlationCandidate."+test.marker, "match") + benign := parse(modified) + if yes, err := cache.Eval(rule.Where, benign); err != nil || yes || gjson.Get(benign, marker).Exists() { + t.Fatalf("noncandidate raw %d retained candidate: %v %v", i, yes, err) + } + check(fmt.Sprintf("noncandidate_raw_%d", i), mutate(benign, "@timestamp", stamp), test.count, false) + } + if test.rule == "eset_console_abuse" { + for _, result := range []string{"Denied", "Rejected"} { + denied := parse(mutate(raw, "result", result)) + if yes, err := cache.Eval(rule.Where, denied); err != nil || !yes || gjson.Get(denied, marker).String() != "match" || gjson.Get(denied, "actionResult").String() != "denied" { + t.Fatalf("explicitly rejected login must count as unsuccessful: %v %v", yes, err) + } + check("explicit_result_"+result, mutate(denied, "@timestamp", stamp), test.count, true) + } + } + fields := []string{"dataSource", "log.endpointKeyType", "log.endpointKey"} + if test.rule == "eset_console_abuse" { + fields = append(fields, "target.user") + } + for _, field := range fields { + t.Run("missing_"+field, func(t *testing.T) { + missing := mutate(out, field, nil) + if yes, err := cache.Eval(rule.Where, missing); err != nil || yes { + t.Fatalf("missing identity remains eligible: %v %v", yes, err) + } + before := queries + if _, _, err := search.Execute(&missing); err == nil || queries != before { + t.Fatal("nil identity must abort before search") + } + }) + } + for _, identity := range []struct { + kind, key string + remove []string + }{ + {"host", "HOST-A", []string{"source_uuid"}}, + {"ip", "192.0.2.10", []string{"source_uuid", "hostname"}}, + } { + alternative := raw + for _, field := range identity.remove { + alternative = mutate(alternative, field, nil) + } + out = parse(alternative) + if yes, err := cache.Eval(rule.Where, out); err != nil || !yes || gjson.Get(out, marker).String() != "match" { + t.Fatalf("%s fallback must remain eligible: %v %v", identity.kind, yes, err) + } + expectedTerms["log.endpointKeyType"], expectedTerms["log.endpointKey"] = identity.kind, identity.key + check(identity.kind+"_fallback_threshold", mutate(out, "@timestamp", stamp), test.count, true) + } + noEndpoint := raw + for _, field := range []string{"source_uuid", "hostname", "ipv4"} { + noEndpoint = mutate(noEndpoint, field, nil) + } + // A management relay in the syslog header must never rescue the + // missing managed endpoint identity used by this correlation. + noEndpoint = "Sep 17 12:00:00 RELAY-NOT-ENDPOINT ERAServer[123]: " + noEndpoint + missing := parse(noEndpoint) + if yes, err := cache.Eval(rule.Where, missing); err != nil || yes || gjson.Get(missing, marker).Exists() { + t.Fatalf("relay cannot replace managed identity: %v %v", yes, err) + } + }) + } +} diff --git a/plugins/alerts/testdata/eset_raw.json b/plugins/alerts/testdata/eset_raw.json new file mode 100644 index 000000000..3075a5a20 --- /dev/null +++ b/plugins/alerts/testdata/eset_raw.json @@ -0,0 +1,1852 @@ +[ + { + "name": "documented threat deletion retains remediation distinction", + "dataSource": "synthetic-eset-collector", + "raw": "Sep 17 12:30:05 manager.example.invalid ERAServer[5648]: {\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"action_taken\":\"Deleted\",\"threat_handled\":true,\"threat_name\":\"Synthetic.TestThreat\",\"processname\":\"C:\\\\Program Files\\\\Example\\\\scanner.exe\",\"username\":\"LAB\\\\analyst\",\"hash\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}", + "expected": { + "log.eventType": "Threat_Event", + "log.eventtype": "Threat_Event", + "target.ip": "192.0.2.30", + "target.host": "endpoint.example.invalid", + "target.operatingSystem": "Microsoft Windows 11 Pro", + "origin.process": "scanner.exe", + "origin.user": "LAB\\analyst", + "target.sha1": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "action": "Deleted", + "severity": "warning", + "log.severity": "Warning", + "deviceTime": "2026-09-17T12:30:00Z", + "log.threathandled": true, + "log.endpointKey": "synthetic-endpoint-1", + "log.endpointKeyType": "uuid", + "origin.path": "C:\\Program Files\\Example\\" + }, + "absent": [ + "actionResult", + "log.msgType", + "log.jsonMessage" + ], + "matches": [] + }, + { + "name": "accepted bare JSON envelope", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"action_taken\":\"blocked\"}", + "expected": { + "actionResult": "denied", + "target.host": "endpoint.example.invalid", + "log.eventType": "Threat_Event" + }, + "absent": [ + "log.msgType" + ], + "matches": [] + }, + { + "name": "accepted rfc5424 JSON envelope", + "dataSource": "synthetic-eset-collector", + "raw": "<134>1 2026-09-17T12:30:05Z manager.example.invalid ERAServer 5648 - - {\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"action_taken\":\"blocked\"}", + "expected": { + "actionResult": "denied", + "target.host": "endpoint.example.invalid", + "log.eventType": "Threat_Event" + }, + "absent": [ + "log.msgType" + ], + "matches": [] + }, + { + "name": "accepted pri3164 JSON envelope", + "dataSource": "synthetic-eset-collector", + "raw": "<134>Sep 17 12:30:05 manager.example.invalid ERAServer[5648]: {\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"action_taken\":\"blocked\"}", + "expected": { + "actionResult": "denied", + "target.host": "endpoint.example.invalid", + "log.eventType": "Threat_Event" + }, + "absent": [ + "log.msgType" + ], + "matches": [] + }, + { + "name": "embedded RFC3164", + "dataSource": "synthetic-eset-collector", + "raw": "application message=\"Sep 17 12:30:05 manager.example.invalid ERAServer[5648]: {\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}\"", + "expected": {}, + "absent": [ + "target.ip", + "target.host", + "actionResult", + "log.eventType" + ], + "matches": [] + }, + { + "name": "embedded RFC5424", + "dataSource": "synthetic-eset-collector", + "raw": "application message=\"<134>1 2026-09-17T12:30:05Z manager.example.invalid ERAServer 5648 - - {\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}\"", + "expected": {}, + "absent": [ + "target.ip", + "target.host", + "actionResult", + "log.eventType" + ], + "matches": [] + }, + { + "name": "wrong RFC3164 program", + "dataSource": "synthetic-eset-collector", + "raw": "Sep 17 12:30:05 manager.example.invalid unrelated[5648]: {\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "target.ip", + "target.host", + "actionResult", + "log.eventType" + ], + "matches": [] + }, + { + "name": "JSON string cannot supply class or identity", + "dataSource": "synthetic-eset-collector", + "raw": "{\"message\":\"{\\\"event_type\\\":\\\"Threat_Event\\\",\\\"ipv4\\\":\\\"198.51.100.42\\\",\\\"action_taken\\\":\\\"blocked\\\"}\"}", + "expected": {}, + "absent": [ + "log.eventType", + "origin.ip", + "target.ip", + "actionResult" + ], + "matches": [] + }, + { + "name": "forged derived fields recomputed", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"eventType\":\"Audit_Event\",\"endpointKey\":\"forged\",\"endpointKeyType\":\"uuid\",\"correlationCandidate\":{\"heuristicRemediation\":\"match\"}}", + "expected": { + "log.eventType": "Threat_Event", + "log.endpointKey": "synthetic-endpoint-1" + }, + "absent": [ + "log.correlationCandidate" + ], + "matches": [] + }, + { + "name": "invalid ipv4 bad-address", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"bad-address\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "bad-address" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"0.0.0.0\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "0.0.0.0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 ::", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"::\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "::" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 ::0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"::0\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "::0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 0:0:0:0:0:0:0:0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"0:0:0:0:0:0:0:0\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 ::ffff:0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"::ffff:0.0.0.0\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": "::ffff:0.0.0.0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":123,\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": 123 + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":false,\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": false + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid ipv4 {'ip': '192.0.2.10'}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":{\"ip\":\"192.0.2.10\"},\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.ipv4": { + "ip": "192.0.2.10" + } + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address bad-address", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"bad-address\"}", + "expected": { + "log.sourceaddress": "bad-address" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address 0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"0.0.0.0\"}", + "expected": { + "log.sourceaddress": "0.0.0.0" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address ::", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"::\"}", + "expected": { + "log.sourceaddress": "::" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address ::0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"::0\"}", + "expected": { + "log.sourceaddress": "::0" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address 0:0:0:0:0:0:0:0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"0:0:0:0:0:0:0:0\"}", + "expected": { + "log.sourceaddress": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address ::ffff:0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"::ffff:0.0.0.0\"}", + "expected": { + "log.sourceaddress": "::ffff:0.0.0.0" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":123}", + "expected": { + "log.sourceaddress": 123 + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":false}", + "expected": { + "log.sourceaddress": false + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid source_address {'ip': '192.0.2.10'}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":{\"ip\":\"192.0.2.10\"}}", + "expected": { + "log.sourceaddress": { + "ip": "192.0.2.10" + } + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address bad-address", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"bad-address\"}", + "expected": { + "log.targetaddress": "bad-address" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address 0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"0.0.0.0\"}", + "expected": { + "log.targetaddress": "0.0.0.0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address ::", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"::\"}", + "expected": { + "log.targetaddress": "::" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address ::0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"::0\"}", + "expected": { + "log.targetaddress": "::0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address 0:0:0:0:0:0:0:0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"0:0:0:0:0:0:0:0\"}", + "expected": { + "log.targetaddress": "0:0:0:0:0:0:0:0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address ::ffff:0.0.0.0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"::ffff:0.0.0.0\"}", + "expected": { + "log.targetaddress": "::ffff:0.0.0.0" + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":123}", + "expected": { + "log.targetaddress": 123 + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":false}", + "expected": { + "log.targetaddress": false + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "invalid target_address {'ip': '192.0.2.10'}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":{\"ip\":\"192.0.2.10\"}}", + "expected": { + "log.targetaddress": { + "ip": "192.0.2.10" + } + }, + "absent": [ + "target.ip" + ], + "matches": [] + }, + { + "name": "IPv6 reporting endpoint fallback", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"0.0.0.0\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"ipv6\":\"2001:db8::30\"}", + "expected": { + "target.ip": "2001:db8::30", + "log.ipv4": "0.0.0.0" + }, + "absent": [], + "matches": [] + }, + { + "name": "valid IPv6 endpoint ending double colon", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"ipv6\":\"2001:db8::\"}", + "expected": { + "target.ip": "2001:db8::" + }, + "absent": [], + "matches": [] + }, + { + "name": "inbound firewall physical endpoints", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"198.51.100.10\",\"target_address\":\"192.0.2.30\",\"source_port\":49152,\"target_port\":443,\"protocol\":\"TCP\",\"inbound\":true,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "origin.ip": "198.51.100.10", + "target.ip": "192.0.2.30", + "target.host": "endpoint.example.invalid", + "target.user": "LAB\\svc", + "target.process": "service.exe", + "origin.port": 49152, + "target.port": 443, + "actionResult": "denied", + "target.path": "C:\\Windows\\" + }, + "absent": [ + "origin.host", + "origin.user", + "origin.process" + ], + "matches": [] + }, + { + "name": "outbound firewall physical endpoints", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":\"TCP\",\"inbound\":false,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "origin.ip": "192.0.2.30", + "target.ip": "198.51.100.10", + "origin.host": "endpoint.example.invalid", + "origin.user": "LAB\\svc", + "origin.process": "service.exe", + "origin.path": "C:\\Windows\\" + }, + "absent": [ + "target.host", + "target.user", + "target.process" + ], + "matches": [] + }, + { + "name": "firewall unknown direction retains reporting identity", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.hostname": "endpoint.example.invalid", + "log.ipv4": "192.0.2.30", + "origin.ip": "192.0.2.30", + "target.ip": "198.51.100.10" + }, + "absent": [ + "target.host", + "origin.host", + "target.user", + "origin.user" + ], + "matches": [] + }, + { + "name": "port type 443", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":443,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "origin.port": 443 + }, + "absent": [], + "matches": [] + }, + { + "name": "port type '443'", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":\"443\",\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "origin.port": 443 + }, + "absent": [], + "matches": [] + }, + { + "name": "port type 0", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":0,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": {}, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type 65535", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":65535,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "origin.port": 65535 + }, + "absent": [], + "matches": [] + }, + { + "name": "port type 1.25", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":1.25,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": 1.25 + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type -1", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":-1,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": -1 + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type 65536", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":65536,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": 65536 + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type '1e2'", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":\"1e2\",\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": "1e2" + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type '443.0'", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":\"443.0\",\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": "443.0" + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type 'bad'", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":\"bad\",\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": "bad" + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":false,\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": false + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type {}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":{},\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": {} + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "port type [443]", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":[443],\"target_port\":443,\"protocol\":\"TCP\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "log.sourceport": [ + 443 + ] + }, + "absent": [ + "origin.port" + ], + "matches": [] + }, + { + "name": "numeric protocol 1", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":1,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "ICMP" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 6", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":\"6\",\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "TCP" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 17", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":17,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "UDP" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 47", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":47,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "GRE" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 50", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":50,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "ESP" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 51", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":51,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "AH" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 58", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":58,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "ICMPV6" + }, + "absent": [], + "matches": [] + }, + { + "name": "numeric protocol 132", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FirewallAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"source_address\":\"192.0.2.30\",\"target_address\":\"198.51.100.10\",\"source_port\":49152,\"target_port\":443,\"protocol\":132,\"action\":\"blocked\",\"account\":\"LAB\\\\svc\",\"process_name\":\"C:\\\\Windows\\\\service.exe\"}", + "expected": { + "protocol": "SCTP" + }, + "absent": [], + "matches": [] + }, + { + "name": "filtered website client and remote sides", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FilteredWebsites_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"target_address\":\"198.51.100.20\",\"object_uri\":\"https://malware.example.invalid/download\",\"hash\":\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\",\"action_taken\":\"blocked\",\"username\":\"LAB\\\\user\",\"processname\":\"C:\\\\Browser\\\\browser.exe\"}", + "expected": { + "origin.ip": "192.0.2.30", + "origin.host": "endpoint.example.invalid", + "origin.user": "LAB\\user", + "origin.process": "browser.exe", + "target.ip": "198.51.100.20", + "target.url": "https://malware.example.invalid/download", + "target.sha1": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + "actionResult": "denied", + "origin.path": "C:\\Browser\\" + }, + "absent": [ + "target.host" + ], + "matches": [] + }, + { + "name": "HIPS actor and observed host", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"HipsAggregated_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"application\":\"C:\\\\Program Files\\\\Example\\\\actor.exe\",\"target\":\"C:\\\\Example\\\\object.exe\",\"action\":\"blocked\",\"handled\":true}", + "expected": { + "target.host": "endpoint.example.invalid", + "origin.process": "actor.exe", + "actionResult": "denied", + "origin.path": "C:\\Program Files\\Example\\" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "Inspect hash describes alarm not file", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"EnterpriseInspectorAlert_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"hash\":\"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\",\"processname\":\"suspicious.exe\"}", + "expected": { + "log.hash": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "origin.process": "suspicious.exe" + }, + "absent": [ + "target.sha1", + "actionResult" + ], + "matches": [] + }, + { + "name": "Audit success explicit result", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Audit_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"domain\":\"Native user\",\"action\":\"Login attempt\",\"target\":\"Administrator\",\"user\":\"\",\"result\":\"Success\"}", + "expected": { + "target.user": "Administrator", + "actionResult": "success" + }, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "Audit failed explicit result", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Audit_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"domain\":\"Native user\",\"action\":\"Login attempt\",\"target\":\"Administrator\",\"user\":\"operator\",\"result\":\"Failure\"}", + "expected": { + "target.user": "Administrator", + "origin.user": "operator", + "actionResult": "failure" + }, + "absent": [], + "matches": [ + "eset_console_abuse" + ] + }, + { + "name": "Audit nonlogin target is not user", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Audit_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"domain\":\"Policy\",\"action\":\"Modified\",\"target\":\"Policy A\",\"result\":\"Success\"}", + "expected": { + "actionResult": "success" + }, + "absent": [ + "target.user" + ], + "matches": [] + }, + { + "name": "vendor severity Information", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Information\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Information", + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "vendor severity Notice", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Notice\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Notice", + "severity": "info" + }, + "absent": [], + "matches": [] + }, + { + "name": "vendor severity Warning", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Warning", + "severity": "warning" + }, + "absent": [], + "matches": [] + }, + { + "name": "vendor severity Error", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Error\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Error", + "severity": "error" + }, + "absent": [], + "matches": [] + }, + { + "name": "vendor severity Critical", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Critical\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Critical", + "severity": "critical" + }, + "absent": [], + "matches": [] + }, + { + "name": "vendor severity Fatal", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Fatal\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "Fatal", + "severity": "critical" + }, + "absent": [], + "matches": [] + }, + { + "name": "unknown severity retained", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"FutureValue\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.severity": "FutureValue" + }, + "absent": [ + "severity" + ], + "matches": [] + }, + { + "name": "invalid standard strings True", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":true,\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":true,\"occurred\":\"17-Sep-2026 12:30:00\",\"username\":true,\"processname\":true,\"action_taken\":true}", + "expected": { + "log.hostname": true + }, + "absent": [ + "target.host", + "origin.user", + "origin.process", + "action", + "severity" + ], + "matches": [] + }, + { + "name": "invalid standard strings 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":123,\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":123,\"occurred\":\"17-Sep-2026 12:30:00\",\"username\":123,\"processname\":123,\"action_taken\":123}", + "expected": { + "log.hostname": 123 + }, + "absent": [ + "target.host", + "origin.user", + "origin.process", + "action", + "severity" + ], + "matches": [] + }, + { + "name": "invalid standard strings []", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":[],\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":[],\"occurred\":\"17-Sep-2026 12:30:00\",\"username\":[],\"processname\":[],\"action_taken\":[]}", + "expected": { + "log.hostname": [] + }, + "absent": [ + "target.host", + "origin.user", + "origin.process", + "action", + "severity" + ], + "matches": [] + }, + { + "name": "invalid standard strings {}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":{},\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":{},\"occurred\":\"17-Sep-2026 12:30:00\",\"username\":{},\"processname\":{},\"action_taken\":{}}", + "expected": { + "log.hostname": {} + }, + "absent": [ + "target.host", + "origin.user", + "origin.process", + "action", + "severity" + ], + "matches": [] + }, + { + "name": "UTC timestamp occurred", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"29-Feb-2024 23:59:59\"}", + "expected": { + "deviceTime": "2024-02-29T23:59:59Z" + }, + "absent": [], + "matches": [] + }, + { + "name": "UTC timestamp occured", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occured\":\"29-Feb-2024 23:59:59\"}", + "expected": { + "deviceTime": "2024-02-29T23:59:59Z" + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid calendar time 31-Apr-2026 12:00:00", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"31-Apr-2026 12:00:00\"}", + "expected": { + "log.occurred": "31-Apr-2026 12:00:00" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid calendar time 29-Feb-2025 12:00:00", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"29-Feb-2025 12:00:00\"}", + "expected": { + "log.occurred": "29-Feb-2025 12:00:00" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid calendar time 29-Feb-1900 12:00:00", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"29-Feb-1900 12:00:00\"}", + "expected": { + "log.occurred": "29-Feb-1900 12:00:00" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid calendar time 17-Sep-2026 25:00:00", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 25:00:00\"}", + "expected": { + "log.occurred": "17-Sep-2026 25:00:00" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid calendar time bad", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"bad\"}", + "expected": { + "log.occurred": "bad" + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "invalid calendar time 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":123}", + "expected": { + "log.occurred": 123 + }, + "absent": [ + "deviceTime" + ], + "matches": [] + }, + { + "name": "endpoint identity fallback endpoint.example.invalid", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "endpoint.example.invalid" + }, + "absent": [], + "matches": [] + }, + { + "name": "endpoint identity fallback 192.0.2.30", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "ip", + "log.endpointKey": "192.0.2.30" + }, + "absent": [], + "matches": [] + }, + { + "name": "endpoint identity fallback 2001:db8::30", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"ipv6\":\"2001:db8::30\"}", + "expected": { + "log.endpointKeyType": "ip", + "log.endpointKey": "2001:db8::30" + }, + "absent": [], + "matches": [] + }, + { + "name": "missing endpoint identity not relay fallback", + "dataSource": "synthetic-eset-collector", + "raw": "Sep 17 12:30:05 manager.example.invalid ERAServer[5648]: {\"event_type\":\"Threat_Event\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "log.endpointKey", + "log.endpointKeyType", + "target.host" + ], + "matches": [] + }, + { + "name": "class alone never success Threat_Event", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"handled\":true,\"threat_handled\":true,\"result\":\"Success\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "class alone never success BlockedFiles_Event", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"BlockedFiles_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"handled\":true,\"threat_handled\":true,\"result\":\"Success\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "class alone never success FilteredWebsites_Event", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"FilteredWebsites_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"handled\":true,\"threat_handled\":true,\"result\":\"Success\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "class alone never success EnterpriseInspectorAlert_Event", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"EnterpriseInspectorAlert_Event\",\"ipv4\":\"192.0.2.30\",\"hostname\":\"endpoint.example.invalid\",\"source_uuid\":\"synthetic-endpoint-1\",\"os_name\":\"Microsoft Windows 11 Pro\",\"severity\":\"Warning\",\"occurred\":\"17-Sep-2026 12:30:00\",\"handled\":true,\"threat_handled\":true,\"result\":\"Success\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "malformed JSON has explicit model error", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\":\"Threat_Event\",\"ipv4\":}", + "expected": {}, + "absent": [], + "parseError": true, + "matches": [] + }, + { + "name": "preexisting standard deviceTime is preserved", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": { + "deviceTime": "2026-09-17T12:00:00Z" + }, + "absent": [ + "log.esetOccurredTime" + ], + "ingress": { + "deviceTime": "2026-09-17T12:00:00Z" + }, + "matches": [] + }, + { + "name": "preexisting standard time with no vendor time", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\"}", + "expected": { + "deviceTime": "2026-09-17T12:00:00Z" + }, + "absent": [ + "log.esetOccurredTime" + ], + "ingress": { + "deviceTime": "2026-09-17T12:00:00Z" + }, + "matches": [] + }, + { + "name": "Threat standard malware fields", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"threat_name\": \"Synthetic.Filecoder.Test\", \"threat_type\": \"Trojan\"}", + "expected": { + "target.malware": "Synthetic.Filecoder.Test", + "target.malwareType": "Trojan" + }, + "absent": [ + "target.malwareFamily" + ], + "matches": [ + "suspicious_encrypted_file_detection" + ] + }, + { + "name": "Firewall bounded vulnerability identifier", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"threat_name\": \"CVE-2017-5638.Struts2\"}", + "expected": { + "target.cve": "CVE-2017-5638" + }, + "absent": [ + "target.malware" + ], + "matches": [] + }, + { + "name": "Firewall unrelated threat name not vulnerability", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"threat_name\": \"Network attempt\"}", + "expected": {}, + "absent": [ + "target.cve", + "target.malware" + ], + "matches": [] + }, + { + "name": "HIPS absolute filesystem C:\\Example\\object.exe", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"target\": \"C:\\\\Example\\\\object.exe\"}", + "expected": { + "target.path": "C:\\Example\\", + "target.filename": "object.exe" + }, + "absent": [], + "matches": [] + }, + { + "name": "HIPS absolute filesystem /opt/example/object", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"target\": \"/opt/example/object\"}", + "expected": { + "target.path": "/opt/example/", + "target.filename": "object" + }, + "absent": [], + "matches": [] + }, + { + "name": "Threat absolute file URI file:///C:/Example/object.exe", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"object_uri\": \"file:///C:/Example/object.exe\", \"object_type\": \"file\"}", + "expected": { + "target.path": "C:/Example/", + "target.filename": "object.exe" + }, + "absent": [], + "matches": [] + }, + { + "name": "Threat absolute file URI file:///opt/example/object", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"object_uri\": \"file:///opt/example/object\", \"object_type\": \"file\"}", + "expected": { + "target.path": "/opt/example/", + "target.filename": "object" + }, + "absent": [], + "matches": [] + }, + { + "name": "nonfilesystem or encoded object remains vendor HKLM\\Software\\Example", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"target\": \"HKLM\\\\Software\\\\Example\"}", + "expected": {}, + "absent": [ + "target.path", + "target.filename" + ], + "matches": [] + }, + { + "name": "nonfilesystem or encoded object remains vendor file:///C:/Example/encoded%20name.exe", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"object_uri\": \"file:///C:/Example/encoded%20name.exe\"}", + "expected": {}, + "absent": [ + "target.path", + "target.filename" + ], + "matches": [] + }, + { + "name": "nonfilesystem or encoded object remains vendor relative/path.exe", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"object_uri\": \"relative/path.exe\"}", + "expected": {}, + "absent": [ + "target.path", + "target.filename" + ], + "matches": [] + }, + { + "name": "heuristic remediation positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.NewHeur\", \"action_taken\": \"Deleted\", \"threat_handled\": true}", + "expected": { + "log.correlationCandidate.heuristicRemediation": "match" + }, + "absent": [ + "actionResult" + ], + "matches": [ + "advanced_heuristic_detection_triggers" + ] + }, + { + "name": "heuristic remediation action error negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.NewHeur\", \"action_taken\": \"Deleted\", \"action_error\": \"Permission denied\"}", + "expected": {}, + "absent": [ + "log.correlationCandidate.heuristicRemediation" + ], + "matches": [] + }, + { + "name": "heuristic unhandled negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.NewHeur\", \"action_taken\": \"Deleted\", \"threat_handled\": false}", + "expected": {}, + "absent": [ + "log.correlationCandidate.heuristicRemediation" + ], + "matches": [] + }, + { + "name": "heuristic name in object filename negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"object_uri\": \"file:///C:/NewHeur.doc\", \"action_taken\": \"Deleted\"}", + "expected": {}, + "absent": [ + "log.correlationCandidate.heuristicRemediation" + ], + "matches": [] + }, + { + "name": "botnet outbound positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"inbound\": false, \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\"}", + "expected": { + "origin.ip": "192.0.2.30", + "target.ip": "198.51.100.40" + }, + "absent": [ + "actionResult" + ], + "matches": [ + "botnet_communication_attempts" + ] + }, + { + "name": "botnet inbound positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"inbound\": true, \"source_address\": \"198.51.100.40\", \"target_address\": \"192.0.2.30\"}", + "expected": { + "origin.ip": "198.51.100.40", + "target.ip": "192.0.2.30" + }, + "absent": [ + "actionResult" + ], + "matches": [ + "botnet_inbound_communication_attempts" + ] + }, + { + "name": "botnet unknown direction false", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\", \"inbound\": \"false\"}", + "expected": {}, + "absent": [ + "origin.host", + "target.host" + ], + "matches": [] + }, + { + "name": "botnet unknown direction true", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\", \"inbound\": \"true\"}", + "expected": {}, + "absent": [ + "origin.host", + "target.host" + ], + "matches": [] + }, + { + "name": "botnet unknown direction 1", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\", \"inbound\": 1}", + "expected": {}, + "absent": [ + "origin.host", + "target.host" + ], + "matches": [] + }, + { + "name": "botnet unknown direction {}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\", \"inbound\": {}}", + "expected": {}, + "absent": [ + "origin.host", + "target.host" + ], + "matches": [] + }, + { + "name": "botnet unknown direction None", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Botnet.CnC.Generic\", \"source_address\": \"192.0.2.30\", \"target_address\": \"198.51.100.40\"}", + "expected": {}, + "absent": [ + "origin.host", + "target.host" + ], + "matches": [] + }, + { + "name": "agent tamper Inspect positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"EnterpriseInspectorAlert_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"rulename\": \"ESET agent tamper protection\"}", + "expected": {}, + "absent": [], + "matches": [ + "eset_agent_tampering" + ] + }, + { + "name": "agent stopped message alone negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Audit_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"detail\": \"ESET agent stopped for maintenance\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "quarantine failure positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"action_taken\": \"Quarantine\", \"action_error\": \"Unable to quarantine file\"}", + "expected": { + "actionResult": "failure", + "log.correlationCandidate.quarantineFailure": "match" + }, + "absent": [], + "matches": [ + "eset_quarantine_failures" + ] + }, + { + "name": "quarantine benign metadata negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"object_uri\": \"file:///C:/quarantine/failed.doc\", \"action_taken\": \"Deleted\"}", + "expected": {}, + "absent": [ + "log.correlationCandidate.quarantineFailure" + ], + "matches": [] + }, + { + "name": "exploit explicit blocked positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Critical\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.Exploit.Test\", \"action_taken\": \"Blocked\"}", + "expected": {}, + "absent": [], + "matches": [ + "exploit_detection_events" + ] + }, + { + "name": "exploit detection only negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.Exploit.Test\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "machine learning positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"scanner_id\": \"Machine learning\"}", + "expected": {}, + "absent": [], + "matches": [ + "machine_learning_detection_anomalies" + ] + }, + { + "name": "machine learning filename negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"object_uri\": \"file:///C:/machine learning threat.doc\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "network attack blocked positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"event\": \"Security vulnerability exploitation attempt\", \"threat_name\": \"CVE-2017-5638.Struts2\", \"inbound\": true, \"source_address\": \"198.51.100.40\", \"target_address\": \"192.0.2.30\", \"action\": \"blocked\"}", + "expected": { + "target.cve": "CVE-2017-5638" + }, + "absent": [], + "matches": [ + "network_attack_detection", + "exploit_detection_events" + ] + }, + { + "name": "network attack observed only negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"FirewallAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"event\": \"Security vulnerability exploitation attempt\", \"inbound\": true, \"source_address\": \"198.51.100.40\", \"target_address\": \"192.0.2.30\"}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "registry HIPS blocked positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"operation\": \"Registry modification\", \"target\": \"HKLM\\\\Software\\\\Synthetic\", \"action\": \"blocked\"}", + "expected": {}, + "absent": [], + "matches": [ + "registry_modification_attempts_blocked" + ] + }, + { + "name": "registry benign audit negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Audit_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Critical\", \"occurred\": \"17-Sep-2026 12:30:00\", \"action\": \"blocked\", \"detail\": \"registry change report\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "HIPS information severity negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Information\", \"occurred\": \"17-Sep-2026 12:30:00\", \"operation\": \"File operation\", \"action\": \"blocked\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "ransomware detection positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"threat_name\": \"Synthetic.Filecoder.Test\"}", + "expected": {}, + "absent": [], + "matches": [ + "suspicious_encrypted_file_detection" + ] + }, + { + "name": "encrypted filename alone negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"object_uri\": \"file:///C:/archive.encrypted\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "PowerShell blocked positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"processname\": \"C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\", \"action_taken\": \"blocked\"}", + "expected": {}, + "absent": [], + "matches": [ + "suspicious_powershell_activity_blocked" + ] + }, + { + "name": "PowerShell only message negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"circumstances\": \"powershell blocked example\", \"action_taken\": \"blocked\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "suspicious process Inspect positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"EnterpriseInspectorAlert_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"rulename\": \"Process injection detected\"}", + "expected": {}, + "absent": [], + "matches": [ + "suspicious_process_behavior" + ] + }, + { + "name": "suspicious process benign path negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"EnterpriseInspectorAlert_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"processname\": \"C:\\\\Process injection\\\\example.exe\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "routine successful console policy edit negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Audit_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"domain\": \"Policy\", \"action\": \"Modified\", \"detail\": \"Policy modified by administrator\", \"result\": \"Success\"}", + "expected": {}, + "absent": [ + "log.correlationCandidate.consoleAuthenticationFailure" + ], + "matches": [] + }, + { + "name": "invalid UUID falls back to hostname 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": 123, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "endpoint.example.invalid", + "log.sourceuuid": 123 + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid UUID and hostname cannot create identity 123", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": 123, \"source_uuid\": 123, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "log.endpointKey", + "log.endpointKeyType", + "target.host" + ], + "matches": [] + }, + { + "name": "invalid UUID falls back to hostname {}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": {}, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "endpoint.example.invalid", + "log.sourceuuid": {} + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid UUID and hostname cannot create identity {}", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": {}, \"source_uuid\": {}, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "log.endpointKey", + "log.endpointKeyType", + "target.host" + ], + "matches": [] + }, + { + "name": "invalid UUID falls back to hostname []", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": [], \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "endpoint.example.invalid", + "log.sourceuuid": [] + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid UUID and hostname cannot create identity []", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": [], \"source_uuid\": [], \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "log.endpointKey", + "log.endpointKeyType", + "target.host" + ], + "matches": [] + }, + { + "name": "invalid UUID falls back to hostname False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": false, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": { + "log.endpointKeyType": "host", + "log.endpointKey": "endpoint.example.invalid", + "log.sourceuuid": false + }, + "absent": [], + "matches": [] + }, + { + "name": "invalid UUID and hostname cannot create identity False", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": false, \"source_uuid\": false, \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\"}", + "expected": {}, + "absent": [ + "log.endpointKey", + "log.endpointKeyType", + "target.host" + ], + "matches": [] + }, + { + "name": "HIPS suspicious object blocked positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"severity\": \"Critical\", \"action\": \"blocked\", \"operation\": \"Attempt to run a suspicious object\"}", + "expected": {}, + "absent": [], + "matches": [ + "host_intrusion_prevention_triggers", + "suspicious_process_behavior" + ] + }, + { + "name": "HIPS ordinary policy block negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"HipsAggregated_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"severity\": \"Critical\", \"action\": \"blocked\", \"operation\": \"File operation\", \"rule_name\": \"Ordinary file policy\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "unrelated backup agent disabled Inspect negative", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"EnterpriseInspectorAlert_Event\", \"hostname\": \"endpoint.example.invalid\", \"ipv4\": \"192.0.2.30\", \"source_uuid\": \"synthetic-endpoint-1\", \"severity\": \"Critical\", \"action\": \"blocked\", \"rulename\": \"backup agent disabled\"}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "file URI suffix stays vendor file:///tmp/sample.exe?query=1", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"object_uri\": \"file:///tmp/sample.exe?query=1\"}", + "expected": { + "log.objecturi": "file:///tmp/sample.exe?query=1" + }, + "absent": [ + "target.path", + "target.filename" + ], + "matches": [] + }, + { + "name": "file URI suffix stays vendor file:///tmp/sample.exe#fragment", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"object_uri\": \"file:///tmp/sample.exe#fragment\"}", + "expected": { + "log.objecturi": "file:///tmp/sample.exe#fragment" + }, + "absent": [ + "target.path", + "target.filename" + ], + "matches": [] + }, + { + "name": "absolute Unix process directory", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Threat_Event\", \"hostname\": \"endpoint.example.invalid\", \"processname\": \"/usr/local/bin/worker\"}", + "expected": { + "origin.process": "worker", + "origin.path": "/usr/local/bin/" + }, + "absent": [], + "matches": [] + }, + { + "name": "Audit Denied authentication positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Audit_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"domain\": \"Native user\", \"action\": \"Login attempt\", \"target\": \"Administrator\", \"user\": \"operator\", \"result\": \"Denied\"}", + "expected": { + "target.user": "Administrator", + "origin.user": "operator", + "actionResult": "denied", + "log.correlationCandidate.consoleAuthenticationFailure": "match" + }, + "absent": [], + "matches": [ + "eset_console_abuse" + ] + }, + { + "name": "Audit Rejected authentication positive", + "dataSource": "synthetic-eset-collector", + "raw": "{\"event_type\": \"Audit_Event\", \"ipv4\": \"192.0.2.30\", \"hostname\": \"endpoint.example.invalid\", \"source_uuid\": \"synthetic-endpoint-1\", \"os_name\": \"Microsoft Windows 11 Pro\", \"severity\": \"Warning\", \"occurred\": \"17-Sep-2026 12:30:00\", \"domain\": \"Native user\", \"action\": \"Login attempt\", \"target\": \"Administrator\", \"user\": \"operator\", \"result\": \"Rejected\"}", + "expected": { + "target.user": "Administrator", + "origin.user": "operator", + "actionResult": "denied", + "log.correlationCandidate.consoleAuthenticationFailure": "match" + }, + "absent": [], + "matches": [ + "eset_console_abuse" + ] + } +] diff --git a/plugins/alerts/testdata/filter-contracts/eset.json b/plugins/alerts/testdata/filter-contracts/eset.json index d8042d96f..7e7f9a3b1 100644 --- a/plugins/alerts/testdata/filter-contracts/eset.json +++ b/plugins/alerts/testdata/filter-contracts/eset.json @@ -6,6 +6,7 @@ "rules": [ "rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml", "rules/antivirus/esmc-eset/botnet_communication_attempts.yml", + "rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml", "rules/antivirus/esmc-eset/eset_agent_tampering.yml", "rules/antivirus/esmc-eset/eset_console_abuse.yml", "rules/antivirus/esmc-eset/eset_quarantine_failures.yml", @@ -20,36 +21,78 @@ ], "fixtures": [ { - "name": "ESET event name is not an outcome", + "name": "ESET isolated empty-input normalization control (raw extraction tested separately)", "filter": "antivirus/esmc-eset.yml", "input": { - "log": { - "event": "HIPS_Event" - } + "dataType": "antivirus-esmc-eset", + "dataSource": "synthetic-relay", + "log": {} }, "expected": { - "log.event": "HIPS_Event" + "dataSource": "synthetic-relay" }, "absent": [ - "actionResult" + "origin.ip", + "target.ip", + "actionResult", + "log.endpointKey" ], - "rules": {} + "rules": { + "rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml": false, + "rules/antivirus/esmc-eset/botnet_communication_attempts.yml": false, + "rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml": false, + "rules/antivirus/esmc-eset/eset_agent_tampering.yml": false, + "rules/antivirus/esmc-eset/eset_console_abuse.yml": false, + "rules/antivirus/esmc-eset/eset_quarantine_failures.yml": false, + "rules/antivirus/esmc-eset/exploit_detection_events.yml": false, + "rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml": false, + "rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml": false, + "rules/antivirus/esmc-eset/network_attack_detection.yml": false, + "rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml": false, + "rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml": false, + "rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml": false, + "rules/antivirus/esmc-eset/suspicious_process_behavior.yml": false + } }, { - "name": "ESET actual blocked result", + "name": "ESET isolated unknown-class outcome control (does not prove class extraction)", "filter": "antivirus/esmc-eset.yml", "input": { + "dataType": "antivirus-esmc-eset", + "dataSource": "synthetic-relay", "log": { - "event": "HIPS_Event", - "result": "Blocked" + "eventtype": "Unverified_Event", + "result": "Success", + "severity": "Warning" } }, "expected": { - "actionResult": "denied", - "log.event": "HIPS_Event" + "log.eventtype": "Unverified_Event", + "log.result": "Success", + "log.severity": "Warning" }, - "absent": [], - "rules": {} + "absent": [ + "actionResult", + "origin.ip", + "target.ip", + "log.endpointKey" + ], + "rules": { + "rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml": false, + "rules/antivirus/esmc-eset/botnet_communication_attempts.yml": false, + "rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml": false, + "rules/antivirus/esmc-eset/eset_agent_tampering.yml": false, + "rules/antivirus/esmc-eset/eset_console_abuse.yml": false, + "rules/antivirus/esmc-eset/eset_quarantine_failures.yml": false, + "rules/antivirus/esmc-eset/exploit_detection_events.yml": false, + "rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml": false, + "rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml": false, + "rules/antivirus/esmc-eset/network_attack_detection.yml": false, + "rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml": false, + "rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml": false, + "rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml": false, + "rules/antivirus/esmc-eset/suspicious_process_behavior.yml": false + } } ] } diff --git a/rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml b/rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml index 38fdc2c67..750f97e7d 100644 --- a/rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml +++ b/rules/antivirus/esmc-eset/advanced_heuristic_detection_triggers.yml @@ -11,11 +11,11 @@ category: Defense Evasion, Privilege Escalation technique: "T1055 - Process Injection" adversary: origin references: - - https://help.eset.com/eea/8/en-US/idh_config_threat_sense.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1055/ description: | - Detects when ESET's advanced heuristic engine identifies suspicious behavior patterns that may indicate novel malware or zero-day threats. These detections use DNA signatures and behavioral analysis. - + Detects three antivirus heuristic detections in thirty minutes on the same managed endpoint when ESET reports an explicit cleaned, deleted, quarantined, or blocked action without an action error. Detection labels are matched in structured threat or scanner fields; remediation does not establish a successful network connection. + Next Steps: - Review the affected hostname and user context to understand the scope - Check the process name (if available) that triggered the detection @@ -29,17 +29,33 @@ description: | - Review system logs for any unusual activities before and after detection - Update ESET signatures and run a full system scan where: | - oneOf("log.msgType", ["EnterpriseInspectorAlert_Event", "threat_event", "FirewallAggregatedAlert_Event"]) && - contains("log.jsonMessage", ["heuristic", "NewHeur", "suspicious behavior"]) && - contains("log.jsonMessage", ["cleaned", "deleted", "quarantined", "blocked"]) + (equals("log.eventType", "Threat_Event") && + (regexMatch("log.threatname", "(?i)(heuristic|newheur|suspicious behavior)") || regexMatch("log.threattype", "(?i)(heuristic|newheur|suspicious behavior)") || regexMatch("log.scannerid", "(?i)(heuristic|newheur|suspicious behavior)")) && + regexMatch("action", "(?i)^(cleaned|deleted|quarantined|blocked|denied|prevented)$") && + (!exists("log.actionerror") || equals("log.actionerror", "")) && + !equals("log.threathandled", false)) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") afterEvents: - indexPattern: v11-log-antivirus-esmc-eset-* with: - - field: log.headHostname + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.heuristicRemediation operator: filter_term - value: '{{.log.headHostname}}' + value: 'match' within: 30m count: 3 groupBy: - - lastEvent.log.headHostname - - lastEvent.log.msgType + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.eventType diff --git a/rules/antivirus/esmc-eset/botnet_communication_attempts.yml b/rules/antivirus/esmc-eset/botnet_communication_attempts.yml index e6c543438..cf71d9782 100644 --- a/rules/antivirus/esmc-eset/botnet_communication_attempts.yml +++ b/rules/antivirus/esmc-eset/botnet_communication_attempts.yml @@ -2,34 +2,40 @@ dataTypes: - antivirus-esmc-eset -name: ESET Botnet Communication Detection +name: ESET Outbound Botnet Communication Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Command and Control technique: "T1071 - Application Layer Protocol" -adversary: origin +adversary: target references: - - https://www.eset.com/us/botnet/ - - https://support.eset.com/en/kb7487-resolve-the-incomingattackgeneric-or-botnetcncgeneric-network-protection-alert + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1071/ description: | - Detects attempts to communicate with known botnet command and control servers. ESET identifies typical communication patterns when a computer is infected and a bot is attempting to communicate with malicious C2 infrastructure. - + Detects an outbound firewall detection whose event or detection name identifies botnet command-and-control activity. The managed endpoint is the network source and the remote destination is the alert adversary. A blocked attempt does not establish an active infection or successful communication; review the reported action and endpoint evidence. Records without a known direction cannot select a remote adversary side. + Next Steps: - 1. Immediately isolate the affected system from the network to prevent further C2 communication - 2. Check the hostname (log.headHostname) to identify the affected system - 3. Review the full log message content (log.jsonMessage) for additional threat details including target IPs and processes - 4. Review process activity on the affected host to identify the malicious process - 5. Scan the system with ESET for complete malware removal + 1. Review the managed endpoint and the reported outcome before choosing containment + 2. Use the managed endpoint identity and network origin to identify the affected system + 3. Review the structured event, detection name, endpoint addresses, and process fields; use the protected raw event for additional context + 4. Investigate relevant process activity on the affected host + 5. Scan the system with ESET and assess any detections 6. Check other systems in the network for similar C2 communication attempts 7. Update firewall rules to block any identified C2 server IPs found in the logs 8. Consider reimaging the system if the infection persists 9. Review ESET logs for the time period around this detection to identify related malicious activity where: | - contains("log.jsonMessage", ["Botnet", "CnC.Generic", "botnet", "C&C", "command and control"]) && - exists("log.headHostname") + (equals("log.eventType", "FirewallAggregated_Event") && + (regexMatch("log.threatname", "(?i)(botnet|cnc[.]generic|c&c|command and control)") || regexMatch("log.event", "(?i)(botnet|cnc[.]generic|c&c|command and control)")) && + equals("log.inbound", false) && exists("target.ip")) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.headHostname - - lastEvent.log.jsonMessage + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - adversary.ip + - lastEvent.log.threatname diff --git a/rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml b/rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml new file mode 100644 index 000000000..4c71ed729 --- /dev/null +++ b/rules/antivirus/esmc-eset/botnet_inbound_communication_attempts.yml @@ -0,0 +1,41 @@ +# Rule version v1.0.0 + +dataTypes: + - antivirus-esmc-eset +name: ESET Inbound Botnet Communication Detection +impact: + confidentiality: 3 + integrity: 3 + availability: 2 +category: Command and Control +technique: "T1071 - Application Layer Protocol" +adversary: origin +references: + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html + - https://attack.mitre.org/techniques/T1071/ +description: | + Detects an inbound firewall detection whose event or detection name identifies botnet command-and-control activity. The managed endpoint is the network destination and the remote source is the alert adversary. A blocked attempt does not establish an active infection or successful communication; review the reported action and endpoint evidence. Records without a known direction cannot select a remote adversary side. + + Next Steps: + 1. Review the managed endpoint and the reported outcome before choosing containment + 2. Use the managed endpoint identity and network destination to identify the affected system + 3. Review the structured event, detection name, endpoint addresses, and process fields; use the protected raw event for additional context + 4. Investigate relevant process activity on the affected host + 5. Scan the system with ESET and assess any detections + 6. Check other systems in the network for similar C2 communication attempts + 7. Update firewall rules to block any identified C2 server IPs found in the logs + 8. Consider reimaging the system if the infection persists + 9. Review ESET logs for the time period around this detection to identify related malicious activity +where: | + (equals("log.eventType", "FirewallAggregated_Event") && + (regexMatch("log.threatname", "(?i)(botnet|cnc[.]generic|c&c|command and control)") || regexMatch("log.event", "(?i)(botnet|cnc[.]generic|c&c|command and control)")) && + equals("log.inbound", true) && exists("origin.ip")) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") +groupBy: + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - adversary.ip + - lastEvent.log.threatname diff --git a/rules/antivirus/esmc-eset/eset_agent_tampering.yml b/rules/antivirus/esmc-eset/eset_agent_tampering.yml index 9110bd8ca..60a127610 100644 --- a/rules/antivirus/esmc-eset/eset_agent_tampering.yml +++ b/rules/antivirus/esmc-eset/eset_agent_tampering.yml @@ -2,7 +2,7 @@ dataTypes: - antivirus-esmc-eset -name: ESET Agent Disabled or Tampered +name: ESET Security Agent Tampering Detection impact: confidentiality: 3 integrity: 3 @@ -11,24 +11,28 @@ category: Defense Evasion technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" adversary: origin references: - - https://help.eset.com/ees/8/en-US/idh_config_era_agent.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1562/001/ description: | - Detects when the ESET security agent is disabled, uninstalled, or tampered with. This is a critical defense evasion indicator as attackers commonly disable endpoint protection before executing their payload. + Detects a blocked HIPS self-defense or agent-tampering rule, or an ESET Inspect alert explicitly naming security-agent tampering. A blocked attempt does not establish that the agent was disabled. Routine audit policy changes and generic agent status text are not tampering evidence. Next Steps: 1. Immediately investigate the affected endpoint - 2. Determine who or what process disabled the agent + 2. Verify whether protection was disabled and identify the responsible process or account 3. Check for concurrent malicious activity on the endpoint - 4. Reinstall and re-enable the ESET agent + 4. Restore or reinstall ESET protection only if it was affected 5. Review the endpoint for malware or unauthorized software 6. Check if similar tampering occurred on other endpoints where: | - (regexMatch("log.message", "(?i)(eset|ekrn|egui|agent)") && - regexMatch("log.message", "(?i)(disabled|stopped|uninstalled|removed|tampered|terminated)")) || - (contains("log.message", "protection status") && contains("log.message", "disabled")) || - (contains("log.message", "agent") && contains("log.message", "not responding")) || - (equals("log.eventType", "AGENT_EVENT") && contains("log.message", "removed")) + (((oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) && equals("actionResult", "denied")) || equals("log.eventType", "EnterpriseInspectorAlert_Event")) && + regexMatch("log.rulename", "(?i)(eset|ekrn|egui|self[- ]defen[sc]e)") && + regexMatch("log.rulename", "(?i)(tamper|self[- ]defen[sc]e|disable|stopp?ed|terminat)")) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.eventType - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.rulename + - lastEvent.log.ruleid diff --git a/rules/antivirus/esmc-eset/eset_console_abuse.yml b/rules/antivirus/esmc-eset/eset_console_abuse.yml index 102896e41..46171cf35 100644 --- a/rules/antivirus/esmc-eset/eset_console_abuse.yml +++ b/rules/antivirus/esmc-eset/eset_console_abuse.yml @@ -2,44 +2,56 @@ dataTypes: - antivirus-esmc-eset -name: ESET ERA/ESMC Console Suspicious Activity +name: ESET Repeated Console Authentication Failures impact: confidentiality: 3 integrity: 3 availability: 2 -category: Lateral Movement -technique: "T1072 - Software Deployment Tools" +category: Credential Access +technique: "T1110 - Brute Force" adversary: origin references: - - https://help.eset.com/esmc_admin/70/en-US/ - - https://attack.mitre.org/techniques/T1072/ + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html + - https://attack.mitre.org/techniques/T1110/ description: | - Detects suspicious activity on the ESET ERA/ESMC management console including unauthorized policy changes, mass task deployments, or admin account modifications that could indicate console compromise. + Detects ten failed or explicitly denied native-user console login attempts in thirty minutes for the same managed console and attempted account. The account comes from the target of the structured Native user / Login attempt audit record. Successful policy changes, task deployments, and account administration do not by themselves establish console abuse. Next Steps: - 1. Verify the admin account performing console operations - 2. Review recent policy changes and task deployments - 3. Check admin login history for unauthorized access - 4. Verify the content of any pushed policies or tasks - 5. Suspend suspicious admin accounts - 6. Audit endpoints affected by recent console changes + 1. Identify the attempted account and confirm whether its owner initiated the logins + 2. Check for stale credentials in scripts, services, or automation + 3. Review successful console logins around the failures and investigate unfamiliar access + 4. Review console changes if account compromise is suspected + 5. Reset credentials or restrict the account if unauthorized activity is confirmed where: | - (contains("log.message", "policy") && - (contains("log.message", "modified") || contains("log.message", "assigned") || - contains("log.message", "created"))) || - (contains("log.message", "client task") && - (contains("log.message", "executed") || contains("log.message", "deployed"))) || - (contains("log.message", "administrator") && - ((contains("log.message", "created") || contains("log.message", "modified")) || - (contains("log.message", "login") && contains("log.message", "failed")))) + (equals("log.eventType", "Audit_Event") && + equalsIgnoreCase("log.domain", "Native user") && + equalsIgnoreCase("action", "Login attempt") && oneOf("actionResult", ["failure", "denied"]) && + exists("target.user") && !oneOf("target.user", ["", "unknown", "-"])) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") afterEvents: - indexPattern: v11-log-antivirus-esmc-eset-* with: - - field: log.headHostname + - field: dataSource operator: filter_term - value: '{{.log.headHostname}}' + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: target.user + operator: filter_term + value: '{{.target.user}}' + - field: log.correlationCandidate.consoleAuthenticationFailure + operator: filter_term + value: 'match' within: 30m count: 10 groupBy: - - lastEvent.log.headHostname - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - target.user diff --git a/rules/antivirus/esmc-eset/eset_quarantine_failures.yml b/rules/antivirus/esmc-eset/eset_quarantine_failures.yml index 11dc8b439..0f7789538 100644 --- a/rules/antivirus/esmc-eset/eset_quarantine_failures.yml +++ b/rules/antivirus/esmc-eset/eset_quarantine_failures.yml @@ -11,10 +11,10 @@ category: Defense Evasion technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" adversary: origin references: - - https://help.eset.com/ees/8/en-US/ + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1562/001/ description: | - Detects repeated quarantine failures in ESET, which may indicate malware actively resisting quarantine through file locks, permission manipulation, or rapid re-creation of malicious files. + Detects five antivirus quarantine or cleaning failures in one hour on the same managed endpoint. The action_error field must explicitly report an error for a relevant remediation operation; unrelated endpoint activity cannot satisfy the threshold. Next Steps: 1. Identify the specific file or threat that cannot be quarantined @@ -24,19 +24,30 @@ description: | 5. Consider isolating the endpoint for manual remediation 6. Run a boot-time scan if available where: | - (contains("log.message", "quarantine") && - (contains("log.message", "failed") || contains("log.message", "error") || - contains("log.message", "unable") || contains("log.message", "denied"))) || - (contains("log.message", "clean") && contains("log.message", "failed") && - contains("log.message", "threat")) + (equals("log.eventType", "Threat_Event") && equals("actionResult", "failure") && + regexMatch("log.actionerror", "(?s).+") && + (regexMatch("action", "(?i)(quarantin|clean)") || regexMatch("log.actionerror", "(?i)(quarantin|clean)"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") afterEvents: - indexPattern: v11-log-antivirus-esmc-eset-* with: - - field: log.headHostname + - field: dataSource operator: filter_term - value: '{{.log.headHostname}}' + value: '{{.dataSource}}' + - field: log.endpointKeyType + operator: filter_term + value: '{{.log.endpointKeyType}}' + - field: log.endpointKey + operator: filter_term + value: '{{.log.endpointKey}}' + - field: log.correlationCandidate.quarantineFailure + operator: filter_term + value: 'match' within: 1h count: 5 groupBy: - - lastEvent.log.headHostname - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey diff --git a/rules/antivirus/esmc-eset/exploit_detection_events.yml b/rules/antivirus/esmc-eset/exploit_detection_events.yml index f2e1f9969..9b7272d0f 100644 --- a/rules/antivirus/esmc-eset/exploit_detection_events.yml +++ b/rules/antivirus/esmc-eset/exploit_detection_events.yml @@ -11,11 +11,11 @@ category: Privilege Escalation technique: "T1068 - Exploitation for Privilege Escalation" adversary: origin references: - - https://www.eset.com/us/about/technology/ + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1068/ description: | - Detects when ESET's Exploit Blocker identifies and blocks exploitation attempts targeting vulnerabilities in commonly exploited applications such as browsers, document readers, email clients, Flash, and Java. - + Detects an explicitly blocked, warning-or-higher exploitation detection in an antivirus threat, firewall event, or HIPS operation/rule. The structured detection must identify exploitation; an exploit-related filename or other arbitrary JSON text does not establish an exploit detection. + Next Steps: 1. Identify the affected host and the specific exploit attempt details 2. Check for any successful exploitation attempts on the same host @@ -24,10 +24,15 @@ description: | 5. Update the vulnerable application if a patch is available 6. Consider isolating the host if exploitation may have succeeded where: | - (contains("log.jsonMessage", "exploit") || - oneOf("log.msgType", ["Exploit_Blocked", "Exploit"])) && - oneOf("actionResult", ["denied", "blocked"]) && - oneOf("log.severity", ["medium", "high"]) + (((equals("log.eventType", "Threat_Event") && regexMatch("log.threatname", "(?i)exploit")) || + (equals("log.eventType", "FirewallAggregated_Event") && (regexMatch("log.event", "(?i)exploit") || regexMatch("log.threatname", "(?i)exploit"))) || + (oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) && (regexMatch("log.operation", "(?i)exploit") || regexMatch("log.rulename", "(?i)exploit")))) && + equals("actionResult", "denied") && oneOf("severity", ["warning", "error", "critical"])) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.jsonMessage - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.eventType diff --git a/rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml b/rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml index 92f9b88c3..94149589c 100644 --- a/rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml +++ b/rules/antivirus/esmc-eset/host_intrusion_prevention_triggers.yml @@ -11,11 +11,11 @@ category: Defense Evasion, Privilege Escalation technique: "T1055 - Process Injection" adversary: origin references: - - https://help.eset.com/ees/8/en-US/idh_hips_main.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1055/ description: | - Detects when ESET's Host-based Intrusion Prevention System (HIPS) blocks suspicious behavior, including process manipulation, registry modifications, and file system changes that indicate potential malware activity. HIPS events indicate active attempts to compromise system integrity through various attack techniques. - + Detects warning-or-higher HIPS blocks whose operation or rule explicitly identifies suspicious or malicious behavior, injection, exploitation, tampering, or self-defense. Ordinary blocked operations from a restrictive policy are excluded from this generic security-behavior rule. The event class identifies HIPS; it is not an action result. A block records an attempted operation, not successful compromise. + Next Steps: 1. Review the blocked process or action details in the ESET console 2. Identify the source application attempting the blocked behavior @@ -25,7 +25,16 @@ description: | 6. Update HIPS rules if necessary to prevent similar attacks 7. Check for persistence mechanisms on the affected host 8. Review network connections from the suspicious process if applicable -where: equals("log.actionResult", "HIPS_Event") && equals("log.action", "blocked") && oneOf("log.severity", ["medium", "high"]) +where: | + (oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) && equals("actionResult", "denied") && oneOf("severity", ["warning", "error", "critical"]) && + (regexMatch("log.operation", "(?i)(suspicious|malicious|injection|exploit|tamper|self[- ]defen[sc]e)") || + regexMatch("log.rulename", "(?i)(suspicious|malicious|injection|exploit|tamper|self[- ]defen[sc]e)"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.objectname - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.rulename + - lastEvent.log.ruleid diff --git a/rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml b/rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml index 0200154a3..75a0809c8 100644 --- a/rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml +++ b/rules/antivirus/esmc-eset/machine_learning_detection_anomalies.yml @@ -11,11 +11,11 @@ category: Execution technique: "T1204.002 - User Execution: Malicious File" adversary: origin references: - - https://help.eset.com/protect_admin/11.0/en-US/events-exported-to-json-format.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1204/002/ description: | - Identifies threats detected by ESET's machine learning engine that analyzes file behavior patterns and characteristics to identify previously unknown malware variants. Machine learning detection indicates advanced malware that may evade signature-based detection methods. - + Detects antivirus detections that explicitly identify machine learning in the structured detection type, detection name, or scanner identity. ESET does not publish an exhaustive value vocabulary for these fields; the retained label heuristic does not claim coverage of every engine/version. + Next Steps: - Immediately investigate the affected host for signs of compromise - Review the threat details in the log message to understand the malware type and behavior @@ -27,10 +27,15 @@ description: | - Update security policies to prevent similar threats - Check for any data exfiltration or lateral movement attempts from the affected host where: | - contains("log.message", "machine learning") && - contains("log.message", ["threat", "detected", "found"]) && - exists("log.msgType") && - exists("log.headHostname") + (equals("log.eventType", "Threat_Event") && + (regexMatch("log.threatname", "(?i)machine[ -]learning") || + regexMatch("log.threattype", "(?i)machine[ -]learning") || + regexMatch("log.scannerid", "(?i)machine[ -]learning"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.headHostname - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.threatname diff --git a/rules/antivirus/esmc-eset/network_attack_detection.yml b/rules/antivirus/esmc-eset/network_attack_detection.yml index fccc30a62..32f565f88 100644 --- a/rules/antivirus/esmc-eset/network_attack_detection.yml +++ b/rules/antivirus/esmc-eset/network_attack_detection.yml @@ -11,11 +11,11 @@ category: Initial Access technique: "T1190 - Exploit Public-Facing Application" adversary: origin references: - - https://help.eset.com/ees/7/en-US/idh_config_epfw_network_attack_protection.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1190/ description: | - Detects network-based attacks and exploits blocked by ESET's Network Attack Protection (IDS). This includes attempts to exploit known vulnerabilities in network services and protocols. - + Detects an explicitly blocked firewall event that identifies an attack, exploitation, intrusion, or named detection. A generic firewall policy block alone is not enough. Physical source and destination addresses remain unchanged; this rule attributes the network attempt to its source. Botnet detections use the separate direction-aware rules so an outbound client is not labeled as the remote command-and-control endpoint. + Next Steps: 1. Review the attack details in ESET console to identify the specific vulnerability or attack pattern 2. Check if the source IP is known malicious using threat intelligence sources @@ -24,9 +24,18 @@ description: | 5. Consider blocking the source IP at the perimeter firewall if attacks persist 6. Update network security policies and ensure all systems are patched where: | - equals("log.event_type", "NetworkProtection_Event") && - equals("log.action", "blocked") && - exists("origin.ip") + (equals("log.eventType", "FirewallAggregated_Event") && equals("actionResult", "denied") && + exists("origin.ip") && + !regexMatch("log.threatname", "(?i)(botnet|cnc[.]generic|c&c|command and control)") && + !regexMatch("log.event", "(?i)(botnet|cnc[.]generic|c&c|command and control)") && + (regexMatch("log.event", "(?i)(attack|exploit|intrusion)") || + (exists("log.threatname") && !oneOf("log.threatname", ["", "unknown", "-"])))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey - adversary.ip - - target.host + - lastEvent.log.threatname diff --git a/rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml b/rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml index b52c7f3f3..b29b26a47 100644 --- a/rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml +++ b/rules/antivirus/esmc-eset/registry_modification_attempts_blocked.yml @@ -11,11 +11,11 @@ category: Defense Evasion, Persistence technique: "T1112 - Modify Registry" adversary: origin references: - - https://help.eset.com/esmc_admin/70/en-US/events-exported-to-json-format.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1112/ description: | - Identifies attempts to modify critical Windows registry keys that were blocked by ESET, indicating potential persistence or system tampering attempts. Registry modifications are a common technique used by malware to establish persistence, disable security features, or alter system behavior. - + Detects warning-or-higher HIPS blocks involving a registry operation or Windows registry target. Review the operation and target to distinguish malicious persistence or tampering from legitimate software blocked by policy. + Next Steps: 1. Review the blocked action details to understand what registry key was targeted 2. Investigate the source process and user account involved in the attempt @@ -24,10 +24,13 @@ description: | 5. If suspicious, isolate the affected system and perform a full malware scan 6. Review system logs for any successful registry modifications before the block occurred where: | - exists("log.jsonMessage") && - contains("log.jsonMessage", "registry") && - oneOf("log.action", ["blocked", "denied", "prevented"]) && - oneOf("log.severity", ["high", "medium"]) + (oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) && equals("actionResult", "denied") && oneOf("severity", ["warning", "error", "critical"]) && + (regexMatch("log.operation", "(?i)registry") || regexMatch("log.target", "(?i)^(HKEY_|HKLM|HKCU|HKCR|HKU|HKCC)"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.action - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.target diff --git a/rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml b/rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml index fdf556479..df103d115 100644 --- a/rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml +++ b/rules/antivirus/esmc-eset/suspicious_encrypted_file_detection.yml @@ -11,22 +11,27 @@ category: Impact technique: "T1486 - Data Encrypted for Impact" adversary: origin references: + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1486/ - - https://help.eset.com/protect_admin/10.1/en-US/events-exported-to-json-format.html description: | - Detects suspicious encrypted file activities that may indicate ransomware encryption attempts or unauthorized file encryption operations. This rule triggers when ESET detects ransomware-related threats or file encryption activities. - + Detects ransomware or Filecoder labels in antivirus detection name/type fields. An encrypted file name or an ordinary encryption-related path alone is not ransomware evidence, and the detection does not establish that file encryption succeeded. + Next Steps: 1. Immediately isolate the affected system to prevent spread 2. Check if backup systems are accessible and uncompromised - 3. Review the threat details in log.jsonMessage for specific ransomware variant + 3. Review the structured threat name and type for the reported ransomware variant 4. Look for other systems showing similar encryption patterns 5. Preserve forensic evidence before remediation 6. Consider engaging incident response team for ransomware cases 7. Do not power off the system if encryption is in progress where: | - equals("log.msgType", "Threat_Event") && - contains("log.jsonMessage", ["ransomware", "filecoder", "encrypted", ".encrypted"]) + (equals("log.eventType", "Threat_Event") && + (regexMatch("log.threatname", "(?i)(ransomware|filecoder)") || regexMatch("log.threattype", "(?i)(ransomware|filecoder)"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.msgType - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.threatname diff --git a/rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml b/rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml index d0927f2be..317ca2da3 100644 --- a/rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml +++ b/rules/antivirus/esmc-eset/suspicious_powershell_activity_blocked.yml @@ -2,7 +2,7 @@ dataTypes: - antivirus-esmc-eset -name: ESET Blocked Suspicious PowerShell Activity +name: ESET Blocked PowerShell Activity impact: confidentiality: 3 integrity: 3 @@ -11,11 +11,11 @@ category: Execution technique: "T1059.001 - Command and Scripting Interpreter: PowerShell" adversary: origin references: - - https://help.eset.com/ees/8/en-US/idh_hips_main.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1059/001/ description: | - Detects when ESET blocks suspicious PowerShell commands or scripts that exhibit malicious behavior patterns, including obfuscated scripts, encoded commands, or attempts to bypass execution policies. This is a high-priority security event that indicates potential malicious activity was prevented. - + Detects an explicitly blocked HIPS or antivirus event associated with the PowerShell or pwsh executable. The process comes from the structured application/process field. Review the detector and operation to establish malicious behavior; a block alone does not prove a particular command-line technique. + Next Steps: 1. Review the blocked PowerShell command details in the log message 2. Identify the user account and process that attempted to execute PowerShell @@ -24,7 +24,14 @@ description: | 5. Look for other suspicious activities from the same host or user 6. Consider isolating the affected system if malicious intent is confirmed 7. Review and update PowerShell execution policies if needed -where: regexMatch("log.message", "(?i)(powershell|pwsh)") && equals("log.action", "blocked") && exists("log.headHostname") +where: | + ((oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) || equals("log.eventType", "Threat_Event")) && + equals("actionResult", "denied") && regexMatch("origin.process", "(?i)^(powershell|pwsh)([.]exe)?$")) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.headHostname - - target.host + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - adversary.process diff --git a/rules/antivirus/esmc-eset/suspicious_process_behavior.yml b/rules/antivirus/esmc-eset/suspicious_process_behavior.yml index cf0ceb691..eb2af618f 100644 --- a/rules/antivirus/esmc-eset/suspicious_process_behavior.yml +++ b/rules/antivirus/esmc-eset/suspicious_process_behavior.yml @@ -11,17 +11,17 @@ category: Defense Evasion, Privilege Escalation technique: "T1055 - Process Injection" adversary: origin references: - - https://help.eset.com/ees/12/en-US/idh_dialog_epfw_ids_alert.html + - https://help.eset.com/protect_admin/13.1/en-US/events-exported-to-json-format.html - https://attack.mitre.org/techniques/T1055/ description: | - Detects suspicious process behaviors including injection attempts, privilege escalation, and abnormal process creation patterns identified by ESET's behavioral monitoring. This alert indicates potential malware activity or exploitation attempts on the affected system. - + Detects HIPS operations or ESET Inspect rules that explicitly identify process injection, suspicious objects/behavior, or anomalous processes. Generic blocked, terminated, or prevented action text alone does not establish one of these behaviors. + Next Steps: 1. Immediately review the alert details to identify: - - Affected hostname (check log.headHostname) - - Specific threat or behavior detected (check log.jsonMessage) + - Affected hostname (check target.host) + - Specific threat or behavior detected (check the structured operation and rule name) - Process name and path if available - - Time of detection (check log.deviceTime) + - Time of detection (check deviceTime) 2. Investigate the process that triggered the alert: - Verify if it's a legitimate application or unknown/suspicious - Check process creation chain and parent-child relationships @@ -43,10 +43,13 @@ description: | - Ensure ESET real-time protection is enabled - Consider implementing application whitelisting where: | - oneOf("log.msgType", ["EnterpriseInspectorAlert_Event", "HIPS_Event"]) && - exists("log.jsonMessage") && - contains("log.jsonMessage", ["Process injection", "Suspicious behavior", - "Anomalous process", "blocked", "terminated", "prevented"]) + ((oneOf("log.eventType", ["HipsAggregated_Event", "HIPSAggregated_Event"]) && (regexMatch("log.operation", "(?i)(process injection|suspicious (object|behavior)|anomalous process|injecting)") || regexMatch("log.rulename", "(?i)(process injection|suspicious (object|behavior)|anomalous process|injecting)"))) || + (equals("log.eventType", "EnterpriseInspectorAlert_Event") && regexMatch("log.rulename", "(?i)(process injection|suspicious (object|behavior)|anomalous process|injecting)"))) && + exists("dataSource") && !oneOf("dataSource", ["", "unknown", "-"]) && + oneOf("log.endpointKeyType", ["uuid", "host", "ip"]) && + exists("log.endpointKey") && !equals("log.endpointKey", "") groupBy: - - lastEvent.log.headHostname - - lastEvent.log.msgType + - lastEvent.dataSource + - lastEvent.log.endpointKeyType + - lastEvent.log.endpointKey + - lastEvent.log.eventType