[php-src] PHP-8.5: ext/uri: Fix GH-22628 Percent-encoding of caret in WHATWG URL paths is not performed (#22700)

Máté Kocsis via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Máté Kocsis (kocsismate)
Committer: GitHub (web-flow)
Pusher: TimWolla
Date: 2026-07-12T20:50:11+02:00

Commit: https://github.com/php/php-src/commit/c025bc47dbbd47c687d06e11c622d32e21f283e8
Raw diff: https://github.com/php/php-src/commit/c025bc47dbbd47c687d06e11c622d32e21f283e8.diff

ext/uri: Fix GH-22628 Percent-encoding of caret in WHATWG URL paths is not performed (#22700)

The caret (^) is part of the path percent-encode set:

"The path percent-encode set is a percent-encode set consisting of the query percent-encode set and U+003F (?), U+005E (^), U+0060 (`), U+007B ({), and U+007D (})."

Until now, this character wasn't percent-encoded in the path likely due to a copy-paste error. This mistake is fixed by adding LXB_URL_MAP_PATH to the lxb_url_map entry for the caret.

Changed paths:
  A  ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch
  M  NEWS
  M  ext/lexbor/lexbor/url/url.c
  M  ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
  M  ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
  M  ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
  M  ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
  M  ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
  M  ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
  M  ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch
  M  ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
  M  ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
  M  ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
  M  ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt


Diff:

diff --git a/NEWS b/NEWS
index bf4e4d2bdb50..a4f1a24223ec 100644
--- a/NEWS
+++ b/NEWS
@@ -104,6 +104,8 @@ PHP                                                                        NEWS
 - URI:
   . Fixed behavior of Uri\WhatWg\Url wither methods with regards to empty
     opaque hosts. (kocsismate)
+  . Fixed bug GH-22628 (Percent-encoding of caret in WHATWG URL paths is not
+    performed). (kocsismate)
   . Fixed bug GH-22629 (WHATWG Validation error incorrect with empty host and
     non-empty userinfo). (kocsismate)
 
diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c
index 19ec23829a23..de19239936a1 100644
--- a/ext/lexbor/lexbor/url/url.c
+++ b/ext/lexbor/lexbor/url/url.c
@@ -159,7 +159,7 @@ static const uint8_t lxb_url_map[256] =
     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5b ([) */
     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5c (\) */
     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5d (]) */
-    LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */
+    LXB_URL_MAP_USERINFO|LXB_URL_MAP_PATH|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */
     LXB_URL_MAP_UNDEF, /* 0x5f (_) */
     LXB_URL_MAP_PATH|LXB_URL_MAP_FRAGMENT|LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x60 (`) */
     LXB_URL_MAP_UNDEF, /* 0x61 (a) */
diff --git a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
index e106b413c22c..533598837822 100644
--- a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
+++ b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Sat, 26 Aug 2023 15:08:59 +0200
-Subject: [PATCH 1/9] Expose line and column information for use in PHP
+Subject: [PATCH 01/10] Expose line and column information for use in PHP
 
 ---
  source/lexbor/dom/interfaces/node.h  |  2 ++
diff --git a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
index 9e83700198e1..8814d5955354 100644
--- a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
+++ b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Mon, 14 Aug 2023 20:18:51 +0200
-Subject: [PATCH 2/9] Track implied added nodes for options use in PHP
+Subject: [PATCH 02/10] Track implied added nodes for options use in PHP
 
 ---
  source/lexbor/html/tree.h                            | 3 +++
diff --git a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
index 9ed36fda109d..aa4802320491 100644
--- a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
+++ b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
@@ -1,8 +1,8 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Thu, 24 Aug 2023 22:57:48 +0200
-Subject: [PATCH 3/9] Patch utilities and data structure to be able to generate
- smaller lookup tables
+Subject: [PATCH 03/10] Patch utilities and data structure to be able to
+ generate smaller lookup tables
 
 Changed the generation script to check if everything fits in 32-bits.
 And change the actual field types to 32-bits. This decreases the hash
diff --git a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
index 9e9fcaf8db7a..1a28b21ccdc5 100644
--- a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
+++ b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Wed, 29 Nov 2023 21:26:47 +0100
-Subject: [PATCH 4/9] Remove unused upper case tag static data
+Subject: [PATCH 04/10] Remove unused upper case tag static data
 
 ---
  source/lexbor/tag/res.h | 2 ++
diff --git a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
index ce3448169395..a1dda1fcd112 100644
--- a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
+++ b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Wed, 29 Nov 2023 21:29:31 +0100
-Subject: [PATCH 5/9] Shrink size of static binary search tree
+Subject: [PATCH 05/10] Shrink size of static binary search tree
 
 This also makes it more efficient on the data cache.
 ---
diff --git a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
index c8f485f34807..57f1e0e92fcb 100644
--- a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
+++ b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Niels Dossche <[email protected]>
 Date: Sun, 7 Jan 2024 21:59:28 +0100
-Subject: [PATCH 6/9] Patch out unused CSS style code
+Subject: [PATCH 06/10] Patch out unused CSS style code
 
 ---
  source/lexbor/css/rule.h | 2 ++
diff --git a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch
index 56e206f63d2f..b55f5aac5894 100644
--- a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch
+++ b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Alexander Borisov <[email protected]>
 Date: Fri, 26 Jun 2026 18:55:56 +0300
-Subject: [PATCH 7/9] URL: fixed setters for empty hosts.
+Subject: [PATCH 07/10] URL: fixed setters for empty hosts.
 MIME-Version: 1.0
 Content-Type: text/plain; charset=UTF-8
 Content-Transfer-Encoding: 8bit
diff --git a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
index c8cd2332eeff..d967c8ca4eb9 100644
--- a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
+++ b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Alexander Borisov <[email protected]>
 Date: Fri, 5 Jun 2026 22:13:32 +0300
-Subject: [PATCH 8/9] URL: fixed uninitialized memory in the path buffer
+Subject: [PATCH 08/10] URL: fixed uninitialized memory in the path buffer
  growth.
 
 When a path was long enough to outgrow the on-stack buffer, the first
diff --git a/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
index ef7743e61539..1e7aa4d1087d 100644
--- a/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
+++ b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= <[email protected]>
 Date: Thu, 9 Jul 2026 21:51:05 +0200
-Subject: [PATCH 9/9] Fix parsing for URL containing empty host and userinfo
+Subject: [PATCH 09/10] Fix parsing for URL containing empty host and userinfo
 
 The returned error code (LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS) apparently contradicts the specification:
 
diff --git a/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch b/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch
new file mode 100644
index 000000000000..04d91bda68a9
--- /dev/null
+++ b/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= <[email protected]>
+Date: Fri, 10 Jul 2026 22:31:16 +0200
+Subject: [PATCH 10/10] Percent-encode the caret in the path
+
+The caret (^) is part of the path percent-encode set:
+
+"The path percent-encode set is a percent-encode set consisting of the query percent-encode set and U+003F (?), U+005E (^), U+0060 (`), U+007B ({), and U+007D (})."
+
+Until now, this character wasn't percent-encoded in the path likely due to a copy-paste error. This is mistake is fixed by adding LXB_URL_MAP_PATH to the lxb_url_map entry for the caret.
+
+Originally reported at https://github.com/php/php-src/issues/22628
+---
+ source/lexbor/url/url.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source/lexbor/url/url.c b/source/lexbor/url/url.c
+index 19ec238..de19239 100644
+--- a/source/lexbor/url/url.c
++++ b/source/lexbor/url/url.c
+@@ -159,7 +159,7 @@ static const uint8_t lxb_url_map[256] =
+     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5b ([) */
+     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5c (\) */
+     LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5d (]) */
+-    LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */
++    LXB_URL_MAP_USERINFO|LXB_URL_MAP_PATH|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */
+     LXB_URL_MAP_UNDEF, /* 0x5f (_) */
+     LXB_URL_MAP_PATH|LXB_URL_MAP_FRAGMENT|LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x60 (`) */
+     LXB_URL_MAP_UNDEF, /* 0x61 (a) */
diff --git a/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
index 59f539124c68..8a168b928820 100644
--- a/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
+++ b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
@@ -15,5 +15,5 @@ var_dump($url2->toAsciiString());
 ?>
 --EXPECT--
 string(1) "/"
-string(8) "/p^th%23"
-string(27) "https://example.com/p^th%23"
+string(10) "/p%5Eth%23"
+string(29) "https://example.com/p%5Eth%23"
diff --git a/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt
index 9419c817fbbd..dd82beeff101 100644
--- a/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt
+++ b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt
@@ -22,10 +22,10 @@ object(Uri\WhatWg\Url)#%d (%d) {
   ["port"]=>
   NULL
   ["path"]=>
-  string(28) "/foo%22/%3Cbar%3E/^%7Bbaz%7D"
+  string(30) "/foo%22/%3Cbar%3E/%5E%7Bbaz%7D"
   ["query"]=>
   NULL
   ["fragment"]=>
   NULL
 }
-string(47) "https://example.com/foo%22/%3Cbar%3E/^%7Bbaz%7D"
+string(49) "https://example.com/foo%22/%3Cbar%3E/%5E%7Bbaz%7D"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.