Skip to content

Bump WebKit (oven-sh/WebKit#460 preview): URL.pathname keeps a dotted first segment in host-less URLs, fixing custom-scheme URLPatterns - #39457

Open
robobun wants to merge 1 commit into
mainfrom
farm/c5bb3848/webkit-url-dot-path-segment
Open

Bump WebKit (oven-sh/WebKit#460 preview): URL.pathname keeps a dotted first segment in host-less URLs, fixing custom-scheme URLPatterns#39457
robobun wants to merge 1 commit into
mainfrom
farm/c5bb3848/webkit-url-dot-path-segment

Conversation

@robobun

@robobun robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Bumps WEBKIT_VERSION to pick up oven-sh/WebKit#460 and adds the bun:test coverage for it.

Pinned to the preview build of oven-sh/WebKit#460 so CI exercises the change; to be re-pinned to the merged oven-sh/WebKit commit before this merges.

Problem

  • URL.prototype.pathname drops the leading /. of a URL that has no host and whose first path segment starts with a dot. href is right, only the getter is wrong, and the same happens through the pathname setter:

    new URL("foo:/.a/b").pathname          // Bun: "a/b"         Node 26 and Deno 2.9: "/.a/b"   (href is "foo:/.a/b" everywhere)
    new URL("git:/.git/config").pathname   // Bun: "git/config"  expected "/.git/config"
    new URL("foo:/..a").pathname           // Bun: ".a"          expected "/..a"

    new URL("foo:/.//b").pathname is //b everywhere, and URLs with a host (foo://h/.a, https://h/.a, file:/.a) are unaffected.

  • Every URLPattern with a custom scheme and a pathname starting with a dot is compiled wrong as a result, because for non-special schemes the pathname text is canonicalized by parsing it as the path of a host-less dummy URL and reading the pathname back (src/jsc/bindings/webcore/URLPatternCanonical.cpp, canonicalizeOpaquePathname). The pattern's host makes no difference since the dummy URL never has one:

    new URLPattern("myapp://host/.well-known/:f").pathname                      // Bun: "well-known/:f", expected "/.well-known/:f"
    new URLPattern("myapp://host/.well-known/:f").test("myapp://host/.well-known/assetlinks.json")   // Bun: false
    new URLPattern("git://h/.git/:rest*").test("git://h/.git/config")           // Bun: false
    new URLPattern({ protocol: "myapp", pathname: "/.well-known/*" }).pathname  // Bun: "well-known/*"

    https patterns and { pathname: "/.well-known/*" } without a protocol are unaffected. u.host = "h:1" on such a URL was also silently ignored, and util.inspect(url, { showHidden: true }) reported no search_start for foo:/.foo?x, since both are computed from the same offset.

  • Cause: WTF::URL::pathStart() (Source/WTF/wtf/URL.cpp in oven-sh/WebKit, the offset every path accessor and setter uses) skips a /. directly after the scheme without checking that a / follows it. That /. is meant to be the guard the serializer puts in front of a path starting with // in a host-less URL, but the check also matches a real first segment such as .a or .git. No Bun source is involved: URLDecomposition::pathname() returns URL::path(), and the URLPattern canonicalizers read the same accessor. Same in upstream WebKit and in every Bun release; the WPT urltestdata.json and URLPattern corpora that Bun runs have no host-less URL with a dotted first segment, which is why this was only found by a differential run against Node and Deno.

Fix

  • WTF: URL::path() keeps a first path segment that starts with a dot in host-less URLs (URL("foo:/.a/b").path() was "a/b") WebKit#460 makes pathStart() also require the character after /. to be / (the only shape the parser ever inserts, and the same test its relative-URL path already used). The branch is directly on top of eeab04040f, the commit main is pinned to, so the new pin is exactly the current engine plus that change. Since the fix is in the offset itself, pathname, the pathname and host setters, relative resolution, URLPattern and the inspect offsets are all fixed at once.
  • Tests, in the existing files for these APIs:
    • test/js/web/url/url.test.ts: the dotted first segments above (including ?q#f, %2F, /.a//b, /./.a), control rows showing the /. guard is still omitted for foo:/.//b, foo:/..//b, foo:/.//.a and that hosts and special schemes are unchanged, relative resolution against and onto such URLs, the pathname, search, protocol and host setters (both h and h:1, on a dotted path and on a /.// path), and search_start in the inspected URLContext.
    • test/js/web/urlpattern/urlpattern.test.ts: the constructor-string, URLPatternInit and baseURL forms with a non-special scheme, test() and exec() results including the matched pathname.input, and unaffected https / protocol-less / later-segment patterns. Every expected value was checked against Node 26.
  • On the previous pin 9 of these tests fail (USE_SYSTEM_BUN=1 bun test on the two files: 436 pass, 9 fail, all in the new blocks plus the extended URLContext test) and the two control tests pass. Against this pin the two files pass in full under bun bd test, including the WPT URL constructor and URLPattern corpora they already contain. The WebKit change itself was also checked by compiling WebKit's own WTF_URL / WTF_URLParser gtest files (with the rows added in WTF: URL::path() keeps a first path segment that starts with a dot in host-less URLs (URL("foo:/.a/b").path() was "a/b") WebKit#460) against the eeab0404 debug ASAN prebuilt: 3 of 29 tests fail with the stock libWTF.a, 29 of 29 pass with the patched URL.cpp linked in front of it.

Background

  • URL Standard, serializer step for the path: when a URL has no host and its path starts with an empty segment, the serialization is scheme:/. followed by the path (foo:/.//b for the path //b), because foo://b would re-parse b as a host. The pathname getter returns the path without that guard, and only the guard; a first segment that merely starts with . is ordinary path text.
  • WTF's URL keeps the serialized string plus component offsets and derives the start of the path from the end of the host and port, which is where the guard special case lives. Bun's URL (src/jsc/bindings/URLDecomposition.cpp) and URLPattern (src/jsc/bindings/webcore/URLPattern*.cpp) are thin layers over it.
  • WEBKIT_VERSION in scripts/build/deps/webkit.ts is the only place the engine version lives; CI and bun bd download the prebuilt autobuild-<version> release for it from oven-sh/WebKit. Preview builds of a WebKit PR are published as autobuild-preview-pr-<n>-<sha> and can be pinned the same way.

[decide:webkit] gate passed · iteration 0 · 3 files touched

passes on PR (with fix)
Test-only change.

Debug/ASAN (expected pass):
$ bun bd test 'test/js/web/url/url.test.ts' 'test/js/web/urlpattern/urlpattern.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/js/web/url/url.test.ts test/js/web/urlpattern/urlpattern.test.ts
bun test v1.4.0 (8326d1bd3)

test/js/web/url/url.test.ts:
(pass) url > URL throws [12.04ms]
(pass) url > ERR_INVALID_URL carries input and, when given, base [9.75ms]
(pass) url > should have correct origin and protocol [11.09ms]
(pass) url > blob urls [7.67ms]
(pass) url > leaves opaque (non-special-scheme) hosts unchanged [7.10ms]
(pass) url > special-scheme hosts use the Unicode 16 IDNA table [5.08ms]
(pass) url > rejects invalid punycode labels however they are spelled in the input (like Node) [19.56ms]
(pass) url > judges literal punycode labels like Node (fast path and ICU path) [22.57ms]
(pass) url > resolves against repeated, alternating and invalid string bases consistently [21.87ms]
(pass) url > href, toString and toJSON agree before and after mutation [118.80ms]
(pass) url > prints [107.20ms]
(pass) url > URLContext offsets account for the /. pathname guard [99.87ms]
(pass) url > pathname of a host-less URL whose first segment starts with a dot > keeps the dotted first segment [14.98ms]
(pass) url > pathname of a host-less URL whose first segment starts with a dot > still omits the /. guard and normalizes dot segments [8.86ms]
(pass) url > pathname of a host-less URL whose first segment starts with a dot > resolves relative references against such a URL [8.80ms]
(pass) url > pathname of a host-less URL whose first segment starts with a dot > setters [18.66ms]
(pass) url > works [20.48ms]
(pass) url > URL.canParse > URL.canParse(undefined, undefined) [3.36ms]
(pass) url > URL.canParse > URL.canParse(a:b, undefined) [0.49ms]
(pass) url > URL.canParse > URL.canParse(undefined, a:b) [0.43ms]
(pass) url > URL.canParse > URL.canParse(a:/b, undefined) [0.34ms]
(pass) url > URL.canParse > URL.canParse(undefined, a:/b) [0.44ms]
(pass) url > URL.canParse > URL.canParse(https://test:test, undefined) [0.33ms]

... (truncated)
Exit: 0
diff hotspot
scripts/build/deps/webkit.ts              |   2 +-
 test/js/web/url/url.test.ts               | 115 ++++++++++++++++++++++++++++++
 test/js/web/urlpattern/urlpattern.test.ts |  75 +++++++++++++++++++
 3 files changed, 191 insertions(+), 1 deletion(-)

gate history · 2 passed · 0 rejected · iteration 0

evidence per changed file
file                                       reads  edits  tests
scripts/build/deps/webkit.ts                   2      3      0
test/js/web/url/url.test.ts                    1      1      0
test/js/web/urlpattern/urlpattern.test.ts      1      1      0

@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 10:43 PM PT - Aug 17th, 2026

@robobun, your commit 124d932 has 1 failures in Build #100406 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 39457

That installs a local version of the PR into your bun-39457 executable, so you can run:

bun-39457 --bun

@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Status: ready for review; blocked on oven-sh/WebKit#460 merging first.

  • Reproduced on main with new URL("foo:/.a/b").pathname ("a/b") and new URLPattern("myapp://host/.well-known/:f").test("myapp://host/.well-known/assetlinks.json") (false); Node 26 gives "/.a/b" and true.
  • Engine fix: WTF: URL::path() keeps a first path segment that starts with a dot in host-less URLs (URL("foo:/.a/b").path() was "a/b") WebKit#460, rebased onto eeab0404 (the commit main is pinned to since Upgrade WebKit to 47f7250137c6 #39371); this PR pins its preview build autobuild-preview-pr-460-8da5349b. Once Add live benchmarks on the website #460 merges this gets re-pinned to the merged commit, which also re-runs CI.
  • Tests: test/js/web/url/url.test.ts, test/js/web/urlpattern/urlpattern.test.ts. 9 fail on the main pin, all pass on this one (bun bd test, debug ASAN), along with the WPT URL and URLPattern corpora in the same files.
  • CI on 124d932b: 178 of 179 jobs green, including the URL and URLPattern files on every platform. The one red lane is test/bake/deinitialization.test.ts crashing during dev server teardown on Windows 2019 x64, which is unrelated to this change and has failed the same way on main (builds #99388 and #99911); it has been reported separately.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 59f621d4-70fc-4484-a4b0-92c54ee719c0

📥 Commits

Reviewing files that changed from the base of the PR and between 079cb0a and fd636e3.

📒 Files selected for processing (3)
  • scripts/build/deps/webkit.ts
  • test/js/web/url/url.test.ts
  • test/js/web/urlpattern/urlpattern.test.ts

Included review availability: Your plan includes up to 5 reviews per rolling hour; 0 remain after this review.


Walkthrough

The build now selects a WebKit preview release. URL and URLPattern tests add regression coverage for dotted first pathname segments across host-less and non-special URLs.

Changes

URL path regressions

Layer / File(s) Summary
WebKit preview selection
scripts/build/deps/webkit.ts
WEBKIT_VERSION now uses the autobuild-preview-pr-460-e932620f release identifier.
Host-less URL path handling
test/js/web/url/url.test.ts
Tests cover pathname offsets, normalization, serialization, relative resolution, and component setters for dotted first path segments.
URLPattern path handling
test/js/web/urlpattern/urlpattern.test.ts
Tests cover constructors, matching, execution, base URL resolution, canonicalization, and unaffected scheme cases.

Possibly related PRs

Suggested reviewers: jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the WebKit preview bump and the URL.pathname and URLPattern fixes covered by the tests.
Description check ✅ Passed The description explains the change, cause, fix, affected APIs, test coverage, verification results, and preview-pin follow-up.

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

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs. Because it bumps WEBKIT_VERSION (an engine change) and is currently pinned to a preview build that the description says must be re-pinned to the merged oven-sh/WebKit commit before this lands, a human look is still warranted.

What was reviewed:

  • The new URL tests: dotted-first-segment pathnames, the /. guard control cases, relative resolution, and the pathname/host/search/protocol setters — expected values line up with the URL Standard's serializer step and Node's behavior.
  • The new URLPattern tests: constructor-string, URLPatternInit, baseURL, and matched-input forms for non-special schemes, plus the unaffected https/protocol-less controls.
  • prebuiltUrl/prebuiltDestDir in scripts/build/deps/webkit.ts already handle autobuild--prefixed version strings, so the preview pin resolves correctly.
Extended reasoning...

Overview

This PR changes WEBKIT_VERSION in scripts/build/deps/webkit.ts from commit c6cfe90c60... to the preview tag autobuild-preview-pr-460-e932620f, and adds ~190 lines of regression tests across test/js/web/url/url.test.ts and test/js/web/urlpattern/urlpattern.test.ts. The actual code fix lives in oven-sh/WebKit#460 (a change to WTF::URL::pathStart()); no Bun source is modified.

Security risks

None identified. The tests are pure in-process assertions with no network, filesystem, or subprocess use. The version bump changes which prebuilt WebKit tarball CI downloads from oven-sh/WebKit releases — same trusted origin as before.

Level of scrutiny

High. WEBKIT_VERSION selects the JavaScriptCore engine build for the entire runtime; even a one-line bump here is a dependency upgrade in the sense of the repo's "Dependencies & vendoring" guidance. The description states the preview branch is directly on top of the previous pin, so the delta is claimed to be exactly the pathStart() fix, but I cannot verify the WebKit-side diff from this repo. The PR also explicitly says the pin must be moved to the merged WebKit commit before this lands — approving now would risk merging a preview pin.

Other factors

The test additions themselves are well-constructed: they follow the file's existing conventions, use exact-value toEqual on labelled objects (so a failure names the offending row), include negative/control cases confirming the /. guard is still stripped where it should be, and cover the setter and URLContext offset paths that share the same pathStart() computation. The description reports 9 of the new tests fail on the old pin and all pass on the new one, which satisfies the fails-for-the-right-reason bar. Given the engine bump and the pending re-pin, deferring to a human is the right call.

… first segment in host-less URLs

WTF::URL::pathStart() skipped the "/." that a host-less URL serializes in
front of a path starting with "//" without checking that a "/" follows, so
new URL("foo:/.a/b").pathname was "a/b" and every URLPattern with a
non-special scheme and a pathname starting with a dot compiled to the
truncated path. oven-sh/WebKit#460 fixes the offset; this pins its preview
build and adds URL and URLPattern tests for the getter, the setters,
relative resolution, the inspected URLContext offsets and pattern matching.
@robobun
robobun force-pushed the farm/c5bb3848/webkit-url-dot-path-segment branch from fd636e3 to 124d932 Compare August 18, 2026 05:22

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs in the test additions or the version-pin change. Since it bumps the WebKit engine pin (and to a preview build that the description says must be re-pinned to the merged oven-sh/WebKit commit before this lands), a human should sign off.

What was reviewed:

  • New URL tests: dotted-first-segment pathnames, control rows for the /. guard, relative resolution, and the pathname/host/search/protocol setters — expectations line up with the URL Standard's serializer step and Node's behavior.
  • New URLPattern tests: constructor-string / init / baseURL forms with non-special schemes, plus unaffected https/protocol-less controls.
  • prebuiltUrl / prebuiltDestDir in scripts/build/deps/webkit.ts already handle autobuild--prefixed version strings, so the preview tag resolves correctly.
Extended reasoning...

Overview

The PR changes WEBKIT_VERSION in scripts/build/deps/webkit.ts from a commit hash to the preview-build tag autobuild-preview-pr-460-8da5349b, and adds ~190 lines of regression tests to test/js/web/url/url.test.ts and test/js/web/urlpattern/urlpattern.test.ts covering the WTF::URL::pathStart() fix in oven-sh/WebKit#460 (host-less URLs whose first path segment starts with a dot).

Security risks

None identified. The change is a version pin plus test additions; no auth, crypto, or input-handling code in Bun itself is touched. The engine-side change (in the WebKit fork, not in this diff) narrows a condition in pathStart() — it makes the /. guard skip more restrictive, not less.

Level of scrutiny

High. WEBKIT_VERSION pins the entire JavaScriptCore engine; even a one-line change in WTF affects every URL operation in the runtime. The repo's review guidance calls out dependency/vendor bumps as needing situational attention, and WebKit bumps in particular are the kind of change a maintainer familiar with the oven-sh/WebKit release process should approve. The PR description also explicitly states the preview pin must be replaced with the merged commit before this lands, so it is not in a mergeable state by the author's own account.

Other factors

The test coverage is thorough and follows the repo's conventions: added to the existing files for URL/URLPattern, exact-value assertions, negative controls confirming the /. guard is still stripped for foo:/.//b, variant coverage across setters and URLPattern constructor forms, and expected values cross-checked against Node 26. I confirmed prebuiltUrl() and prebuiltDestDir() already special-case values starting with autobuild-, so the preview tag will download and cache correctly. Nothing in the tests looks flaky (no sleeps, no network). The remaining reason to defer is purely that engine pin changes — especially to an unmerged preview build — warrant a maintainer's explicit go-ahead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants