Skip to content

fix(provider-utils): prevent SSRF bypass via DNS resolution in validateDownloadUrl#13718

Closed
majiayu000 wants to merge 2 commits intovercel:mainfrom
majiayu000:fix/issue-13510-ssrf-dns-bypass
Closed

fix(provider-utils): prevent SSRF bypass via DNS resolution in validateDownloadUrl#13718
majiayu000 wants to merge 2 commits intovercel:mainfrom
majiayu000:fix/issue-13510-ssrf-dns-bypass

Conversation

@majiayu000
Copy link

Background

validateDownloadUrl() only validates literal IP addresses against private ranges. When a hostname is provided, it skips IP validation entirely, allowing DNS-based SSRF bypass where an attacker-controlled domain resolves to internal IPs (e.g., evil.com → 169.254.169.254).

Additionally, several private IPv4 ranges were missing from the blocklist.

Summary

Part 1 — Add missing private IPv4 ranges to isPrivateIPv4():

  • 100.64.0.0/10 (Carrier-Grade NAT, RFC 6598)
  • 198.18.0.0/15 (Benchmarking, RFC 2544)
  • 240.0.0.0/4 (Reserved)
  • 255.255.255.255 (Broadcast)

Part 2 — DNS resolution validation:

  • Add validateResolvedUrl() async function that resolves hostnames via dns.promises.lookup() and checks resolved IPs against private ranges
  • Uses dynamic import('node:dns') to support edge runtimes (graceful skip when unavailable)
  • Integrate into both download-blob.ts and download.ts after existing sync validation
  • Keep existing sync validateDownloadUrl unchanged for backward compatibility

Manual Verification

  • Ran pnpm test in packages/provider-utils — all 477 tests pass (node + edge)
  • Verified edge runtime tests pass (dynamic import gracefully skips DNS check)
  • Confirmed new tests cover the core SSRF bypass scenario (hostname → private IP)

Checklist

  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Related Issues

Fixes #13510

Add missing private IPv4 ranges (CGNAT, benchmarking, reserved, broadcast)
to isPrivateIPv4(). Add validateResolvedUrl() that resolves hostnames via
DNS and blocks private IPs, preventing DNS rebinding SSRF attacks where a
hostname resolves to an internal address like 169.254.169.254.

Closes vercel#13510

Signed-off-by: majiayu000 <1835304752@qq.com>
…runtimes

Replace top-level node:dns import with dynamic import inside
validateResolvedUrl() so the module loads without crashing in edge
runtimes (Vercel Edge, Cloudflare Workers, Deno Deploy). The try/catch
around the dynamic import gracefully skips DNS validation when node:dns
is unavailable. Also adds missing changeset for the SSRF fix.

Signed-off-by: majiayu000 <1835304752@qq.com>
@tigent tigent bot added ai/provider related to a provider package. Must be assigned together with at least one `provider/*` label bug Something isn't working as documented maintenance CI, internal documentation, automations, etc labels Mar 22, 2026
@majiayu000 majiayu000 marked this pull request as ready for review March 22, 2026 04:49
try {
const { promises } = await import('node:dns');
result = await promises.lookup(hostname);
} catch {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSRF bypass: dns.promises.lookup() without { all: true } only validates a single DNS record, allowing an attacker with mixed public/private DNS records to bypass the private IP check.

Fix on Vercel

@majiayu000
Copy link
Author

Closing as duplicate — #13512 already addresses this issue. Apologies for the overlap.

@majiayu000 majiayu000 closed this Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai/provider related to a provider package. Must be assigned together with at least one `provider/*` label bug Something isn't working as documented maintenance CI, internal documentation, automations, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security: SSRF bypass via DNS resolution in validateDownloadUrl

1 participant