Skip to content

fix(url): reject bare IPv6 addresses in z.url()#6103

Open
MerlijnW70 wants to merge 1 commit into
colinhacks:mainfrom
MerlijnW70:fix/url-bare-ipv6
Open

fix(url): reject bare IPv6 addresses in z.url()#6103
MerlijnW70 wants to merge 1 commit into
colinhacks:mainfrom
MerlijnW70:fix/url-bare-ipv6

Conversation

@MerlijnW70

Copy link
Copy Markdown

Fixes #6031.

Problem

z.url() accepts bare IPv6 link-local addresses such as fe80::1 as valid URLs, while correctly rejecting ::1, 2001:db8::1, and 192.168.1.1:

z.url().safeParse("fe80::1");   // ✅ VALID  — bug (should be invalid)
z.url().safeParse("2001:db8::1"); // ❌ invalid — correct

The cause is the WHATWG URL constructor: a bare IPv6 address whose first group starts with a hex letter (fe80:) is parsed as scheme:opaque-path, so new URL("fe80::1") succeeds.

Fix

Before normal URL handling, reject any input that is itself a valid IPv6 literal by probing new URL("http://[<input>]") — the same technique Zod's ipv6() validator already uses. A cheap pre-check (only hex digits, colons, dots and a zone %, and no /) keeps schemed URIs and ordinary URLs out of this branch, so the existing behaviour is preserved:

  • rejected: ::1, 2001:db8::1, fe80::1, fe80::abcd:1234, full-form fe80:0000:…:0001, dead:beef:… (×8)
  • still valid: http://[2001:db8::1], https://example.com, c:, mailto:foo@bar.com, face:b00c

Tests

Adds a regression test in url.test.ts covering both the rejected bare-IPv6 cases and the preserved valid cases. Verified red without the fix, green with it; full suite (3817 tests) passes.

The WHATWG URL constructor parses a bare IPv6 address whose first group
starts with a hex letter (e.g. "fe80::1") as `scheme:opaque-path`, so
z.url() accepted it as a valid URL while correctly rejecting "::1" and
"2001:db8::1". Reject any input that is itself a valid IPv6 literal by
probing `http://[<input>]`, guarded by a cheap pre-check so schemed URIs
like "c:" or "mailto:x" and ordinary URLs are unaffected.

Fixes colinhacks#6031

Co-Authored-By: Claude Opus 4.8 (1M context) <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.

Bug: z.string().url() incorrectly validates IPv6 link-local addresses ("fe80:0000:0000:0000:0000:0000:0000:0001") as valid URLs

2 participants