feat(crowdstrike): fetch incrementally via a created_from watermark (v1.3.0)

search_detections had one input for everything, `filter`, so an alert rule
configured for incremental fetch had nowhere to put its watermark but
there — where it REPLACED the FQL with a bare timestamp instead of
narrowing it. The API rejects that outright (HTTP 400, "invalid query
filter in request"), and even had it not, the product/type scoping would
have been silently discarded with it. The only working configuration left
was a full fetch on every run.

created_from is that missing input, following the shape get_alerts already
uses on sentinelone: a timestamp of its own, declared as the command's
incremental_field so the alert-rule editor offers it. The script folds it
into the FQL as created_timestamp, which is where this API expresses a time
bound. filter now carries the scoping only — the two no longer compete.

Surrounding quotes on the value are stripped: FQL wants them, and an
operator filling the first-fetch date by hand may reasonably include them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 22:47:46 +02:00
parent 21b957be72
commit 643290b733
2 changed files with 15 additions and 4 deletions
+6 -4
View File
@@ -1,8 +1,8 @@
id: crowdstrike id: crowdstrike
name: CrowdStrike Falcon name: CrowdStrike Falcon
version: 1.2.4 version: 1.3.0
description: "CrowdStrike Falcon (OAuth2 API) — full IR coverage: device/IOC/process enrichment, detections & cases, host groups, Real Time Response, ML/IOA exclusions, quarantine, Spotlight/CVE, ODS scans, CSPM, users, IOA rules, CNAPP, and Fusion workflows." description: "CrowdStrike Falcon (OAuth2 API) — full IR coverage: device/IOC/process enrichment, detections & cases, host groups, Real Time Response, ML/IOA exclusions, quarantine, Spotlight/CVE, ODS scans, CSPM, users, IOA rules, CNAPP, and Fusion workflows."
changelog: "1.2.4 — Fixed two silent defects in the search_detections OCSF mapper: severity was a ternary that resolved to nothing, so every alert kept the alert rule's default severity instead of Falcon's — it is now a plain path to severity_name, whose words the engine reads natively; and no entry fed the OCSF `time` field, so detected_at was never stamped and MTTD measured the collection delay as zero. 1.2.3 — Documentation wording cleanup. 1.2.2 — Corrected the search_detections OCSF mapper against the Falcon Raptor alert schema: domain is device.hostinfo.domain, dropped non-existent fields, added scenario/alleged_filetype/cid. 1.2.1 — Marked search_detections as an ingestion source (ingest hints: results_path=resources, dedup_key=composite_id) so the alert-rule editor pre-fills it. 1.2.0 — Added a pre-built OCSF mapper for search_detections (Falcon unified alerts → OCSF: device, finding, MITRE, actor/parent process, file and IOC). 1.1.1 — Added test_connection for the instance Test button. 1.1.0 — Expanded to 81 commands (host groups, cases, RTR files/scripts/responders, ML/IOA exclusions, quarantine, Spotlight host-by-vuln/CVE, ODS scans, CSPM, users, IOA rules, CNAPP, identity/mobile detection resolve, and workflows). 1.0.0 — Initial release: device/detection enrichment, Spotlight, IOC management, contain/lift, and core RTR." changelog: "1.3.0 — search_detections takes a created_from timestamp, folded into the FQL as created_timestamp, and declares it as the ingestion watermark input. An alert rule can now fetch incrementally instead of re-reading the whole window every run: this API expresses its time bound inside the FQL, so the watermark used to have nowhere to go but the filter input, where it replaced the query rather than narrowing it. filter now carries the scoping only. 1.2.4 — Fixed two silent defects in the search_detections OCSF mapper: severity was a ternary that resolved to nothing, so every alert kept the alert rule's default severity instead of Falcon's — it is now a plain path to severity_name, whose words the engine reads natively; and no entry fed the OCSF `time` field, so detected_at was never stamped and MTTD measured the collection delay as zero. 1.2.3 — Documentation wording cleanup. 1.2.2 — Corrected the search_detections OCSF mapper against the Falcon Raptor alert schema: domain is device.hostinfo.domain, dropped non-existent fields, added scenario/alleged_filetype/cid. 1.2.1 — Marked search_detections as an ingestion source (ingest hints: results_path=resources, dedup_key=composite_id) so the alert-rule editor pre-fills it. 1.2.0 — Added a pre-built OCSF mapper for search_detections (Falcon unified alerts → OCSF: device, finding, MITRE, actor/parent process, file and IOC). 1.1.1 — Added test_connection for the instance Test button. 1.1.0 — Expanded to 81 commands (host groups, cases, RTR files/scripts/responders, ML/IOA exclusions, quarantine, Spotlight host-by-vuln/CVE, ODS scans, CSPM, users, IOA rules, CNAPP, identity/mobile detection resolve, and workflows). 1.0.0 — Initial release: device/detection enrichment, Spotlight, IOC management, contain/lift, and core RTR."
category: endpoint category: endpoint
# Per-instance configuration. Scripts obtain an OAuth2 bearer token from # Per-instance configuration. Scripts obtain an OAuth2 bearer token from
@@ -74,13 +74,15 @@ commands:
risk: read risk: read
inputs_schema: inputs_schema:
properties: properties:
filter: { type: string, description: "FQL filter (default \"product:'epp'+type:'ldt'\"). For incremental fetch add e.g. created_timestamp:>'<ISO8601>'." } filter: { type: string, description: "FQL scoping filter (default \"product:'epp'+type:'ldt'\"). Say WHAT to fetch; leave the time bound to created_from." }
limit: { type: number, description: "Max alerts to return (default 50)." } created_from: { type: string, description: "Return alerts created at or after this timestamp (ISO 8601). Folded into the FQL as created_timestamp. Receives the alert rule's incremental watermark." }
limit: { type: number, description: "Max alerts to return (default 50). One page only — there is no pagination, so size it above the volume of one polling window." }
required: [] required: []
outputs_schema: { properties: {} } outputs_schema: { properties: {} }
ingest: ingest:
results_path: resources results_path: resources
dedup_key: composite_id dedup_key: composite_id
incremental_field: created_from
- id: list_host_groups - id: list_host_groups
name: crowdstrike-list-host-groups name: crowdstrike-list-host-groups
@@ -34,6 +34,15 @@ def csv(v):
def main(): def main():
tok = token() tok = token()
fql = INPUTS.get("filter") or "product:'epp'+type:'ldt'" fql = INPUTS.get("filter") or "product:'epp'+type:'ldt'"
# The alert rule's incremental watermark lands in created_from, NOT in filter.
# This API carries its time bound inside the FQL, so pointing the watermark at
# filter would REPLACE the whole query with a bare timestamp rather than narrow
# it — which the API rejects outright, and which would also silently discard the
# product/type scoping above. Its own input lets the two coexist: filter says
# what to look for, created_from says since when.
since = str(INPUTS.get("created_from") or "").strip().strip("'\"")
if since:
fql += "+created_timestamp:>'" + since + "'"
limit = INPUTS.get("limit", 50) limit = INPUTS.get("limit", 50)
params = [("filter", fql), ("limit", limit), ("sort", "created_timestamp.desc")] params = [("filter", fql), ("limit", limit), ("sort", "created_timestamp.desc")]
found = call("GET", "/alerts/queries/alerts/v2", tok, params=params) found = call("GET", "/alerts/queries/alerts/v2", tok, params=params)