Skip to content

fix(security): extend SSRF protection with private IP range checks#2186

Open
tessaherself wants to merge 2 commits intohuggingface:mainfrom
tessaherself:fix/extended-ssrf-private-ip-checks
Open

fix(security): extend SSRF protection with private IP range checks#2186
tessaherself wants to merge 2 commits intohuggingface:mainfrom
tessaherself:fix/extended-ssrf-private-ip-checks

Conversation

@tessaherself
Copy link

Summary

  • Add isPrivateIPv4(): checks RFC 1918 (10/8, 172.16/12, 192.168/16), loopback, link-local, 0.0.0.0/8
  • Add isPrivateIPv6(): checks loopback, link-local (fe80::/10), unique-local (fc00::/7)
  • Add isURLPrivate(): resolves hostname and checks against all private ranges
  • Add validateExternalUrl(): protocol + private IP validation for external fetches
  • All existing exports (isURLLocal, isURLStringLocal, isHostLocalhost) are unchanged

Problem

The current isURLLocal() only blocks 127.0.0.0/8, allowing SSRF to other private ranges like 10.x.x.x, 172.16.x.x, 192.168.x.x, and IPv6 private addresses.

Changes

  • src/lib/server/isURLLocal.ts: Add 4 new exports (purely additive, no breaking changes)

Test plan

  • Existing isURLLocal tests still pass
  • isPrivateIPv4("10.0.0.1") returns true
  • isPrivateIPv4("8.8.8.8") returns false
  • validateExternalUrl("http://192.168.1.1") throws

🤖 Generated with Claude Code

The existing isURLLocal only checks 127.0.0.0/8 for IPv4. Add
comprehensive private/reserved IP detection covering RFC 1918
(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), link-local
(169.254.0.0/16), and IPv6 equivalents (fc00::/7, fe80::/10).

New exports: isPrivateIPv4, isPrivateIPv6, isURLPrivate,
validateExternalUrl. All existing exports unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2da2cbaa44

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


/** Check if a URL resolves to a private/reserved IP address (SSRF protection). */
export async function isURLPrivate(url: URL): Promise<boolean> {
const { address, family } = await dnsLookup(url.hostname);

Choose a reason for hiding this comment

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

P1 Badge Evaluate all DNS records in private-IP check

isURLPrivate only inspects the single address returned by dns.lookup, but hostnames can legitimately return multiple A/AAAA records; in a mixed set (e.g., one public and one private answer), this can return false even though the URL does resolve to a private IP, allowing SSRF checks to be bypassed depending on DNS answer ordering or rebinding timing. Use lookup with all: true and fail if any resolved address is private/reserved.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

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

This feedback doesn't match the actual code. isURLPrivate already uses dnsLookupAll (which calls dns.lookup with { all: true }) and iterates over all resolved addresses, rejecting if any is private. This is exactly what the suggestion recommends — it's already implemented.

Resolve all A/AAAA records with dns.lookup({ all: true }) and reject
if any resolved address is private/reserved, preventing bypass via
mixed DNS answers or rebinding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant