URLPattern: use encoded username/password when matching URL inputs
URLPattern#exec / #test percent-decoded the username and password of URL-string inputs before matching, so a pattern built from a URL's own canonical userinfo failed to match that URL, and exec() echoed reserved bytes raw. Dict-form inputs behaved correctly, so Bun disagreed with itself. Node 26, Deno, and the spec all keep the encoded form.
const m = new URLPattern({}).exec("https://u%20x:%2F%3A%40@h.example/");
m.username.input; // bun: "u x" node/deno: "u%20x"
m.password.input; // bun: "/:@" node/deno: "%2F%3A%40"
new URLPattern({ username: "u%20x", password: "%2F%3A%40" })
.test("https://u%20x:%2F%3A%40@h.example/"); // bun: false node/deno: true
const p = new URLPattern({ password: "paß" });
p.password; // "pa%C3%9F" everywhere
p.test("https://user:pa%C3%9F@h.example/"); // bun: false node/deno: true
p.test({ password: "paß" }); // true everywhere (dict form)
Cause
matchHelperAssignInputsFromURL in URLPattern.cpp read input.user() / input.password(), which are WTF::URL's escape-decoding accessors. The spec's match step 11 takes the parsed URL's serialized (still-encoded) username and password, which is what encodedUser() / encodedPassword() return. The baseURL path of processInit had the same bug for the same reason.
Fix
Switch both sites to encodedUser() / encodedPassword(). Upstream WebCore's URLPattern.cpp has the same defect at the same two sites.
Verification
5 new cases in test/js/web/urlpattern/urlpattern.test.ts cover URL-string input, baseURL input, matching against encoded and non-ASCII patterns, and agreement with new URL(). All 5 fail on the released Bun; all 413 tests in the file pass with this change. Every expected value matches Node 26.3.
[review] gate passed · iteration 0 · 2 files touched
fails on main (without fix)
ASAN without fix: 5 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/web/urlpattern/urlpattern.test.ts
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (75265af8b)
test/js/web/urlpattern/urlpattern.test.ts:
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"}] [33.73ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/ba"}] [7.67ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/"}] [7.27ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/baz"}] [8.36ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar"] [13.46ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar/baz"] [9.07m
... (truncated)
release without fix: 5 FAILED
bun test v1.4.0-canary.1 (1498d7b77)
test/js/web/urlpattern/urlpattern.test.ts:
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"}] [0.57ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/ba"}] [0.05ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/"}] [0.04ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/baz"}] [0.06ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar"] [0.27ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar/baz"] [0.08ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"hostname":"example.com","pathname":"/foo/bar"}] [0.14ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"hostname":"example.com","pathname":"/foo/bar/baz"}] [0.06ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar","baseURL":"https://example.com"}] [0.06ms]
(pass)
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/web/urlpattern/urlpattern.test.ts
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (75265af8b)
test/js/web/urlpattern/urlpattern.test.ts:
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"}] [34.35ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/ba"}] [7.76ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/"}] [7.59ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/baz"}] [8.11ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar"] [13.37ms]
(pass) URLPattern > WPT tests > Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar/baz"] [9.01m
... (truncated)
release with fix: all passed
$ bun scripts/build.ts --profile=release
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
[configured] bun-profile → bun (stripped) in 752ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/119] gen generated_host_exports.rs
generated_host_exports.rs: 91 exports (host=3, lazy=10, generic=78, rust=0); 243 extern-C blocks audited
[2/119] gen cpp.rs (cppbind)
[2/119] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: component rust-std is up to date
nightly-2026-05-06-x86_64-unknown-linux-gnu unchanged - rustc 1.97.0-nightly (e95e73209 2026-05-05)
info: checking for self-update (current version: 1.29.0)
[1m[92m Blocking[0m waiting for file lock on build directory
[1m[92m Compiling[0m bun_runtime v0.0.0 (/
... (truncated)
diff hotspot
src/jsc/bindings/webcore/URLPattern.cpp | 8 +++---
test/js/web/urlpattern/urlpattern.test.ts | 42 +++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+), 4 deletions(-)
gate history · 1 passed · 0 rejected · iteration 0
evidence per changed file
file reads edits tests
src/jsc/bindings/webcore/URLPattern.cpp 1 2 0
test/js/web/urlpattern/urlpattern.test.ts 1 1 0