6b76270cc6
Riposte accepts no inbound connection, so a vendor that can only push (Rapid7 InsightIDR Essential basic detection rules) posts to the relay, and an alert rule on this integration reads one relay source back over outbound HTTPS. fetch_messages takes the rule's watermark as `since` and walks every page within the run, since the next watermark is this run's instant. A window holding more than `limit` fails the run instead of truncating, so the watermark stays put. relay_status and test_connection tell a 404 from the proxy (this Riposte's address is not listed) from a refused token. The generic mapper keeps the raw body, the sender's address and the checks the delivery passed (custom:webhook_verified). Tested against a local relay: 2503 messages over three pages, a millisecond watermark, the limit, unknown source and bad token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xa1QDbsPTYiVREv2Cvs3Zn
36 lines
2.2 KiB
YAML
36 lines
2.2 KiB
YAML
name: "Webhook Relay message (generic)"
|
|
description: |
|
|
Maps one relayed message — whatever the vendor sent — to an alert, without knowing
|
|
the vendor. It is the mapper a new source starts with: the alert exists, the raw
|
|
payload is readable in Riposte, and a vendor mapper is then written from it
|
|
(clone this one, or pick the vendor's own when the marketplace ships it).
|
|
|
|
A message is {id, source, received_at, remote_ip, content_type, headers, verified,
|
|
body | body_b64, json}. `json` is the body already decoded when it is JSON, so a
|
|
vendor field reads json.<field>.
|
|
|
|
The title tries the fields alert payloads most often name themselves with, and
|
|
falls back to the source. Severity is left to the alert rule's default: a generic
|
|
payload has no scale this mapper could trust.
|
|
|
|
custom:webhook_verified is the trust level: the checks the delivery passed at the
|
|
relay (path_token, header_token, hmac, ip). An alert authenticated by a URL alone
|
|
is plausible, not proven — declare the field and keep destructive automation off
|
|
such alerts until a playbook has confirmed them against the vendor.
|
|
field_mappings:
|
|
title: "jsonata: $exists(json.title) ? $string(json.title) : $exists(json.name) ? $string(json.name) : $exists(json.alert.name) ? $string(json.alert.name) : $exists(json.rule.name) ? $string(json.rule.name) : $exists(json.detection_name) ? $string(json.detection_name) : ('Webhook · ' & source)"
|
|
source: "jsonata: 'Webhook relay · ' & source"
|
|
description: "jsonata: body ? $substring(body, 0, 8000) : ('Binary body, ' & $string($length(body_b64)) & ' base64 characters')"
|
|
"custom:webhook_source": "source"
|
|
"custom:webhook_verified": "jsonata: $join(verified, ',')"
|
|
"custom:webhook_message_id": "id"
|
|
"custom:webhook_received_at": "received_at"
|
|
"custom:webhook_sender_ip": "remote_ip"
|
|
# Key/value rows of the decoded payload's top level, for a table field.
|
|
"custom:webhook_payload": "jsonata: json ? $toTable(json) : body"
|
|
ocsf:
|
|
- { source_path: "id", ocsf_field: "finding_info.uid" }
|
|
- { source_path: "id", ocsf_field: "metadata.uid" }
|
|
- { source_path: "received_at", ocsf_field: "time" }
|
|
- { source_path: "source", ocsf_field: "metadata.product.name" }
|