Skip to content

GHSA security-advisory webhook

This page is auto-generated from src/blocklist/webhook.ts. Do not edit by hand — see docs/dev/docs-auto-sync.md.

Inbound webhook from GitHub — real-time GHSA advisory publish / withdraw.

MethodPath
USE/-/blocklist/webhook/ghsa
POST/-/blocklist/webhook/ghsa

Mechanism: HMAC-SHA256 (GitHub webhook convention)

Transport: X-Hub-Signature-256 header (sha256=<hex>)

GitHub-style HMAC-SHA256 of the raw request body, encoded as sha256=<hex>. Constant-time hex comparison against the expected value. Identical scheme to GitHub’s own webhook signature.

No explicit dedup gate at the receiver. The downstream action is naturally idempotent (e.g. unbind on an already-unbound row is a no-op).

Retry posture is governed by the upstream’s webhook delivery contract. Upwarden responds 2xx on accepted deliveries (including known-no-op events) so the upstream stops retrying; transient failures return 5xx so the upstream re-delivers within its retry budget.

FieldTypeOptional
action"published" | "updated" | "performed" | "withdrawn" | "republished"no
security_advisoryGhsaAdvisoryno

No dispatch tables found in source.

The leading doc-comment block from the source file, reproduced verbatim. This is the canonical narrative explanation of the receiver — the auto-generated sections above are derived from it.

GHSA webhook receiver — #100 (#C1) real-time path.
Turns the existing 15-min delta poll into near-instant: GitHub fires a
`security_advisory` event when an advisory is published / updated /
withdrawn, we validate the signature and write straight to the blocklist.
Topology:
GitHub Webhook → POST /-/blocklist/webhook/ghsa
↓ HMAC-SHA256 signature check
↓ upsertGhsaAdvisory (published / updated / performed)
↓ withdrawGhsaAdvisory (withdrawn)
↓ setBlocklistLastSync("ghsa", now) // freshness signal
The poll cron (k8s/blocklist-fast-sync-cron.yml) stays running as a
backstop — if a webhook delivery is dropped (GitHub's at-most-once-ish
delivery is best-effort), the next 15-min sync picks up the miss. Same
holds for the daily full-sync. Defense in depth, not replacement.
Why this matters: the 24h → 30min reduction from the fast-sync poll
closed the bulk of the gap; this closes the long tail. The first
install of a freshly-flagged malware no longer waits up to 15 minutes
for the next poll to catch it. The cost is one new endpoint + one
shared secret.