Skip to content

Commit a8ece4b

Browse files
authored
Correct correlation-rule Event Query hydration guidance (#28)
The skills stated that correlation-rule detections cannot be hydrated by an Event Query and must use a Get Detection Details action. Community evidence from working production workflows shows this is wrong: correlation-rule detections hydrate with the same Ngsiem.alert.id = ?DetectionID query as first- and third-party detections. The catch is that the query returns multiple records (the alert record plus the underlying events), so an unfiltered results[0] is non-deterministic. Filtering with | #event.kind = "event" or | Ngsiem.event.product = CrowdStrike, or projecting named columns with table([...]), narrows it to one predictable row. Updates the two trigger-types.md copies, event-query-vs-api.md, event-queries.md, and the ngsiem-detection-response use case to say hydration works for all detection types with the filtering caveat, keeping Get Detection Details as a valid fallback when Event Query results are unreliable. The validate.py Ngsiem.detection.id join guard is unchanged in behavior (that field is the wrong join key for any detection type), but its error message no longer tells authors correlation-rule detections need Get Detection Details; it now points them to Ngsiem.alert.id with the multi-record filtering note. 541 tests pass; pylint and markdownlint clean.
1 parent 8a12ae0 commit a8ece4b

6 files changed

Lines changed: 51 additions & 39 deletions

File tree

skills/authoring/references/event-query-vs-api.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,17 @@ silently return zero. The split is about whether you already hold the object:
8181
Ngsiem.detection.id = ?detectID
8282
```
8383
84-
- **Correlation-rule detections** → **Get Detection Details** action, NOT an
85-
Event Query. The rule trigger exposes only the composite `DetectionID` and an
86-
opaque `ID`; neither matches the `Ngsiem.detection.id` value that Advanced
87-
event search filters on, and that field is not in the trigger schema. The
88-
field that used to bridge it (`Ngsiem.original_indicator.id`) is gone from the
89-
rule-trigger events, so a correlation-rule detection cannot be hydrated via
90-
Event Query today. Add a **Get Detection Details** gather step (or a
91-
CrowdStrike HTTP Request to `/alerts/entities/alerts/v2` passing the composite
92-
`DetectionID` as `composite_id`) to fetch the full detection, then read fields
93-
from its response.
84+
- **Correlation-rule detections** → hydrate with the same
85+
`Ngsiem.alert.id = ?detectID` query. It works, but returns **multiple records**
86+
(the alert record plus the underlying events), so `results[0]` is
87+
non-deterministic across runs. Filter to a single predictable row with
88+
`| #event.kind = "event"` or `| Ngsiem.event.product = CrowdStrike`, or project
89+
named columns with `table([field1, field2, ...])`. Restrict the action's output
90+
schema to only the fields you read, so runs that omit some fields don't fail
91+
schema validation. If Event Query results are unreliable for your detection
92+
type, a **Get Detection Details** action (or a CrowdStrike HTTP Request to
93+
`/alerts/entities/alerts/v2` passing the composite `DetectionID` as
94+
`composite_id`) is a valid alternative.
9495
9596
**Treat detection IDs as opaque.** Per the detections team, `composite_id` (what
9697
the trigger hands you) is the primary key used to retrieve a detection, but its

skills/authoring/references/trigger-types.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,17 @@ list), hydrate with an Event Query and read from its `results` array:
214214
through an inline Python extractor** (`cs.json.decode(data['<Python>.output_stdout'])`)
215215
— that form does not resolve at release. Read `results[0].<Field>` directly.
216216

