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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ
This commit is contained in:
2026-10-11 21:07:52 +00:00
parent 4a339fda24
commit c5da537434
3 changed files with 119 additions and 3 deletions
+46 -2
View File
@@ -30,8 +30,10 @@ integrations/
│ └── <command-id>.py │ └── <command-id>.py
├── mappers/ # optional — bundled mapper templates, one per command ├── mappers/ # optional — bundled mapper templates, one per command
│ └── <command-id>.yaml │ └── <command-id>.yaml
└── incident-types/ # optional — bundled incident types ├── incident-types/ # optional — bundled incident types
└── <anything>.yaml │ └── <anything>.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 `<handle> · <integration name>`; 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 ## Well-known commands: the on-call router
Most command ids are an integration's own business. **Three are a contract**, and Most command ids are an integration's own business. **Three are a contract**, and
@@ -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=<that RRN>) 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("<user, host or IP>"),
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
File diff suppressed because one or more lines are too long