diff --git a/integrations/rapid7-insightidr/manifest.yaml b/integrations/rapid7-insightidr/manifest.yaml index 001a9c9..33a692b 100644 --- a/integrations/rapid7-insightidr/manifest.yaml +++ b/integrations/rapid7-insightidr/manifest.yaml @@ -1,8 +1,8 @@ id: rapid7_insightidr name: Rapid7 InsightIDR -version: 1.2.0 +version: 1.2.1 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, custom threat indicators (add/replace), log management and LEQL log/log-set queries with downloads, and user directory search." -changelog: "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." +changelog: "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 # Per-instance configuration. The Insight platform region selects the API host @@ -337,7 +337,7 @@ commands: search_field: { type: string, description: "Comma-separated field ids for a single search term" } search_values: { type: string, description: "Comma-separated values the search_field must match (OR'ed together)" } search_operator: { type: string, description: "EQUALS, NOT_EQUALS, CONTAINS, GREATER_THAN or LESS_THAN (default EQUALS)" } - terms_json: { type: string, description: "Raw JSON array of search terms, for criteria the simple triple cannot express. Takes precedence over search_field/search_values." } + terms_json: { type: string, description: "Raw JSON array of search terms, for criteria the simple triple cannot express — e.g. [{\"field_ids\":[\"investigation_rrn\"],\"operator\":\"CONTAINS\",\"terms\":[\"rrn:investigation\"]}] to keep only alerts attached to an investigation. Paste the array alone, without the field name. Takes precedence over search_field/search_values." } sort_field: { type: string, description: "Field id to sort on" } sort_direction: { type: string, description: "ASC or DESC (default ASC)" } rrns_only: { type: string, description: "Return only alert RRNs instead of full details (true/false)" } diff --git a/integrations/rapid7-insightidr/scripts/search_alerts.py b/integrations/rapid7-insightidr/scripts/search_alerts.py index 9ef5bde..d07c009 100644 --- a/integrations/rapid7-insightidr/scripts/search_alerts.py +++ b/integrations/rapid7-insightidr/scripts/search_alerts.py @@ -54,8 +54,20 @@ def _terms(inp): """ raw = inp.get("terms_json") if raw: - parsed = json.loads(raw) if isinstance(raw, str) else raw - return parsed if isinstance(parsed, list) else [parsed] + if isinstance(raw, str): + try: + parsed = json.loads(raw) + except ValueError as e: + # json's own message ("Expecting value: line 1 column 1") names + # neither the input nor what it received, so an operator who + # pasted the field name along with the value — the obvious + # mistake, since both are shown together — gets a parser error + # with nothing to act on. Echo the head of what arrived. + head = raw[:80] + ("…" if len(raw) > 80 else "") + raise ValueError("terms_json is not valid JSON (" + str(e) + "). " + "Expected a JSON array of search terms, got: " + head) + return parsed if isinstance(parsed, list) else [parsed] + return raw if isinstance(raw, list) else [raw] field_ids = _list(inp.get("search_field")) values = _list(inp.get("search_values")) if not field_ids or not values: