json.loads answers a bad value with "Expecting value: line 1 column 1 (char 0)",
which names neither the input at fault nor what it actually received. The
command has three string inputs; nothing in that message says which one broke,
so the operator is left guessing.
The mistake it hides is the obvious one. Documentation shows the input name and
the value together, so `terms_json: [{...}]` gets pasted whole into the value
field, and the parser then chokes on the field name it was handed. Echoing the
first 80 characters back makes that visible immediately.
Also carry a worked example in the manifest — the term that keeps only alerts
attached to an investigation, which is the filter this input mainly exists for —
and say explicitly to paste the array alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An incident built from a Rapid7 investigation indexed zero entities. Nothing to
pivot on, nothing to correlate, and an AI analysis with nothing to read. The
reason turned out not to be the mapper but the feed: an investigation is an
envelope, and neither it nor its /alerts list carries a host, an account, a
process or a MITRE code.
The Alert Triage API does, in rule_keys_of_interest. On the alert behind the
investigation that started this, that field holds:
hostname NBSGIT01-26
process.name bcdedit.exe
process.cmd_line bcdedit /set {default} recoveryenabled No
process.exe_path C:\WINDOWS\System32\bcdedit.exe
parent_process.name cmd.exe
parent_process.cmd_line "C:\WINDOWS\system32\cmd.exe"
parent_process.exe_file.hashes.md5 e463e36104d22a2479046a7d4f620816
with rule.mitre_tcodes ["TA0040", "T1490"] — Impact / Inhibit System Recovery,
which is what disabling Windows recovery actually is. None of it was reachable
before.
It is a list of {key, values} pairs rather than an object, so every entry reads
through a JSONata predicate — rule_keys_of_interest[key='hostname'].values[0].
A key a rule does not emit resolves to nothing and is skipped, so one file
covers every detection rule.
Two details worth keeping:
- mitre_tcodes mixes tactics and techniques in one array ("TA0040" beside
"T1490"). They are split on the "TA" prefix so a tactic never lands in the
technique field, where it would name a technique that does not exist.
- rule.name is documented but absent from live ABA alerts, so the analytic
name falls back to `type`, which repeats the rule's display name.
Rapid7's own status, disposition and AI-suggested disposition ride as custom
fields. They cannot reach the incident's status and verdict columns — MapFields
accepts only title, severity, source, description and custom:* — but an analyst
can at least see that the source already closed the alert and what it decided.
The AI suggestion is exposed as a fact, never as a verdict.
Validated end to end against a live alert: field mapping, OCSF mapping and
entity extraction, which now yields the host and the parent process hash where
it previously yielded nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bare call answered HTTP 400:
root path with path depth greater than 1 is not supported for V2
without a search filter
Sending neither path nor path_depth asks the V2 fields endpoint to walk the
whole hierarchy from the root, which it refuses. That is exactly the call an
operator makes first — open the command, run it, see what the tenant exposes —
so the one entry point into this API failed on its most obvious use.
Default path_depth to 1 when neither path nor search is given: the top-level
nodes come back, and `path` drills down from there. Also expose the `search`
query parameter, which was missing from the command entirely and which lifts
the depth restriction — it is the way to look through the whole tree at once
for a field whose branch you do not know yet (search=hostname).
Found by running the command against a live tenant. The 400 also settles the
open question from v1.1.0: the API answered a validation error rather than
403, so the Alert Triage API is reachable with this key.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An ingested investigation carried almost nothing: title, priority, status,
assignee and timestamps. Not a host, not an account, not a command line — so
no entity was ever indexed, and neither the pivot graph nor the AI analysis
had anything to read. Fetching the investigation's alerts did not help: that
endpoint answers with the rule name (already the investigation's title), the
alert source and an alert RRN, and nothing else.
The detection content lives in a different API. The Alert Triage API is served
under /idr/at — its own base, next to /idr/v1 and /idr/v2 — and it is where
the evidence, the actors, the MITRE T-codes and the process trees actually
are. It was missing from this package entirely.
Six read commands:
search_alerts POST /alerts/ops/search
get_alert GET /alerts/{rrn}
get_alert_evidence GET /alerts/{rrn}/evidences
get_alert_actors GET /alerts/{rrn}/actors
get_alert_process_trees POST /alerts/{rrn}/process_trees/latest
list_alert_fields GET /alerts/2/fields
search_alerts is the one that changes the shape of the feed. It returns full
alert objects — rule with its mitre_tcodes, rule_keys_of_interest, the
AI-suggested disposition, the parent investigation RRN, the status transition
timings and the log entries behind the alert — and its start_time is required
by the API, which makes it a natural incremental watermark. So the alerts can
be read directly, paginated, with no per-investigation fan-out.
Two things worth knowing before using them:
- field_ids is not optional in practice. The API returns no additional
fields when it is empty, and those additional fields are where the
hostnames, accounts and command lines are. Their ids are per-tenant dotted
paths (process.cmd_line, …), which is what list_alert_fields is for.
/alerts/fields is deprecated, hence /alerts/2/fields.
- the whole API is gated on Managed Detection and Response with access to
the Alerts experience. A key that works on every investigation endpoint
can still be refused here, so 401 and 403 are reported with that named
explanation rather than as a bare status code — a licence boundary should
not read as a bug.
Written against the published OpenAPI spec (SIEM Alerts API 39.10.0); not yet
exercised against a live tenant.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
REST API integration for Rapid7 InsightIDR. Investigation ingestion
(list_investigations) with an exhaustive OCSF mapper and a bundled default
incident type, plus 18 commands across investigations (list/get/search/
create/update/assign/set-status/bulk-close), investigation alerts and Rapid7
product alerts, custom threat indicators (add/replace), log management and
LEQL log/log-set queries with downloads, and user directory search.
API v1/v2 selectable per instance (is_v2) and per command (api_version);
multi-customer query parameter supported on v2 calls.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>