Skip to content

Validate and sandbox merchant webhook URLs to prevent SSRF from webhook delivery - #538

Open
EmmanuelAdah wants to merge 11 commits into
ZyntariHQ:mainfrom
EmmanuelAdah:manuel-adah
Open

Validate and sandbox merchant webhook URLs to prevent SSRF from webhook delivery#538
EmmanuelAdah wants to merge 11 commits into
ZyntariHQ:mainfrom
EmmanuelAdah:manuel-adah

Conversation

@EmmanuelAdah

@EmmanuelAdah EmmanuelAdah commented Aug 30, 2026

Copy link
Copy Markdown

Summary

Closes SSRF and internal port-scanning vulnerabilities in merchant webhooks by implementing defense-in-depth URL validation, request-time DNS pinning to prevent rebinding, safe bounded redirects, and API error sanitization.

Type of Change

  • Feature
  • Fix
  • Refactor
  • Documentation
  • Chore

Description

Write-time Validation: Added @IsSafeWebhookUrl() decorator and SsrfProtectionService.assertSafeForWrite() to enforce HTTPS, forbid embedded credentials, and block private/reserved IP ranges on save.

Request-time Enforcement: Replaced raw axios calls with SafeWebhookHttpService. Outbound calls re-resolve hostnames against an IP blocklist (ipaddr.js) and pin the socket to the validated IP to defeat DNS rebinding.

Bounded Redirects: Disabled auto-redirects (maxRedirects: 0). Redirect locations are individually extracted and re-validated per hop up to WEBHOOK_MAX_REDIRECTS (default: 3).

Error Sanitization: Collapsed API-facing delivery errors into generic categories (invalid_destination, unreachable, etc.) to prevent internal network fingerprinting.

Dev Escape Hatch: Added ALLOW_LOCAL_WEBHOOKS=true config (default: false) to allow local/CI testing against loopback addresses.

Testing Evidence

Ran full automated test suite locally:

Bash
pnpm test

Result: 41 test suites passed, 480 tests passed

Automated Coverage: Added unit/integration tests for IP blocklists (loopback, RFC1918, cloud metadata 169.254.169.254, IPv6), write/request-time DNS resolution, DNS-rebinding attack simulations, redirect-to-private-IP blocks, and error-sanitization paths.

Manual Verification: Verified that saving webhook URLs pointing to 127.0.0.1, 10.0.0.5, 169.254.169.254, or http:// endpoints triggers a 400 Bad Request. Verified ALLOW_LOCAL_WEBHOOKS=true permits http://localhost only when explicitly enabled.

Screenshots
N/A (Backend-only change)

Checklist

  • Code builds successfully
  • Tests added/updated where applicable
  • Documentation updated if needed
  • Linked issue referenced
  • Ready for review

@EmmanuelAdah

EmmanuelAdah commented Aug 30, 2026

Copy link
Copy Markdown
Author

Review this PR for any update needed

@Cedarich

Copy link
Copy Markdown
Contributor

Kindly fix CI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants