Skip to content

URL: Stop normalizePath throwing on a malformed percent sequence - #81086

Merged
hbhalodia merged 2 commits into
WordPress:trunkfrom
konnen916:fix/normalize-path-malformed-input
Aug 14, 2026
Merged

URL: Stop normalizePath throwing on a malformed percent sequence#81086
hbhalodia merged 2 commits into
WordPress:trunkfrom
konnen916:fix/normalize-path-malformed-input

Conversation

@konnen916

Copy link
Copy Markdown
Contributor

What?

Closes #81085

normalizePath throws a URIError when a query parameter contains a malformed percent sequence. This switches it to the package's own safeDecodeURIComponent.

Why?

normalizePath decodes each parameter with bare decodeURIComponent, so a lone % in a value takes the whole call down:

normalizePath( '/wp/v2/posts?search=50%off' );
// URIError: URI malformed

This is the same failure that was fixed for getQueryArgs in #45561, where the throw was breaking Calypso. That PR introduced safeDecodeURIComponent for exactly this, and normalizePath was not updated at the time.

It reaches past the function because the preloading middleware in @wordpress/api-fetch calls normalizePath on every key of the preloaded data and on options.path for every request that passes through it. One unencoded %, in a hand-written path or in a server-generated preload key, takes out the middleware instead of failing a single request, and it surfaces during normalization rather than as an API error.

How?

One line plus the import, matching #45561: decodeURIComponent becomes safeDecodeURIComponent, which returns the component untouched when decoding fails.

Well-formed input is unaffected. Malformed input now normalizes instead of throwing, and the function stays order-stable, which is the property it exists to provide:

normalizePath( '/foo/bar?a=5&b=50%off' ) === normalizePath( '/foo/bar?b=50%off&a=5' );
// true

Testing Instructions

  1. Open a post or page.
  2. Open the console.
  3. Run wp.url.normalizePath( '/wp/v2/posts?search=50%off' ).
  4. On trunk it throws URIError: URI malformed. With this branch it returns /wp/v2/posts?search=50%25off.

Or run the package tests:

npm run test:unit -- packages/url

Two tests are added, one asserting it no longer throws and one asserting it stays order-stable when a parameter is malformed. packages/api-fetch also passes, since it is the consumer of this function.

Testing Instructions for Keyboard

Not applicable, this is a package-level change with no UI.

Screenshots or screencast

Not applicable.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @konnen916.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: konnen916.

Co-authored-by: hbhalodia <hbhalodia@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions github-actions Bot added the [Package] Url /packages/url label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @konnen916! In case you missed it, we'd love to have you join us in our Slack community.

If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information.

@github-actions github-actions Bot added the First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository label Aug 3, 2026
@konnen916
konnen916 force-pushed the fix/normalize-path-malformed-input branch from cd98ef8 to 0457272 Compare August 8, 2026 15:35
Comment thread packages/url/CHANGELOG.md Outdated
Comment thread packages/url/src/normalize-path.ts Outdated
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 WordPress#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.
@konnen916
konnen916 force-pushed the fix/normalize-path-malformed-input branch from 0457272 to df32b75 Compare August 14, 2026 02:39
@konnen916

Copy link
Copy Markdown
Contributor Author

Thanks, fixed both.

The lint error was the dependency group comment block I put above the import. No other .ts file in packages/url uses one, get-query-args.ts included, so I removed it and the file now matches its siblings. npx eslint packages/url/src/normalize-path.ts is clean and the package suite passes, 503 tests.

I also took your CHANGELOG wording, and rebased on trunk since it had picked up a conflict from the 4.53.0 section being cut.

@hbhalodia hbhalodia 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.

Thanks @konnen916, LGTM! and works well. Below is the test report.

Without patch

Screen.Recording.2026-08-14.at.1.02.22.PM.mov

With patch

Screen.Recording.2026-08-14.at.1.03.04.PM.mov

Unit tests

  normalizePath
    ✓ returns same value if no query parameters
    ✓ returns a stable path (1 ms)
    ✓ sorts urldecoded values and returns property urlencoded query string
    ✓ should not blow up on malformed params (1 ms)
    ✓ returns a stable path when a param is malformed

@hbhalodia
hbhalodia enabled auto-merge (squash) August 14, 2026 07:36
@hbhalodia
hbhalodia merged commit 51d339f into WordPress:trunk Aug 14, 2026
42 checks passed
@github-actions

Copy link
Copy Markdown

Congratulations on your first merged pull request, @konnen916! We'd like to credit you for your contribution in the post announcing the next WordPress release, but we can't find a WordPress.org profile associated with your GitHub account. When you have a moment, visit the following URL and click "link your GitHub account" under "GitHub Username" to link your accounts:

https://profiles.wordpress.org/me/profile/edit/

And if you don't have a WordPress.org account, you can create one on this page:

https://login.wordpress.org/register

Kudos!

@github-actions github-actions Bot added this to the Gutenberg 23.9 milestone Aug 14, 2026
@jeryj jeryj added the [Type] Bug An existing feature does not function as intended label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository [Package] Url /packages/url [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

normalizePath throws a URIError on a malformed percent sequence in a query parameter

3 participants