Skip to content

fix(ipv6): accept every RFC 4291 form of embedded IPv4 address - #1681

Open
breken-ai wants to merge 2 commits into
open-circle:mainfrom
breken-ai:fix-ipv6-embedded-ipv4
Open

breken-ai wants to merge 2 commits into
open-circle:mainfrom
breken-ai:fix-ipv6-embedded-ipv4

Conversation

@breken-ai

@breken-ai breken-ai commented Sep 30, 2026 •

Copy link
Copy Markdown

Summary

v.ipv6() and v.ip() reject valid IPv6 addresses whose last 32 bits are written as an IPv4 address, unless the IPv4 part comes straight after :: (or after ::ffff:0:), or after x:...:: with nothing between :: and the IPv4 part.

const Schema = v.pipe(v.string(), v.ipv6());

v.is(Schema, '::ffff:129.144.52.38'); // true
v.is(Schema, '0:0:0:0:0:FFFF:129.144.52.38'); // false today, same address written out in full
v.is(Schema, '0:0:0:0:0:0:13.1.68.3'); // false today
v.is(Schema, '64:ff9b:1::10.0.0.1'); // false today, local-use NAT64 prefix (RFC 8215)
v.is(Schema, '::1:2:3:4:192.168.1.1'); // false today

0:0:0:0:0:0:13.1.68.3 and 0:0:0:0:0:FFFF:129.144.52.38 are the examples RFC 4291 section 2.2 (form 3) uses for this notation. Node's net.isIPv6 accepts all of the addresses above, and so does the format: "ipv6" that @valibot/to-json-schema emits for this action when it is checked with Ajv and ajv-formats. Today a form that accepts addresses from a JSON Schema validator or from Node will reject some of the same addresses when it uses Valibot.

Cause

The last two alternatives of IPV6_REGEX (and the copy inside IP_REGEX) handle the IPv4 tail:

  • ::(?:ffff(?::0{1,4})?:)?<ipv4>
  • (?:h16:){1,4}:<ipv4>

Neither allows groups between :: and the IPv4 part (other than ffff:0), and there is no alternative for the uncompressed form with six groups.

Changes

  • library/src/regex.ts: replace those two alternatives with the full set from the RFC 4291 / RFC 3986 grammar: six groups before the IPv4 part without ::, or n groups (0 to 5), then ::, then at most 5 - n groups before the IPv4 part. The new alternatives accept everything the old two did. The IPv4 octet pattern, the i flag and the other alternatives are unchanged, and IP_REGEX gets the same change.
  • library/src/actions/ipv6/ipv6.test.ts and library/src/actions/ip/ip.test.ts: new valid cases for the embedded IPv4 forms, and new invalid cases for embedded IPv4 addresses with too many groups (for example 1:2:3:4:5:6:7:192.168.1.1 and 1:2:3:4:5:6::192.168.1.1). The valid cases fail on the current main.
  • library/CHANGELOG.md: entry under the unreleased section.

Out of scope: zone IDs on uncompressed link-local addresses (fe80:0:0:0:1:2:3:4%eth0) and leading zeros in the embedded IPv4 octets. Both behave the same as before.

Verification

In library/:

  • New test cases on main without the regex change: 2 failed (the new "embedded IPv4" tests in ip.test.ts and ipv6.test.ts).
  • With the change: ip, ipv4 and ipv6 tests pass (27 tests), and the full Vitest suite passes (280 files, 3234 tests).
  • ESLint (including the ReDoS rule) on the changed files, tsc --noEmit, and Prettier all pass. I did not run deno check because Deno isn't installed here.
  • I also ran 200,000 random IPv6 strings, with and without :: and with and without an IPv4 tail, through both the new regex and Node's net.isIPv6. They agreed on every string except the two out-of-scope cases above. Long crafted inputs (10k+ characters) match in under a millisecond.

This PR was written with help from an AI coding assistant. I checked the diff, the tests and the results above.

Summary by CodeRabbit

  • Bug Fixes
    • ip and ipv6 now accept IPv6 addresses with embedded IPv4 addresses in the forms allowed by RFC 4291, while continuing to reject invalid or incomplete addresses.
  • Documentation
    • Updated the unreleased changelog with the IPv6 address validation change.

IPV6_REGEX and IP_REGEX only accepted an IPv4 tail after '::' with no
groups between them (or after '::ffff:0:'), so valid addresses such as
0:0:0:0:0:ffff:192.168.1.1, ::1:2:3:4:192.168.1.1 and
64:ff9b:1::10.0.0.1 were rejected. Allow up to six groups before the
IPv4 tail without compression and up to five with '::'.
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 100aa7c5-b347-4392-8f4c-bcbff0861427

📥 Commits

Reviewing files that changed from the base of the PR and between 7b0c7de and a4be45a.

📒 Files selected for processing (2)
  • library/src/actions/ip/ip.test.ts
  • library/src/actions/ipv6/ipv6.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.


Walkthrough

The IPv6 regex and the IPv6 branch of the IP regex now cover additional IPv6 addresses with embedded IPv4 notation. Tests for ipv6 and ip include valid and invalid examples. The changelog records the supported forms.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to a4be4

The changes broaden IPv6 and IP validation for embedded IPv4 forms. No actionable merge-blocking risk is established in the supplied context.

Architecture Summary

Architecture risk: 🔵 Low · up to a4be4

The change affects 1 system.

Changed systems: library

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — library (service) was modified; 4 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in library/CHANGELOG.md: Adds an unreleased changelog entry documenting the embedded-IPv4 IPv6 address forms accepted by ipv6 and ip, with examples and a pull request reference.
  • observed — Modified behavior in library/src/regex.ts: IPV6_REGEX replaces its embedded-IPv4 alternatives with alternatives covering a six-hextet prefix and supported compressed-hextet prefixes before the IPv4 address; the dedicated ::(?:f{4}...) form is removed.
  • observed — Modified behavior in library/src/regex.ts: IP_REGEX updates its IPv6 alternatives to match the revised embedded-IPv4 forms, while retaining its existing IPv4 alternative.
  • observed — Modified behavior in library/src/actions/ip/ip.test.ts: Added a test that expects ip to accept 13 IPv6 addresses containing embedded IPv4 addresses.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: accepting all RFC 4291 forms of embedded IPv4 addresses in IPv6 validation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread library/src/actions/ipv6/ipv6.test.ts

This branch has not been deployed

No deployments
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