From df32b75b22c06ef7dedf64ee74a79e78e5f744e2 Mon Sep 17 00:00:00 2001 From: konnen916 <300166086+konnen916@users.noreply.github.com> Date: Tue, 4 Aug 2026 00:36:44 +0800 Subject: [PATCH] URL: Stop normalizePath throwing on a malformed percent sequence normalizePath decoded each query parameter with bare decodeURIComponent, which throws a URIError when a value contains a malformed percent sequence such as `?search=50%off`. This is the same failure that was fixed for getQueryArgs in #45561, by switching to the package's own safeDecodeURIComponent. normalizePath was not updated at the time. Well formed input is unaffected, and the function stays order stable, which is what it exists for. It matters past the function itself because the api-fetch preloading middleware calls normalizePath on every preload key and on every request path, so the throw takes out the middleware rather than failing a single request. --- packages/url/CHANGELOG.md | 1 + packages/url/src/normalize-path.ts | 4 +++- packages/url/src/test/index.js | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index 4e0d35acf814ce..4516d20637c695 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -5,6 +5,7 @@ ### Bug Fixes - `getQueryArgs`: Split each query argument on its first `=` only, so values containing `=` — base64 padding, JWTs, nested URLs — are no longer truncated, and ignore malformed pairs with no key instead of promoting the value to one ([#81066](https://github.com/WordPress/gutenberg/pull/81066)). +- `normalizePath` no longer throws a `URIError` when a query parameter contains a malformed percent sequence, such as `/wp/v2/posts?search=50%off`. It now uses `safeDecodeURIComponent`, matching the fix already applied to `getQueryArgs` in [#45561](https://github.com/WordPress/gutenberg/pull/45561) ([#81086](https://github.com/WordPress/gutenberg/pull/81086)). ## 4.53.0 (2026-08-12) diff --git a/packages/url/src/normalize-path.ts b/packages/url/src/normalize-path.ts index 93d7cca54f9158..0cc7564043b0ed 100644 --- a/packages/url/src/normalize-path.ts +++ b/packages/url/src/normalize-path.ts @@ -1,3 +1,5 @@ +import { safeDecodeURIComponent } from './safe-decode-uri-component'; + /** * Given a path, returns a normalized path where equal query parameter values * will be treated as identical, regardless of order they appear in the original @@ -25,7 +27,7 @@ export function normalizePath( path: string ): string { // [ [ 'b, '1%2C2' ], [ 'c', '2' ], [ 'a', '5' ] ] .map( ( entry ) => entry.split( '=' ) ) // [ [ 'b', '1,2' ], [ 'c', '2' ], [ 'a', '5' ] ] - .map( ( pair ) => pair.map( decodeURIComponent ) ) + .map( ( pair ) => pair.map( safeDecodeURIComponent ) ) // [ [ 'a', '5' ], [ 'b, '1,2' ], [ 'c', '2' ] ] .sort( ( a, b ) => a[ 0 ].localeCompare( b[ 0 ] ) ) // [ [ 'a', '5' ], [ 'b, '1%2C2' ], [ 'c', '2' ] ] diff --git a/packages/url/src/test/index.js b/packages/url/src/test/index.js index 935ee6123e8771..c090e4dbb2d05f 100644 --- a/packages/url/src/test/index.js +++ b/packages/url/src/test/index.js @@ -1298,4 +1298,18 @@ describe( 'normalizePath', () => { const ab = normalizePath( '/foo/bar?a%2Ca=5,5&a,b=1,1' ); expect( ab ).toBe( '/foo/bar?a%2Ca=5%2C5&a%2Cb=1%2C1' ); } ); + + it( 'should not blow up on malformed params', () => { + const path = '/foo/bar?baz=%E0%A4%A'; + + expect( () => normalizePath( path ) ).not.toThrow(); + expect( normalizePath( path ) ).toBe( '/foo/bar?baz=%25E0%25A4%25A' ); + } ); + + it( 'returns a stable path when a param is malformed', () => { + const ab = normalizePath( '/foo/bar?a=5&b=50%off' ); + const ba = normalizePath( '/foo/bar?b=50%off&a=5' ); + + expect( ab ).toBe( ba ); + } ); } );