217-
**Event Query hydration only works for first-party and third-party detections**
218-
(match the composite `DetectionID` against `Ngsiem.alert.id`, not
219-
`Ngsiem.detection.id`). **Correlation-rule detections cannot be hydrated by an
220-
Event Query** — their `Ngsiem.detection.id` is not exposed on the trigger and no
221-
Event Query field matches it. For those, add a **Get Detection Details** action
222-
(or an HTTP Request to `/alerts/entities/alerts/v2` passing the composite
223-
`DetectionID` as `composite_id`). See `references/event-query-vs-api.md`.
217+
**Event Query hydration works for all NG-SIEM detection types** — match the
218+
composite `DetectionID` against `Ngsiem.alert.id` (not `Ngsiem.detection.id`).
219+
For **correlation-rule detections** the same `Ngsiem.alert.id = ?DetectionID`
220+
query works, but it returns **multiple records** (the alert record plus the
221+
underlying events), so `results[0]` is non-deterministic without a filter. Narrow
222+
it to one predictable row with `| #event.kind = "event"` or
223+
`| Ngsiem.event.product = CrowdStrike`, or project named columns with
224+
`table([field1, field2, ...])`. If Event Query results are unreliable for a given
225+
detection type, a **Get Detection Details** action (or an HTTP Request to
226+
`/alerts/entities/alerts/v2` passing the composite `DetectionID` as `composite_id`)
227+
is a valid alternative. See `references/event-query-vs-api.md`.
224228

225229
**Not every `Trigger.Detection.*` field resolves on the NG-SIEM trigger.**
226230
`${Trigger.Detection.Product}` and `${Trigger.Detection.Description}` are available

