feat(sentinelone): with_details on the unified alerts fetch (v1.13.0)
The list nodes carry no indicator, no ATT&CK mapping, no observable and no related alert — everything that says why the console fired, and the analyst opens the alert in SentinelOne to find it. with_details reads the per-alert view for every alert the fetch returns and folds it into the alert, so the incident is created with that context instead of a title and a severity. Read in aliased batches of ten, so a default-limit fetch costs ten extra requests rather than a hundred. Merged under the list node — the list is the authority on the fields both carry, the detail only fills in — so the richer list asset/process survive the merge and gain process.username and file.size. Best effort: an alert the console will not detail keeps its list fields and the shortfall is reported in pagination.details, rather than failing a fetch whose alerts are already in hand. Off by default, and verified query-identical to 1.12.0 for a rule that does not set it. The mapper gains evidences, finding_info.attacks, finding_info.related_events and the full observables list. Each is guarded with $exists and wrapped in [ ] — checked against the jsonata-go build the scheduler uses: the brackets keep a single indicator from collapsing into a bare object, and the guard leaves the entry undefined on a plain list fetch so it cannot write an empty array over the file summary those alerts do carry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EU1Rk9N6vNtp3q7YvLpjcc
This commit is contained in:
@@ -113,3 +113,17 @@ ocsf:
|
||||
# ── The alert's own file summary, hash type unstated ──────────────
|
||||
- { source_path: "fileName", ocsf_field: "observables.name" }
|
||||
- { source_path: "fileHash", ocsf_field: "observables.value" }
|
||||
# ── Only present when the fetch ran with_details ──────────────────
|
||||
# Absent paths are skipped at ingestion, so these stay inert on a plain
|
||||
# list fetch. observables comes last on purpose: with the detail read, the
|
||||
# full list replaces the single file summary set just above.
|
||||
- { source_path: "process.username", ocsf_field: "actor.user.name" }
|
||||
- { source_path: "process.file.size", ocsf_field: "actor.process.file.size" }
|
||||
- { source_path: "process.file.size", ocsf_field: "file.size" }
|
||||
# [ ] forces a list of one to stay a list; the $exists guard keeps an empty
|
||||
# or absent value undefined, so a plain list fetch skips these entries
|
||||
# instead of writing an empty array over the file summary above.
|
||||
- { source_path: '$exists(indicators) ? [indicators]', ocsf_field: "evidences" }
|
||||
- { source_path: '$exists(indicators.attacks) ? [indicators.attacks.{"tactic": {"name": tactic.name, "uid": tactic.uid}, "technique": {"name": technique.name, "uid": technique.uid}}]', ocsf_field: "finding_info.attacks" }
|
||||
- { source_path: '$exists(relatedAlerts) ? [relatedAlerts.{"uid": id, "title": name}]', ocsf_field: "finding_info.related_events" }
|
||||
- { source_path: '$exists(observables) ? [observables.{"name": name, "value": value, "type": type}]', ocsf_field: "observables" }
|
||||
|
||||
Reference in New Issue
Block a user