Skip to content

fix(kaspersky): classify final actions and recover endpoint IPs - #2672

Merged
osmontero merged 2 commits into
utmstack:v11from
kryonsx:codex/data-engine-antivirus-kaspersky-20260923
Sep 25, 2026
Merged

osmontero merged 2 commits into
utmstack:v11from
kryonsx:codex/data-engine-antivirus-kaspersky-20260923

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The Kaspersky filter's CEF extension and native KSC syslog groks do not match the sequential EventProcessor executor. It trims before each pattern and rejects empty matches, so the reviewed filter loses action and IP fields. That prevents an explicit Allow or Blocked action from producing a reliable final actionResult, and loses the endpoint IP on native health events.

Change

Use nonempty captures compatible with the actual grok plugin. Clear any incoming actionResult and derive it from explicit final act values: allow to success; block, deny, drop, reject and quarantine to denied; fail to failure. Leave unknown actions and native KSC endpoint-health records without an outcome. Preserve CEF source/destination IP roles and map a valid native hip to target.ip without treating a health status as a successful connection. Preserve CEF message text and prevent an escaped equals sign in it from creating a false source IP.

Validation

  • Seventeen fabricated CEF and native KSC cases pass the isolated EventProcessor parser; the unchanged filter passed 0/17. The cases include allowed and denied actions with both IP endpoints, failed and unknown operations, a stale incoming success result, and native health events that must remain unclassified.
  • Read-only sampling found 20 native health records whose raw messages included endpoint IPs but whose stored events had neither an IP nor an outcome. The corrected local parser recovers target.ip and leaves actionResult unset on all 20. Raw records and identifiers stay in the private evidence pack.
  • The full plugins/alerts Go suite passes, including 85 existing Kaspersky raw contracts and positive/negative SDK CEL predicates. The local parser uses EventProcessor's default SDK v1.1.26; this branch's alerts module pins v1.1.33, so deployed behavior after rollout remains unverified.

The grok implementation establishes its trim and empty-match behavior; the SDK schema defines controlled result values. The configured Kaspersky documentation links redirected to a generic support page and did not provide an event contract, so the new verdicts are limited to unambiguous action words. No customer configuration was changed and no production TI alert is claimed.

GitHub's AI review jobs cannot start on this fork PR because THREATWINDS_API_KEY and THREATWINDS_API_SECRET are unavailable to the runner; their log stops at the missing-key check, so they reported no code findings. The Go dependency check passes, and the local parser and Go tests above provide validation for this draft.

kryonsx and others added 2 commits September 23, 2026 14:38
…eeps

The parser plugins remove every character except letters, digits and dots
from the field names they write. The Kaspersky filter added its 4
correlation markers with underscores (for example
log.correlationCandidate.lateral_movement_indicators), so they were stored
without them (log.correlationCandidate.lateralmovementindicators) and the 4
history rules that count them never matched.

Rename the markers to camelCase (for example
log.correlationCandidate.lateralMovementIndicators) in the filter and in
every rule that reads them, and bump those rules to v1.1.1. Rule
conditions, thresholds and windows are unchanged.

Tests: the Kaspersky parse model now stores grok, rename and add targets
the way the parser does, the history test counts each marker by its new
name, and a new test checks that every marker the filter adds and every
marker a Kaspersky rule reads are the same name, made only of letters,
digits and dots. The fixture that forges a marker in the input now uses the
new name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kryonsx

kryonsx commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Change pushed (ead382f5): history markers renamed to names the parser keeps

What changed

  • filters/antivirus/kaspersky.yml: the 4 log.correlationCandidate.* markers are now camelCase: dataExfiltrationAttempts, kasperskyRansomwareBehavior, lateralMovementIndicators and suspiciousNetworkActivity.
  • The 4 rules in rules/antivirus/kaspersky/ that read them use exactly the same names, and are now v1.1.1.
  • plugins/alerts:
    • New kaspersky_marker_names_test.go. It reads the filter and every Kaspersky rule, and checks that each marker the filter adds and each marker a rule reads have the identical name, made only of letters, digits and dots. It also checks that each history rule counts the value the filter adds.
    • The parse model in the tests now cleans grok, rename and add target names the way the parser does, and the history tests look up the new names.

Why

  • The EventProcessor parser keeps only letters, digits and dots in the field names it writes (utils.SanitizeField). The filter wrote names such as log.correlationCandidate.data_exfiltration_attempts, which is stored as log.correlationCandidate.dataexfiltrationattempts, while the rules counted the underscored name. So the history rules never counted anything.
  • The old tests missed this because their parse model kept the underscores. Against the previous names, the new test and the history threshold tests fail for every marker. On this commit they pass.
  • The same underscored markers are already in v11 from fix(kaspersky): parse native KSC logs and repair CEF rule contracts #2635, so v11 has this problem until this merges.
  • Until the EventProcessor handles these characters, filters and rules should use only letters, digits and dots in field names.

Validation

  • Local EventProcessor playground on 27 real Kaspersky records from production instances, comparing this draft before and after the change. All 27 events are parsed on both sides, with no errors and no alerts. None of these records triggers a marker in either version, so real data cannot show the rename.
  • The playground also ran on the 85 cases in this pull request's Kaspersky test data. All 4 markers are stored under their camelCase names, and each of the 4 rules now has 1 or 2 events carrying its marker, up from 0.
  • go test ./... in plugins/alerts passes. The Kaspersky filter and rules have no field names with _ or other special characters.

Not changed

  • The CEF msg value is taken up to the end of the extension and then cut at the next unescaped =. When another key follows msg, that key's name stays at the end of log.msg; only a trailing dst is removed. In the real records we sampled, msg is the last key, so they are not affected. The rules that match words in log.msg could see a stray key name when the order differs.
  • The history thresholds themselves were not tested, because counting past events needs a search index. After deployment, only new events count toward the history windows.

@osmontero
osmontero marked this pull request as ready for review September 25, 2026 15:13
@osmontero
osmontero requested a review from a team September 25, 2026 15:13
@osmontero
osmontero merged commit 0e4992b into utmstack:v11 Sep 25, 2026
3 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants