diff --git a/integrations/crowdstrike/manifest.yaml b/integrations/crowdstrike/manifest.yaml index 92c2e4e..215088e 100644 --- a/integrations/crowdstrike/manifest.yaml +++ b/integrations/crowdstrike/manifest.yaml @@ -1,8 +1,8 @@ id: crowdstrike name: CrowdStrike Falcon -version: 1.6.1 +version: 1.6.2 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.6.1 — search_detections and search_ngsiem_detections return a query block next to resources: the FQL actually sent, the total Falcon counts for it and any errors the query carried. A search that matched nothing used to come back as a bare empty list, which could not be told apart from a wrong region, a filter Falcon read differently or an API client that sees no alerts. | 1.6.0 — New search_ngsiem_detections command for Next-Gen SIEM detections. Falcon files the detections of NG-SIEM correlation rules in the same unified Alerts API as the EDR, under product 'ngsiem', so search_detections' default epp/ldt scope never returned them, and its mapper reads device, process and file blocks an NG-SIEM detection does not carry. The new command always keeps the product:'ngsiem' clause and ANDs any filter to it, takes the same created_from watermark, and ships its own mapper and a CrowdStrike NG-SIEM Detection incident type. The mapper is a first cut on the fields every unified alert carries (identity, name, description, severity, status, timestamps, MITRE); hosts, users and the source log will follow from a real payload. | 1.5.0 — resolve_detection sets the verdict. Falcon has no verdict field on an alert: the console's Resolution column is read from the tags true_positive, false_positive and ignored, and the status only takes new, in_progress, reopened and closed — so no value of the old status box could record a verdict. The new verdict input adds the tag, after removing the other two so an alert never carries two resolutions, and status and verdict are now drop-downs. 1.4.0 — New device_count command: counts devices matching filters without listing them. The device query already answers with meta.pagination.total — the number of matches, independent of the page asked for — so device_count asks for a one-ID page and reads it, where search-device resolves every matching device in full through /devices/entities/devices/v2 to reach the same figure, and only filters on four properties. Filters cover sixteen device FQL properties (hostname, device id, local and external IP, platform, OS version, role, containment status, sensor version, AD domain/OU/site, serial, tags, host groups, RFM), add first_seen/last_seen bounds, and leave a raw FQL `filter` escape hatch for the rest. Several values on one property become an OR group, parenthesised so it binds before the AND that joins it to the others — the reference client omits those parentheses and leaves the precedence to the server. Values are quoted with the single quote escaped, so a hostname carrying an apostrophe cannot end the literal early and change what the query counts. The estate `total` comes back next to `count` so the figure reads as a proportion, and a filter too broad to mean anything shows as matching everything rather than passing for an answer. | 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." +changelog: "1.6.2 — search_ngsiem_detections reads products thirdparty and ngsiem by default, set by a new products input. On a real tenant the NG-SIEM detections of the Unified Detections page — here FortiGate IPS hits — are filed under product thirdparty, and product ngsiem matched nothing, so 1.6.0 returned an empty list. Its mapper is rewritten from those payloads: source product and vendor, the host Falcon matched the detection to, source and target IPs and ports, protocol, the vendor signature id as the analytic uid, the Falcon priority as risk score, the console link, and the source event time as OCSF time. Third-party detections carry no description, so the incident's is built from the source product, the signature and the connection. | 1.6.1 — search_detections and search_ngsiem_detections return a query block next to resources: the FQL actually sent, the total Falcon counts for it and any errors the query carried. A search that matched nothing used to come back as a bare empty list, which could not be told apart from a wrong region, a filter Falcon read differently or an API client that sees no alerts. | 1.6.0 — New search_ngsiem_detections command for Next-Gen SIEM detections. Falcon files the detections of NG-SIEM correlation rules in the same unified Alerts API as the EDR, under product 'ngsiem', so search_detections' default epp/ldt scope never returned them, and its mapper reads device, process and file blocks an NG-SIEM detection does not carry. The new command always keeps the product:'ngsiem' clause and ANDs any filter to it, takes the same created_from watermark, and ships its own mapper and a CrowdStrike NG-SIEM Detection incident type. The mapper is a first cut on the fields every unified alert carries (identity, name, description, severity, status, timestamps, MITRE); hosts, users and the source log will follow from a real payload. | 1.5.0 — resolve_detection sets the verdict. Falcon has no verdict field on an alert: the console's Resolution column is read from the tags true_positive, false_positive and ignored, and the status only takes new, in_progress, reopened and closed — so no value of the old status box could record a verdict. The new verdict input adds the tag, after removing the other two so an alert never carries two resolutions, and status and verdict are now drop-downs. 1.4.0 — New device_count command: counts devices matching filters without listing them. The device query already answers with meta.pagination.total — the number of matches, independent of the page asked for — so device_count asks for a one-ID page and reads it, where search-device resolves every matching device in full through /devices/entities/devices/v2 to reach the same figure, and only filters on four properties. Filters cover sixteen device FQL properties (hostname, device id, local and external IP, platform, OS version, role, containment status, sensor version, AD domain/OU/site, serial, tags, host groups, RFM), add first_seen/last_seen bounds, and leave a raw FQL `filter` escape hatch for the rest. Several values on one property become an OR group, parenthesised so it binds before the AND that joins it to the others — the reference client omits those parentheses and leaves the precedence to the server. Values are quoted with the single quote escaped, so a hostname carrying an apostrophe cannot end the literal early and change what the query counts. The estate `total` comes back next to `count` so the figure reads as a proportion, and a filter too broad to mean anything shows as matching everything rather than passing for an answer. | 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 # Per-instance configuration. Scripts obtain an OAuth2 bearer token from @@ -116,11 +116,12 @@ commands: - id: search_ngsiem_detections name: crowdstrike-search-ngsiem-detections - description: "Search Next-Gen SIEM detections — the alerts raised by NG-SIEM correlation rules, which Falcon files in the unified Alerts API under product 'ngsiem' rather than with the EDR detections. Returns full alert entities. Use as an alert rule ingestion source (results path: resources)." + description: "Search Next-Gen SIEM detections — what NG-SIEM raises from ingested third-party logs (product 'thirdparty', e.g. a FortiGate IPS hit) and from the tenant's correlation rules (product 'ngsiem'). Falcon files them in the unified Alerts API next to, not among, the EDR detections. Returns full alert entities. Use as an alert rule ingestion source (results path: resources)." risk: read inputs_schema: properties: - filter: { type: string, description: "Optional FQL narrowing the NG-SIEM detections, e.g. \"severity_name:'High'\". Always ANDed with product:'ngsiem'. Leave the time bound to created_from." } + products: { type: string, description: "Comma-separated Alerts API products to read (default \"thirdparty,ngsiem\"). Add xdr for NG-SIEM incidents or automated-lead for automated leads." } + filter: { type: string, description: "Optional FQL narrowing the detections, e.g. \"severity:>=40\" to drop Informational and Low. Always ANDed with the product clause. Leave the time bound to created_from." } created_from: { type: string, description: "Return detections 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 detections to return (default 50). One page only — there is no pagination, so size it above the volume of one polling window." } required: [] diff --git a/integrations/crowdstrike/mappers/search_ngsiem_detections.yaml b/integrations/crowdstrike/mappers/search_ngsiem_detections.yaml index 7699c32..6ea1d74 100644 --- a/integrations/crowdstrike/mappers/search_ngsiem_detections.yaml +++ b/integrations/crowdstrike/mappers/search_ngsiem_detections.yaml @@ -1,10 +1,12 @@ name: "CrowdStrike Next-Gen SIEM Detections → OCSF" -description: "First cut of the map of a Falcon Next-Gen SIEM detection (alerts/entities/alerts/v2 filtered on product 'ngsiem', results_path = resources) to OCSF. An NG-SIEM detection is raised by a correlation rule over ingested logs, not by the sensor: it carries no device, process or file block, so none of the EDR paths of search_detections apply. Only the fields every unified alert carries are mapped here; hosts, users, IPs and the source log come with the first real payload." +description: "Map of a Falcon Next-Gen SIEM detection (alerts/entities/alerts/v2 on products 'thirdparty' and 'ngsiem', results_path = resources) to OCSF. Read from real FortiGate IPS detections: a third-party detection carries no sensor process or file block, but names its source product and vendor, the source and target endpoints with ports and protocol, the host Falcon matched it to (enriched_entities) and the vendor's own signature id. It has no description: the incident's is built from the source product, the signature and the connection." field_mappings: title: "name" # Plain path: the ingestion engine reads informational/low/medium/high/critical natively. severity: "severity_name" - description: "description" + # Third-party detections carry no description of their own; say what fired, + # from which product, on which connection. + description: "jsonata: description ? description : $join(source_products, ', ') & ' — ' & name & (source_endpoint_address_ip4 ? ' — ' & source_endpoint_address_ip4 & (local_port ? ':' & local_port : '') & ' → ' & target_endpoint_address_ip4 & (remote_port ? ':' & remote_port : '') & (protocol ? ' (' & protocol & ')' : '') : '')" # results_path = resources; source_path is JSONata over ONE alert object. # Paths absent from a given alert are skipped at ingestion, so extra entries are safe. ocsf: @@ -12,15 +14,38 @@ ocsf: - { source_path: "composite_id", ocsf_field: "finding_info.uid" } - { source_path: "name", ocsf_field: "finding_info.title" } - { source_path: "description", ocsf_field: "finding_info.desc" } + - { source_path: "categorization", ocsf_field: "finding_info.types" } + - { source_path: "data_domains", ocsf_field: "finding_info.data_sources" } + - { source_path: "falcon_host_link", ocsf_field: "finding_info.src_url" } - { source_path: "created_timestamp", ocsf_field: "finding_info.created_time" } - - { source_path: "created_timestamp", ocsf_field: "time" } - { source_path: "updated_timestamp", ocsf_field: "finding_info.modified_time" } - - { source_path: "type", ocsf_field: "finding_info.types" } + # `time` is when the source product saw the event (timestamp), ahead of + # Falcon's own record of the detection (created_timestamp, ~20 s later). + - { source_path: "timestamp", ocsf_field: "time" } + # The vendor's signature id (FortiGate IPS 100012) identifies the rule; + # pattern_id is Falcon's generic third-party bucket (300000 for all of them). + - { source_path: "name", ocsf_field: "finding_info.analytic.name" } + - { source_path: "vendor_pattern_id", ocsf_field: "finding_info.analytic.uid" } - { source_path: "confidence", ocsf_field: "confidence" } + - { source_path: "priority_value", ocsf_field: "risk_score" } - { source_path: "status", ocsf_field: "status" } + # ── Source product — the log the detection came from ────────────── + - { source_path: "source_products[0]", ocsf_field: "metadata.product.name" } + - { source_path: "source_vendors[0]", ocsf_field: "metadata.product.vendor_name" } - { source_path: "product", ocsf_field: "metadata.product.feature.name" } - { source_path: "cid", ocsf_field: "cloud.account.uid" } - # ── MITRE ATT&CK ────────────────────────────────────────────────── + # ── Host Falcon matched the detection to ────────────────────────── + - { source_path: "agent_id", ocsf_field: "device.uid" } + - { source_path: "enriched_entities.hostnames[0]", ocsf_field: "device.hostname" } + # ── Connection ──────────────────────────────────────────────────── + - { source_path: "source_endpoint_address_ip4 ? source_endpoint_address_ip4 : source_ips[0]", ocsf_field: "src_endpoint.ip" } + - { source_path: "source_hosts[0]", ocsf_field: "src_endpoint.hostname" } + - { source_path: "$number(local_port)", ocsf_field: "src_endpoint.port" } + - { source_path: "target_endpoint_address_ip4 ? target_endpoint_address_ip4 : destination_ips[0]", ocsf_field: "dst_endpoint.ip" } + - { source_path: "$number(remote_port)", ocsf_field: "dst_endpoint.port" } + - { source_path: "protocol", ocsf_field: "connection_info.protocol_name" } + - { source_path: "users[0]", ocsf_field: "actor.user.name" } + # ── MITRE ATT&CK (correlation rules that declare it) ────────────── - { source_path: "tactic", ocsf_field: "attacks.tactic.name" } - { source_path: "tactic_id", ocsf_field: "attacks.tactic.uid" } - { source_path: "technique", ocsf_field: "attacks.technique.name" } diff --git a/integrations/crowdstrike/scripts/search_ngsiem_detections.py b/integrations/crowdstrike/scripts/search_ngsiem_detections.py index 57d1844..23315e7 100644 --- a/integrations/crowdstrike/scripts/search_ngsiem_detections.py +++ b/integrations/crowdstrike/scripts/search_ngsiem_detections.py @@ -33,10 +33,14 @@ def csv(v): def main(): tok = token() - # Next-Gen SIEM correlation rules raise their detections in the same unified - # Alerts API as the EDR, under product 'ngsiem'. The product clause is always - # kept, so a filter narrows the NG-SIEM detections and never widens past them. - fql = "product:'ngsiem'" + # Next-Gen SIEM files its detections in the same unified Alerts API as the + # EDR, under two products: 'thirdparty' for the alerts it raises from ingested + # third-party logs (a FortiGate IPS hit, say) and 'ngsiem' for those of the + # tenant's own correlation rules. The product clause is always kept, so a + # filter narrows these detections and never widens past them. + products = [p.strip().strip("'\"") for p in str(INPUTS.get("products") or "thirdparty,ngsiem").split(",") if p.strip()] + # ',' is OR in FQL; parenthesised so the group binds before the '+' ANDs. + fql = "(" + ",".join("product:'" + p.replace("'", "\\'") + "'" for p in products) + ")" extra = str(INPUTS.get("filter") or "").strip() if extra: fql += "+(" + extra + ")"