feat(rapid7-insightidr): ingest only the alerts that belong to an investigation (v1.3.0)
Ingesting from search_alerts brings back every alert the tenant produces, including the operational noise — two of the first three sampled were an agent reporting it lacked Full Disk Access. What is worth an incident is what Rapid7 or the analyst folded into an investigation. Filtering search_alerts on investigation_rrn does not work. The term is accepted — no validation error, HTTP 200 — and matches nothing: zero alerts over fifteen days, on a tenant where an alert carrying an investigation_rrn provably exists in that window. The field is not searchable, and the API says so by returning an empty page rather than by refusing the term. That failure mode is the reason not to build an ingestion feed on it: a filter that silently stops matching drains the feed to zero with no error anywhere, and nothing would ever say why. search_investigation_alerts is guaranteed by construction instead: 1. the investigations in the window (incremental on their created_time) 2. their alert RRNs, one call per investigation — there is no bulk form 3. the alerts themselves via /alerts/ops/rrns, batched 100 at a time So 1+N+1 calls, and an alert with no investigation is never even named. Its failure modes are loud: an unreadable investigation is skipped and counted, and HTTP errors surface as themselves. Step 2 is the one that grows with the window, so it runs under a time budget (60s, against the server's 90s ceiling) and reports truncated=true rather than being killed with the whole run's alerts lost. A partial fetch that says it is partial beats a silent zero — the same reasoning that rules out the filter. The output shape matches search_alerts exactly, so the mapper committed in 1.2.0 serves both. It gains five entries for the parent investigation, which resolve to nothing on the search_alerts feed and are skipped there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
name: "Rapid7 InsightIDR Alerts → OCSF"
|
||||
description: |
|
||||
Maps one Rapid7 alert from the Alert Triage API (search_alerts, results_path = alerts)
|
||||
to OCSF finding fields.
|
||||
Maps one Rapid7 alert from the Alert Triage API to OCSF finding fields. Serves both
|
||||
search_alerts and search_investigation_alerts (results_path = alerts on either): the
|
||||
alert objects are identical, and the second command simply carries the parent
|
||||
investigation alongside under `investigation`.
|
||||
|
||||
This mapper exists because the investigation feed cannot carry a detection. An
|
||||
investigation, and the /investigations/{rrn}/alerts list behind it, hold a title, a
|
||||
@@ -113,3 +115,12 @@ ocsf:
|
||||
# Not investigable on its own, but it is the handle a LEQL query needs to pull the raw
|
||||
# event out of Log Search — the only bridge from an alert back to the line that made it.
|
||||
- { source_path: "log_details[0].log_entry_id", ocsf_field: "metadata.uid" }
|
||||
# ── The parent investigation ──────────────────────────────────────
|
||||
# Present only on the search_investigation_alerts feed, which carries the container
|
||||
# beside the alert. Absent on search_alerts, where these resolve to nothing and are
|
||||
# skipped — the same file serves both.
|
||||
- { source_path: "investigation.title", ocsf_field: "finding_info.related_events.title" }
|
||||
- { source_path: "investigation.status", ocsf_field: "finding_info.related_events.status" }
|
||||
- { source_path: "investigation.priority", ocsf_field: "finding_info.related_events.priority" }
|
||||
- { source_path: "investigation.disposition", ocsf_field: "finding_info.related_events.disposition" }
|
||||
- { source_path: "investigation.rrn", ocsf_field: "finding_info.related_events.uid" }
|
||||
|
||||
Reference in New Issue
Block a user