skills/authoring/scripts/validate.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,12 +421,11 @@ def _check_detection_hydration_join(label, config, issues):
421421
issues.append(
422422
f"ERROR: Event Query action '{label}' hydrates a detection by "
423423
f"matching 'Ngsiem.detection.id = ?...'. The trigger never provides "
424-
f"that field. For first-party and third-party detections, the "
425-
f"trigger's composite DetectionID is stored in 'Ngsiem.alert.id' — "
426-
f"change the join field to 'Ngsiem.alert.id'. For correlation-rule "
427-
f"detections, neither Event Query field matches; use a 'Get Detection "
428-
f"Details' action (or an HTTP Request to /alerts/entities/alerts/v2 "
429-
f"passing the composite DetectionID as composite_id) instead. As "
424+
f"that field. The trigger's composite DetectionID is stored in "
425+
f"'Ngsiem.alert.id' for all detection types — change the join field to "
426+
f"'Ngsiem.alert.id'. For correlation-rule detections that query returns "
427+
f"multiple records (the alert plus underlying events), so filter it "
428+
f"(e.g. '| #event.kind = \"event\"') to a single predictable row. As "
430429
f"written this query returns zero rows at runtime and the workflow "
431430
f"enriches nothing, despite releasing cleanly. See "
432431
f"references/event-query-vs-api.md."

skills/workflows/references/trigger-types.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,16 @@ rejected at release as "unknown variable" for EPP triggers. Enrich indicators
123123
straight from the trigger payload (e.g. send `Process.SHA256` to VirusTotal);
124124
you do not need an Event Query to hydrate the detection.
125125

126-
**If you must hydrate a detection** (indicators not on the trigger), the path
127-
depends on detection type. First-party/third-party detections can be pulled with
128-
an Event Query matching the composite `DetectionID` against `Ngsiem.alert.id` (not
129-
`Ngsiem.detection.id`). **Correlation-rule detections cannot be hydrated by an
130-
Event Query** — their `Ngsiem.detection.id` is not exposed on the trigger; use a
131-
**Get Detection Details** action (or an HTTP Request to
126+
**If you must hydrate a detection** (indicators not on the trigger), match the
127+
composite `DetectionID` against `Ngsiem.alert.id` (not `Ngsiem.detection.id`) in
128+
an Event Query. This works for all NG-SIEM detection types. For **correlation-rule
129+
detections** the same query returns **multiple records** (the alert plus the
130+
underlying events), so filter it to one predictable row — `| #event.kind = "event"`
131+
or `| Ngsiem.event.product = CrowdStrike` — or project columns with `table([...])`.
132+
A **Get Detection Details** action (or an HTTP Request to
132133
`/alerts/entities/alerts/v2` passing the composite `DetectionID` as `composite_id`)
133-
instead. See `../../authoring/references/event-query-vs-api.md`.
134+
is a valid alternative if Event Query results are unreliable. See
135+
`../../authoring/references/event-query-vs-api.md`.
134136

135137
**Severity is an integer (1-5) at `Trigger.Detection.Severity`, not a string.** Use numeric comparison in CEL conditions:
136138

use-cases/event-queries.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ User wants a workflow to query the NG-SIEM event store — ingested logs, custom
1212
LogScale search results, or enriching a detection the workflow already holds (e.g.
1313
`Ngsiem.alert.id = ?detectID` — for first-party/third-party detections the trigger's composite
1414
`DetectionID` is stored as `Ngsiem.alert.id`, not `Ngsiem.detection.id`; correlation-rule
15-
detections can't be hydrated this way, use a Get Detection Details action) — without defining a
15+
detections can be hydrated the same way, but the query returns multiple records — filter it, see
16+
below) — without defining a
1617
schema up front. The Event Query action
1718
runs a CQL/FQL query inline and
1819
returns matching events, which subsequent actions process and branch on. This is the
@@ -109,6 +110,9 @@ code-driven querying belongs in a function.
109110
> you already hold* — when the workflow was triggered on a **first-party or third-party** detection
110111
> and has its ID, an Event Query like `Ngsiem.alert.id = ?detectID` to pull more fields is the right
111112
> tool (match the composite `DetectionID` against `Ngsiem.alert.id`, not `Ngsiem.detection.id`).
112-
> **Correlation-rule detections are the exception:** their `Ngsiem.detection.id` is not in the
113-
> trigger and can't be reached by an Event Query — use a **Get Detection Details** action instead.
114-
> See [event-query-vs-api.md](../skills/authoring/references/event-query-vs-api.md).
113+
> **Correlation-rule detections can also be hydrated** with `Ngsiem.alert.id = ?detectID`, but the
114+
> query returns multiple records (the alert plus the underlying events), so `results[0]` is
115+
> non-deterministic without a filter. Narrow it with `| #event.kind = "event"` or
116+
> `| Ngsiem.event.product = CrowdStrike`, or project named columns with `table([...])`. A **Get
117+
> Detection Details** action remains a valid alternative if Event Query results are unreliable for
118+
> your detection type. See [event-query-vs-api.md](../skills/authoring/references/event-query-vs-api.md).

use-cases/ngsiem-detection-response.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ where each is covered in depth and verified live.
7676

7777
## Gotchas
7878

79-
- **Hydration join key is detection-type-dependent.** `Ngsiem.alert.id` works for
80-
first/third-party detections (and for querying a correlation detection's
81-
underlying connector events); a correlation *detection record* with no queryable
82-
underlying events needs a Get Detection Details action instead. This is the most
83-
common silent-empty failure - see `references/event-query-vs-api.md`.
79+
- **Hydration join key is `Ngsiem.alert.id`, not `Ngsiem.detection.id`.** Match the
80+
composite `DetectionID` against `Ngsiem.alert.id` for all detection types. A
81+
correlation-rule detection hydrates the same way, but the query returns multiple
82+
records (the alert plus its underlying events), so filter to one predictable row
83+
(`| #event.kind = "event"` or `| Ngsiem.event.product = CrowdStrike`) — an
84+
unfiltered `results[0]` is the most common silent-empty/wrong-row failure. A Get
85+
Detection Details action is a valid fallback. See `references/event-query-vs-api.md`.
8486
- **Charlotte AI needs credits**, and the org must opt in. A workflow that invokes
8587
it will not produce a summary in a tenant without them.
8688
- **Business-hours logic:** the guide implements the time gate as hardcoded

0 commit comments

Comments
 (0)