fix(webhook): block SSRF to internal addresses in callback URL validation#43
Conversation
ogazboiz
left a comment
There was a problem hiding this comment.
SSRF guard is solid. DNS resolution at both registration and delivery time covers the DNS rebinding vector. Comprehensive IP range checks for IPv4 and IPv6 (including IPv4-mapped). Tests cover the key scenarios. Docs are clear. LGTM.
|
This PR has been approved but has merge conflicts with the base branch. Please rebase your branch on the latest |
347bbd6 to
235f7aa
Compare
|
Hi @ogazboiz I've rebased on the latest main and resolved the conflict in indexerController.ts. All good to merge |
ogazboiz
left a comment
There was a problem hiding this comment.
re-confirming after your rebase: the SSRF guard is still solid (DNS resolution at both registration and delivery, full IPv4/IPv6 range checks incl IPv4-mapped) and the indexerController conflict resolution is clean. note: the repo's ci is currently red for reasons unrelated to your pr (a separate lint/typecheck/test mess on main from earlier bad merges, i'm cleaning it up separately) so i'm merging on the approved logic + mergeable state. thanks for the solid work.
Summary
Closes #3. Webhook callback URL registration previously only checked that the
URL parsed and used http/https, letting an authenticated caller point the
indexer at internal services or cloud metadata endpoints (SSRF).
Changes
src/utils/ssrfGuard.tswithassertCallbackUrlAllowed()— validates thescheme, resolves the host via DNS, and rejects any address in a
loopback, link-local (incl.
169.254.169.254), RFC1918, CGNAT, or otherreserved range (IPv4 and IPv6, including IPv4-mapped).
indexerController.ts(returns400on a blocked URL).webhookService.ts(
postWebhook), defending against DNS rebinding on both initial dispatch andretries.
docs/webhook-url-policy.md.Tests
src/utils/__tests__/ssrfGuard.test.tscovers127.0.0.1,169.254.169.254, a10.xhost, IPv6, public addresses, and multi-addressresolution.
npm test -- ssrfGuard webhookService→ 33/33 passing;npm run lint→ 0errors.