feat(webhook): accept standard webhook signatures
This commit is contained in:
@@ -548,8 +548,8 @@ class WebhookAdapter(BasePlatformAdapter):
|
||||
prompt = self._render_prompt(route_config.get("prompt", ""), payload, event_type, route_name)
|
||||
if skills := route_config.get("skills", []):
|
||||
prompt = self._apply_skills(prompt, skills)
|
||||
delivery_id = headers.get("X-GitHub-Delivery", headers.get(
|
||||
"svix-id", headers.get("X-Request-ID", str(int(time.time() * 1000)))))
|
||||
delivery_id = headers.get("X-GitHub-Delivery", headers.get("svix-id", headers.get(
|
||||
"webhook-id", headers.get("X-Request-ID", str(int(time.time() * 1000))))))
|
||||
now = time.time() # idempotency: skip duplicate deliveries (webhook retries)
|
||||
if not self._record_delivery_id(delivery_id, now):
|
||||
logger.info("[webhook] Skipping duplicate delivery %s", delivery_id)
|
||||
@@ -617,14 +617,17 @@ class WebhookAdapter(BasePlatformAdapter):
|
||||
# --- Signature validation ---
|
||||
|
||||
def _validate_signature(self, request: "web.Request", body: bytes, secret: str) -> bool:
|
||||
"""Validate webhook signature (GitHub, GitLab, Svix, Linear, generic HMAC-SHA256)."""
|
||||
"""Validate webhook signature (GitHub, GitLab, Svix, Standard Webhooks, Linear, generic HMAC-SHA256)."""
|
||||
headers = request.headers
|
||||
|
||||
def _header(name: str) -> str:
|
||||
return headers.get(name, "") or headers.get(name.lower(), "") or headers.get(name.upper(), "")
|
||||
|
||||
# Svix / AgentMail: signed content is "{id}.{timestamp}.{raw_body}".
|
||||
# Svix / AgentMail: signed content is "{id}.{timestamp}.{raw_body}". Standard Webhooks
|
||||
# (webhook-*; GitLab signing tokens) is the same scheme under other header names.
|
||||
svix = [_header(name) for name in ("svix-id", "svix-timestamp", "svix-signature")]
|
||||
if not any(svix):
|
||||
svix = [_header(name) for name in ("webhook-id", "webhook-timestamp", "webhook-signature")]
|
||||
if any(svix):
|
||||
return _validate_svix_signature(body, secret, *svix)
|
||||
# Linear (any header case): hex HMAC of the body. GitHub: sha256=<hex>. GitLab: plain token.
|
||||
|
||||
@@ -498,6 +498,7 @@ The adapter validates incoming webhook signatures using the appropriate method f
|
||||
|
||||
- **GitHub**: `X-Hub-Signature-256` header — HMAC-SHA256 hex digest prefixed with `sha256=`
|
||||
- **GitLab**: `X-Gitlab-Token` header — plain secret string match
|
||||
- **Standard Webhooks**: `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers — signed content is `{id}.{timestamp}.{raw_body}` with a `v1,<base64-hmac-sha256>` signature
|
||||
- **Generic (V2, recommended)**: `X-Webhook-Signature-V2` + `X-Webhook-Timestamp` headers — HMAC-SHA256 hex digest of `<timestamp>.<body>`. The timestamp (Unix seconds) must be within ±300 seconds of the server clock, which prevents captured requests from being replayed later.
|
||||
- **Generic (V1, legacy)**: `X-Webhook-Signature` header — raw HMAC-SHA256 hex digest of the body only. Still accepted for backward compatibility, but it has no replay protection (a captured request replays indefinitely); the gateway logs a deprecation warning once per route. Switch senders to V2.
|
||||
|
||||
@@ -529,7 +530,7 @@ Requests exceeding the limit receive a `429 Too Many Requests` response.
|
||||
|
||||
### Idempotency
|
||||
|
||||
Delivery IDs (from `X-GitHub-Delivery`, `X-Request-ID`, or a timestamp fallback) are cached for **1 hour**. Duplicate deliveries (e.g. webhook retries) are silently skipped with a `200` response, preventing duplicate agent runs.
|
||||
Delivery IDs (from `X-GitHub-Delivery`, `svix-id`, `webhook-id`, `X-Request-ID`, or a timestamp fallback) are cached for **1 hour**. Duplicate deliveries (e.g. webhook retries) are silently skipped with a `200` response, preventing duplicate agent runs.
|
||||
|
||||
### Body size limits
|
||||
|
||||
@@ -588,7 +589,7 @@ This is the same trust model that applies to everything the agent reads: web pag
|
||||
|
||||
### Duplicate responses
|
||||
|
||||
- The idempotency cache should prevent this — check that the webhook source is sending a delivery ID header (`X-GitHub-Delivery` or `X-Request-ID`)
|
||||
- The idempotency cache should prevent this — check that the webhook source is sending a delivery ID header (`X-GitHub-Delivery`, `svix-id`, `webhook-id`, or `X-Request-ID`)
|
||||
- Delivery IDs are cached for 1 hour
|
||||
|
||||
### `gh` CLI errors (GitHub comment delivery)
|
||||
|
||||
Reference in New Issue
Block a user