Skip to content

fix(sentinel-one): parse CEF header and full values; align rule consumers - #2677

Draft
kryonsx wants to merge 3 commits into
utmstack:v11from
kryonsx:codex/v11-sentinel-one-review-20260923
Draft

kryonsx wants to merge 3 commits into
utmstack:v11from
kryonsx:codex/v11-sentinel-one-review-20260923

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Summary

The SentinelOne filter did not parse the management-console CEF records seen in the field, so none of the 19 SentinelOne rules could match a real record. This draft fixes the parser and updates 14 rules so they read what the filter now produces, without firing on ordinary console administration.

Predecessor: #2595 (closed, historical context only).

Filter (filters/antivirus/sentinel-one.yml, 3.0.0 → 3.0.1)

  • Parse the CEF header by position, anchored on CEF:. The event name now reaches log.eventDescription. The header severity is kept as vendor detail in log.cefSeverity; standard severity is not set.
  • Remove the two header parsers that took log.syslogHost from the product-version slot. Real records carry a console build string there.
  • Guard kv so lines without a CEF extension no longer produce a large parser error.
  • Keep multi-word values whole (site, account, user, group and address keys), including when the key is last. Header text no longer becomes junk field names.
  • Extract the full rt time to log.ruleTime and convert it to deviceTime.
  • Copy the console actor (suser) to origin.user for console events (cat=SystemEvent).
  • The actionResult block is unchanged. A separate draft revises it.

Rules (rules/antivirus/sentinel-one/, 14 files)

  • Replace the log.syslogHost gate and grouping with target.host. This ships together with the parser removal above.
  • memory_injection_detection: remove a branch that read log.eventDescToParse, which the filter deletes.
  • s1_policy_downgrade: require the Protect-to-Detect direction; it no longer fires on upgrades.
  • Whole-word matching in the tampering, mitigation-failure and IoT rules. For example, "ics" no longer matches inside "analytics".
  • kernel_level_threat and the "reputation" branch of threat_intelligence_matches now require target.host, so console admin text cannot fire them.
  • The exclusion and policy rules now group by adversary.user.
  • threat_intelligence_matches: use the vendor-documented confidence value MALICIOUS instead of >= 90.
  • Names, IDs, thresholds, windows, impact, adversary and MITRE labels are unchanged.

Validation

  • Full plugins/alerts Go suite passes, linked go-sdk v1.1.33. Five new SentinelOne tests are included, and four of them fail against the original files.
  • plugins/alerts/testdata/sentinel-one/replay.py runs 52 fabricated raw lines through an isolated EventProcessor playground (commit 497bf53). With the corrected filter and rules there were zero parser errors, and each intended local alert fired exactly once.
  • Genuine console records (user added, user deleted, role assigned) were replayed privately. With the corrected filter and rules, none of the 19 rules alerted on them. With the corrected filter and the original rules, 7 rules produced false alarms, so the filter and rules must ship together.

Limits

  • SentinelOne's syslog/CEF reference was not readable from public vendor sources. Threat-event extension keys, the activity-code list and the CEF severity scale are therefore not established.
  • Seven rules now depend on target.host. Nothing writes it until the endpoint key is documented, and before this change those rules depended on a field that real records never had.
  • Deferred and listed in the audit:
    • activity-code lists, dusertarget.user, severity/action mapping, IoT/Storyline rule relevance;
    • MITRE relabelling (T1562 was revoked in ATT&CK v19).
  • Two known false-alarm patterns remain until activity codes are documented. They are listed in the audit.
  • Playground parser plugins link go-sdk v1.1.26 and the rule plugin links v1.1.34, while this module pins v1.1.33. History searches, grouping, deduplication, production alerts and notifications were not tested. No SentinelOne threat records were available.
  • A parallel draft edits the same filter's actionResult block. The only expected conflict is the version comment line.

See filters/audits/sentinel-one.md for details.

🤖 Generated with Claude Code

kryonsx and others added 3 commits September 23, 2026 17:03
…mers

The management-console CEF records lost the event name, kept one word of
values with spaces, turned header text into field names and cut rt to a
fragment, so the twelve rules that need the event name never matched.

Filter 3.0.1:
- Parse the CEF header by position after "CEF:"; name to
  log.eventDescription, header severity to log.cefSeverity. The
  standard severity stays unset: the CEF scale is not documented.
- Drop the two parsers that stored the version slot as log.syslogHost.
- Run kv only when log.restData exists.
- Keep the full rt in log.ruleTime; set deviceTime from it only for the
  observed UTC layout.
- Keep whole values for nine keys, including the last key.
- Copy suser to origin.user for SystemEvent records only.

Rules (names, thresholds, impact, adversary and MITRE unchanged):
- memory_injection_detection: drop the deleted scratch-field branch.
- target.host replaces log.syslogHost in five gates and eight groupings.
- kernel_level_threat and the reputation branch require target.host.
- s1_policy_downgrade fires only on Protect to Detect, paired wording
  included.
- s1_exclusion_abuse and s1_policy_downgrade group by adversary.user.
- Whole-word lists in agent tampering, mitigation failures and IoT.
- threat_intelligence_matches compares confidencelevel with MALICIOUS.

The actionResult steps are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sentinel_one_filter_test.go checks the filter structure, compiles every
pattern, runs the header, value and rt patterns over 52 fabricated lines
and evaluates 41 predicate cases against the shipped rules with go-sdk
v1.1.33. testdata/sentinel-one/replay.py replays the same lines through
the public EventProcessor playground and checks every field and alert,
optionally with a test-only step that supplies target.host. All inputs
are invented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Evidence basis, each change and its proof level, validation results,
deferred items and known limits.

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

This branch has not been deployed

No deployments
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.

1 participant