Skip to content

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

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

kryonsx wants to merge 6 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

Re-checked on 2026-09-24 on the latest versions: official v11 d2479c1a (merged into this branch, no conflicts; plugins/alerts now pins go-sdk v1.1.36) and EventProcessor main 8a3ade7 (playground and every plugin on go-sdk v1.1.36).

  • go-sdk v1.1.35+ keeps underscores in field names the parser plugins write, and v1.1.36 makes regexMatch match strings only. Neither changes this draft: the filter writes only letters and digits in names, no kv key in the fixtures or the genuine records contains an underscore, and every regexMatch/contains call reads a text field. No file needed a change.
  • Full plugins/alerts Go suite on go-sdk v1.1.36: 48 tests pass, 11 skip (other technologies' private-evidence tests, same as the base), 0 fail. Five new SentinelOne tests are included, and four of them fail against the original files.
  • plugins/alerts/testdata/sentinel-one/replay.py on the latest playground: 52 fabricated raw lines, zero parser errors, every field as in expected.json, 10 local alerts, each from its intended rule. With --endpoint-harness: 59 events and 17 alerts.
  • go-sdk v1.1.36 rule replay of all 19 rules over those events: no errors, and the matches are exactly the playground alerts and the alerts in expected.json.
  • Genuine console records (user added, user deleted, role assigned) were replayed privately on the latest playground too. 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. All 4,304 private assertions pass, as they did on the older build.

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, duser → target.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.
  • The playground (EventProcessor 8a3ade7) and this module both use go-sdk v1.1.36; neither is claimed to match a customer deployment. 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>
kryonsx and others added 3 commits September 24, 2026 15:17
Brings in go-sdk v1.1.36 for plugins/alerts (field names keep underscores; regexMatch matches strings only) and the coordinated vendor re-baseline. No file overlaps this draft.

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

Official v11 d2479c1 (go-sdk v1.1.36 in plugins/alerts) and EventProcessor main 8a3ade7 (every playground plugin on v1.1.36). Field names now keep underscores and regexMatch matches strings only; neither changes this draft, because the filter writes only letters and digits in names, no kv key in the fixtures or genuine records has an underscore, and every text-search call reads a string field. Full plugins/alerts suite: 48 pass, 11 skip, 0 fail. replay.py: 52 events and 10 alerts, 59 and 17 with the endpoint harness, all as expected. v1.1.36 rule replay matches the playground alerts exactly. The private runs repeat all 4,304 assertions.

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

The newest published engine image, eventprocessor:v11.2.14 (built 2026-09-24 19:13 UTC), carries EventProcessor revision 8a3ade7 with go-sdk v1.1.36, built with go1.26.8 for linux/amd64. The local build used for the re-validation is the same source compiled natively for darwin/arm64 with go1.25.7.

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