Skip to content

fix(security): block IPv4-compatible IPv6 SSRF bypass#4467

Merged
waleedlatif1 merged 3 commits intostagingfrom
waleedlatif1/ssrf-ipv4-compat-check
May 6, 2026
Merged

fix(security): block IPv4-compatible IPv6 SSRF bypass#4467
waleedlatif1 merged 3 commits intostagingfrom
waleedlatif1/ssrf-ipv4-compat-check

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • isPrivateOrReservedIP classified deprecated IPv4-compatible IPv6 addresses (::a.b.c.d, RFC 4291 §2.5.5.1) as plain unicast because ipaddr.js has no SpecialRange entry for that prefix
  • Node's URL parser normalizes [::192.168.1.1] to [::c0a8:101], which then slipped past the validator and reached the MCP connection stage — confirmed reachable via POST /api/mcp/servers/test-connection
  • Patch detects ::/96 (excluding ::ffff:0:0/96 which ipaddr.js already extracts), pulls out the embedded IPv4, and recurses on range() so private/reserved targets like 169.254.169.254, 127.0.0.1, and RFC1918 ranges are blocked regardless of encoding
  • Same normalization pattern used by recent CVE fixes in Directus, Discourse, AVideo, OpenClaw, LibreChat, Craft CMS

Type of Change

  • Bug fix

Testing

  • Added 56 unit tests covering IPv4 private/reserved, IPv6 reserved, IPv4-mapped, NAT64, IPv4-compatible (hex + dotted forms), public IPv4-compat, over-block regression cases, alternate IPv4 notations, and URL-parser integration
  • All 491 tests pass across input-validation.test.ts and domain-check.test.ts
  • Type check clean

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 6, 2026 3:50am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 6, 2026

PR Summary

Medium Risk
Touches SSRF/IP-blocking logic used for outbound URL/database host validation; a mistake could over-block legitimate IPv6 targets or leave a bypass. Added extensive tests mitigate regression risk.

Overview
Closes an SSRF validation gap where IPv4-compatible IPv6 forms under ::/96 (including Node URL-normalized hex like ::c0a8:101) could be treated as public unicast and slip past isPrivateOrReservedIP.

isPrivateOrReservedIP now detects IPv4-embedded IPv6 addresses, extracts the embedded IPv4, and applies the same private/reserved checks to it. The test suite is expanded substantially to cover IPv4/IPv6 reserved ranges, mapped/compat forms, alternate notations, and Node URL hostname normalization integration.

Reviewed by Cursor Bugbot for commit 031ef68. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR fixes an SSRF bypass where IPv4-compatible IPv6 addresses (RFC 4291 §2.5.5.1, the deprecated ::/96 prefix) were not blocked by isPrivateOrReservedIP because ipaddr.js has no SpecialRange entry for that prefix and classifies those addresses as plain 'unicast'.

  • The fix adds a check inside the IPv6 branch: if the first six 16-bit parts are all zero (identifying the IPv4-compatible space, distinct from IPv4-mapped which has parts[5] = 0xffff), it extracts the 32-bit embedded IPv4 from parts[6] and parts[7], constructs a real IPv4 address via ipaddr.fromByteArray, and recurses on range() — so private/reserved targets like 169.254.169.254, 127.0.0.1, and RFC 1918 ranges are blocked regardless of encoding.
  • Fifty-six new unit tests are added covering all major classes of addresses (private IPv4, IPv6 reserved, IPv4-mapped, NAT64, IPv4-compatible in hex and dotted form, public IPv4-compat over-block regression, alternate notations, invalid input, and Node URL-parser integration).

Confidence Score: 5/5

Safe to merge — the change is a tightly scoped addition to a well-tested security function with no side effects on unrelated code paths.

The logic correctly identifies the IPv4-compatible ::/96 space (parts[0..5] all zero, parts[5] ≠ 0xffff distinguishes it from IPv4-mapped which ipaddr.js already handles via process()), extracts the embedded IPv4 accurately, and gates the return on range() !== 'unicast'. Addresses already caught by the upfront range check (::, ::1, link-local, ULA, NAT64) never reach the new branch, so there is no double-handling or over-blocking risk. The 56 new tests cover all meaningful sub-ranges including the regression case for public embedded IPs, and the Node URL-parser integration test directly proves the original attack vector is closed.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/lib/core/security/input-validation.server.ts Adds IPv4-compatible IPv6 detection (::/96) by checking if the first 6 parts of an IPv6 address are all zero, extracting the embedded IPv4, and checking its range — correctly excluding the IPv4-mapped prefix (handled by ipaddr.js's process()) since that requires parts[5]=0xffff.
apps/sim/lib/core/security/input-validation.test.ts Adds 56 new unit tests covering IPv4 private/reserved, IPv6 reserved, IPv4-mapped, NAT64, IPv4-compatible (hex and dotted), public IPv4-compat (over-block regression), alternate IPv4 notations, invalid inputs, and Node URL-parser integration; all cases pass on the correct code paths.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[isPrivateOrReservedIP ip] --> B{ipaddr.isValid?}
    B -- No --> BLOCK[return true blocked]
    B -- Yes --> C[addr = ipaddr.process ip]
    C --> D{addr.range != unicast?}
    D -- Yes --> BLOCK
    D -- No --> E{addr.kind == ipv6?}
    E -- No IPv4 --> ALLOW[return false allowed]
    E -- Yes --> F{parts 0..5 all zero? /::/96 space/}
    F -- No --> ALLOW
    F -- Yes --> G[Extract embedded IPv4 from parts 6+7]
    G --> H[embedded = ipaddr.fromByteArray bytes]
    H --> I{embedded.range != unicast?}
    I -- Yes private/reserved --> BLOCK
    I -- No public --> ALLOW
Loading

Reviews (2): Last reviewed commit: "fix(security): correct RFC1918 test labe..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/security/input-validation.test.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 031ef68. Configure here.

@waleedlatif1 waleedlatif1 merged commit 80eb5b9 into staging May 6, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/ssrf-ipv4-compat-check branch May 6, 2026 04:26
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