From c5da537434c7238aa2e040f3e5636f5ce60274b4 Mon Sep 17 00:00:00 2001 From: Guillaume BOURGEOIS Date: Sun, 11 Oct 2026 21:07:52 +0000 Subject: [PATCH] rapid7-insightidr 1.14.0: ship Static, the InsightIDR operator agent agents/operator.yaml: read-only commands, analyst_l2 ceiling, and the procedure learned on a real UBA alert (start from the investigation's alerts, plain-UUID alerts have no Alert Triage evidence, LEQL keyword search in parentheses, search_investigation takes start_time/end_time). README documents agents/. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ --- README.md | 48 ++++++++++++- .../rapid7-insightidr/agents/operator.yaml | 72 +++++++++++++++++++ integrations/rapid7-insightidr/manifest.yaml | 2 +- 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 integrations/rapid7-insightidr/agents/operator.yaml diff --git a/README.md b/README.md index d9d9edd..73f9daf 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,10 @@ integrations/ │ └── .py ├── mappers/ # optional — bundled mapper templates, one per command │ └── .yaml - └── incident-types/ # optional — bundled incident types - └── .yaml + ├── incident-types/ # optional — bundled incident types + │ └── .yaml + └── agents/ # optional — the operator agent (Riposte 0.302.0+) + └── operator.yaml ``` --- @@ -228,6 +230,48 @@ named by field, evaluated against the rule's last captured samples. --- +## Bundled operator agents (`agents/*.yaml`) + +An integration can ship the **operator agent** that knows how to use it. +Installing the integration in Riposte (0.302.0 and later) creates the agent in the +crew, named ` · `; older versions ignore the folder. +The sheet carries what took a real investigation to learn — the vendor's traps, +the order to call things in — so nobody has to rebuild it by hand. + +```yaml +key: operator # stable id within the package; one agent per key +handle: Static # an invented netrunner handle, never an existing character +title: Rapid7 InsightIDR operator +role: analyst_l2 # must hold integrations.execute +approval_threshold: safe_write +max_tool_calls: 10 +when_to_delegate: | # the only thing an orchestrator reads to route work to it + … +needs: | + … +returns: | + … +instructions: | # the vendor know-how: procedure and traps + … +commands: [list_investigation_alerts, get_alert_actors] # command ids of THIS manifest +riposte_tools: [get_incident, get_incident_context] +``` + +Riposte **refuses the sheet** — and still installs the integration — when it: + +- lists a command this manifest does not declare, or one that is not `risk: read` + (an omitted risk counts as destructive); +- lists a Riposte tool that is not read-tier; +- names a role without `integrations.execute`. `viewer`, `auditor`, + `mcp_readonly` and `analyst_l1` lack it, and with them every integration + command would be invisible to the agent; +- has no `key`, `handle`, `role`, `when_to_delegate` or command. + +The model is not part of the sheet: operators borrow Riposte's **Crew** role. A +sheet nobody edited follows the package's updates; an edited one keeps its edit +and offers the new version for review. Removing a sheet from the package retires +its agent on the next install. + ## Well-known commands: the on-call router Most command ids are an integration's own business. **Three are a contract**, and diff --git a/integrations/rapid7-insightidr/agents/operator.yaml b/integrations/rapid7-insightidr/agents/operator.yaml new file mode 100644 index 0000000..b140402 --- /dev/null +++ b/integrations/rapid7-insightidr/agents/operator.yaml @@ -0,0 +1,72 @@ +# The Rapid7 InsightIDR operator. Installed with the integration by Riposte +# 0.302.0 and later (older versions ignore this folder). Read-only by design: +# Riposte refuses a sheet that lists a write. +key: operator +handle: Static +title: Rapid7 InsightIDR operator +role: analyst_l2 # needs integrations.execute — viewer would hide every command +approval_threshold: safe_write +max_tool_calls: 10 + +when_to_delegate: | + Anything an InsightIDR investigation can answer: the alerts it holds, the + assets, accounts and IPs involved, the evidence that triggered them, process + trees, other alerts or investigations for the same user or host, and log + searches in InsightIDR log sets. Not for other vendors' alerts. + +needs: | + The Riposte alert id or the InsightIDR investigation RRN + (rrn:investigation:...). For a log search: the user, host or IP, and a time + window. + +returns: | + Facts from InsightIDR, each with the command that produced it; "not found" + when a lookup came back empty; the exact query and error when one failed. + No verdict. + +instructions: | + ALWAYS start here, whatever you were asked: + 1. Read the Riposte alert with get_incident_context. Its source_ref is the + investigation RRN (rrn:investigation:...), never an alert RRN. + 2. list_investigation_alerts(investigation_id=) gives the + investigation's alerts. + 3. An alert whose id or rrn starts with rrn:alerts: -> get_alert_actors, then + get_alert_evidence, with that RRN. get_alert_process_trees only for + endpoint (process) alerts. + 4. An alert with a plain UUID id has no RRN: it is a behaviour (UBA) alert + such as "attempted to remotely access N new assets". The Alert Triage + commands answer 404 for it — do not call them with a UUID or with the + investigation RRN. Go to the logs instead (below). + Only then answer the question you were asked. + + Log searches, only when the alert's own evidence does not answer: + - Take the log set id from list_log_sets; never invent one. + - LEQL keyword search, no field names: where(""), + e.g. where("jdoe") + Always parentheses, always quotes. "where x = y" without parentheses is + rejected as Invalid Query Syntax. No groupby or calculate. + - Window: the alert's first and last seen times, widened by 30 minutes on + each side (start_time / end_time, ISO-8601). + - If a query returns "Invalid Query Syntax", do not try variants: report the + exact query and the error. + + Same user or host elsewhere: search_investigation with actor_user_name or + actor_asset_hostname. It takes start_time / end_time, not time_range; left + empty it covers the last 28 days. + +commands: + - list_investigation_alerts + - get_investigation + - search_investigation + - get_alert + - get_alert_actors + - get_alert_evidence + - get_alert_process_trees + - search_alerts + - list_users + - list_log_sets + - query_log_set + +riposte_tools: + - get_incident + - get_incident_context diff --git a/integrations/rapid7-insightidr/manifest.yaml b/integrations/rapid7-insightidr/manifest.yaml index b558a41..8fd5415 100644 --- a/integrations/rapid7-insightidr/manifest.yaml +++ b/integrations/rapid7-insightidr/manifest.yaml @@ -1,6 +1,6 @@ id: rapid7_insightidr name: Rapid7 InsightIDR -version: 1.13.2 +version: 1.14.0 description: "Rapid7 InsightIDR (REST API) — investigation ingestion and full lifecycle (list/get/search/create/update/assign/set-status/bulk-close), investigation alerts and Rapid7 product alerts, investigation comments (add/list), custom threat indicators (add/replace), log management and LEQL log/log-set queries with downloads, and user directory search." changelog: "1.13.2 — query_log and query_log_set: trim the quotes and spaces a pasted log or log set ID drags along, and explain the 404. A trailing quote was URL-encoded into the path and the API answered a bare \"HTTP 404 Not Found\" that named nothing. Also send both time bounds on every run: a scheduled run passes only the watermark, and the first run nothing — the window now ends now and, without a start, opens 24 hours back. | 1.13.1 — test_connection: accept an InsightIDR Essential key. Essential answers the Investigations API with a 403 that names the package, so a valid key failed its test; the test now falls back to Log Search and says which commands that package leaves out — the LEQL feeds (query_log, query_log_set) are its way to ingest detections. | 1.13.0 — Add add_comment and list_comments: the investigation comment thread is where an analyst’s reasoning lives, and a playbook had no way to write into it or read it back. The Comments API is v1-only and addresses its target by RRN — the short investigation ID the other v1 endpoints accept is rejected with a 400 that names nothing, so both commands refuse it up front. | 1.12.0 — Make the LEQL queries ingestible: query_log and query_log_set gain an ingest block, a stable event_id for dedup, a bounded page walk and a detection_name stamp, and they now read start_time as ISO-8601, UNIX seconds or milliseconds alike. A tenant whose basic detection rules only notify (email, Slack) creates no investigation and no alert an API can read, so replaying the rule's own query on a schedule is the only way its detections reach a SOAR. | 1.11.1 — Strip the whitespace out of the comma-separated filter params (statuses, priorities, sources, tags) before the URL. \"HIGH, CRITICAL\" as typed sent \" CRITICAL\" to the API, which rejects the whole call — so a priority filter written naturally fetched nothing. | 1.11.0 —Build one evidence row per alert evidence into custom:rapid7_evidence, for the table field: sortable and filterable where the Markdown summary only reads. | 1.10.0 — Render every detection key as Markdown into custom:rapid7_summary, so a layout can show what the OCSF catalogue has no field for. Map both ends of an account action, read source_json top-level scalars, drop placeholder values, and force evidences to stay an array. | 1.9.2 — Read the evidence data in both of its shapes. A parsed source states its event flat, with no source_data/custom_data wrapper, and the parser returned {} for all of them. | 1.9.1 — get_alert_process_trees: explain the 404, which the API returns with an empty body for three different situations. | 1.9.0 — Fetch each alert process tree and flatten it into ctx.process_tree, the shape the layout Process Tree block reads. Gated on the event type by default, since an authentication alert never has one. | 1.8.0 — Fetch each alert evidence at ingestion and parse it: the log line behind the alert lives in a JSON-encoded string no mapper can reach, and it is where the source and destination addresses, the account and the device actually are. | 1.7.0 — Map source/destination assets and accounts to src_endpoint, dst_endpoint, actor.user and user. A connection rule names both ends and neither is \"the host\", so an RDP-to-DC alert produced no entity at all and nothing a layout could show. | 1.6.0 — Surface region_failures: the Alert Triage API answers HTTP 200 with a partial result when it cannot reach a region, and that list was being dropped. Also report the regions seen in the RRNs against the configured one. | 1.5.0 — Flatten the whole of rule_keys_of_interest into the context. It is an open vocabulary — a process rule and an authentication rule share no keys — so the hardcoded list only covered rules already seen and left every other alert empty. | 1.4.1 — Read the alert RRN from `rrn` before `id` and drop anything not RRN-shaped: the investigation alert list also returns bare UUIDs, and one of those rejects the entire /alerts/ops/rrns batch (HTTP 400), losing every alert in the run. | 1.4.0 — Add get_investigations_full: the investigation with its alerts inlined, so it stays the unit that becomes an incident. Drop-in for list_investigations (results_path data, dedup rrn). | 1.3.0 — Add search_investigation_alerts: ingest only the alerts that belong to an investigation, with their full detection content, in 1+N+1 calls. Filtering search_alerts on investigation_rrn does not work — the term is accepted and silently matches nothing. | 1.2.1 — search_alerts: report a malformed terms_json with the input named and echoed, instead of json's bare \"Expecting value: line 1 column 1\". | 1.2.0 — Add the search_alerts OCSF mapper: rule_keys_of_interest carries the host, the process, its command line, its parent and the file hashes, so an alert-based feed indexes entities where the investigation feed indexed none. Validated against a live ABA alert. | 1.1.1 — list_alert_fields: default path_depth to 1 and expose the search filter. The V2 fields endpoint refuses an unbounded walk of the tree (\"root path with path depth greater than 1 is not supported for V2 without a search filter\"), so the bare call returned HTTP 400. | 1.1.0 — Add the Alert Triage API (/idr/at): search alerts, get an alert, its evidence, its actors and its process trees, plus the tenant's alert field catalogue. These carry the detection content the investigation endpoints do not — MITRE T-codes, matched rule keys, assets and accounts, command lines. MDR-gated. | 1.0.0 — Initial release: investigation ingestion (list_investigations) with an exhaustive OCSF mapper, 18 commands across investigations, alerts, custom threats, logs/LEQL search and users. API v1/v2 selectable per instance and per command." category: siem