From 56ca55a96e320f7c6cf8e4c31e88c09b661fee02 Mon Sep 17 00:00:00 2001 From: Sainath Poojary Date: Wed, 5 Aug 2026 20:46:48 +0530 Subject: [PATCH 1/2] Rewrite Rules: Decode URL-encoded Unicode permastructs to prevent capture group misalignment --- src/wp-includes/class-wp-rewrite.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-rewrite.php b/src/wp-includes/class-wp-rewrite.php index 8b75fa5c36d16..9cc69a68aebb3 100644 --- a/src/wp-includes/class-wp-rewrite.php +++ b/src/wp-includes/class-wp-rewrite.php @@ -1844,7 +1844,13 @@ public function add_permastruct( $name, $struct, $args = array() ) { $struct = $this->root . $struct; } - $args['struct'] = $struct; + $args['struct'] = preg_replace_callback( + '/(?:%[0-9A-F]{2})+/', + static function ( $matches ) { + return rawurldecode( $matches[0] ); + }, + $struct + ); $this->extra_permastructs[ $name ] = $args; } From e1514330e3ef1c1d83b39ab2669bb6cf799f7882 Mon Sep 17 00:00:00 2001 From: Sainath Poojary Date: Thu, 6 Aug 2026 19:01:22 +0530 Subject: [PATCH 2/2] Tests: Add PHPUnit tests for URL-encoded Unicode permastructs --- tests/phpunit/tests/rewrite/permastructs.php | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/phpunit/tests/rewrite/permastructs.php b/tests/phpunit/tests/rewrite/permastructs.php index ce98e06a68bb3..db4361b3c0059 100644 --- a/tests/phpunit/tests/rewrite/permastructs.php +++ b/tests/phpunit/tests/rewrite/permastructs.php @@ -14,6 +14,13 @@ public function set_up() { $this->set_permalink_structure( '/%postname%/' ); } + public function tear_down() { + remove_permastruct( 'wptests_cert' ); + unregister_taxonomy( 'wptests_cert' ); + + parent::tear_down(); + } + public function test_add_permastruct() { global $wp_rewrite; @@ -31,6 +38,7 @@ public function test_add_permastruct() { ), $wp_rewrite->extra_permastructs['foo'] ); + remove_permastruct( 'foo' ); } public function test_remove_permastruct() { @@ -43,4 +51,34 @@ public function test_remove_permastruct() { remove_permastruct( 'foo' ); $this->assertArrayNotHasKey( 'foo', $wp_rewrite->extra_permastructs ); } + + /** + * Tests that a URL-encoded Unicode taxonomy slug is decoded in the permastruct and + * that the generated rewrite rules correctly map the taxonomy query var to $matches[1]. + * + * @ticket 41791 + */ + public function test_add_permastruct_with_url_encoded_unicode_slug() { + global $wp_rewrite; + + register_taxonomy( + 'wptests_cert', + 'post', + array( + 'rewrite' => array( + 'slug' => urlencode( 'Сертификат' ), + ), + ) + ); + + $stored_struct = $wp_rewrite->extra_permastructs['wptests_cert']['struct']; + + // The struct must store decoded Unicode, not percent-encoded sequences. + $this->assertStringContainsString( 'Сертификат', $stored_struct ); + $this->assertStringNotContainsString( urlencode( 'Сертификат' ), $stored_struct ); + + // The generated rules must map the taxonomy var to $matches[1], not a shifted index. + $rules = $wp_rewrite->generate_rewrite_rules( $stored_struct ); + $this->assertContains( 'index.php?wptests_cert=$matches[1]', array_values( $rules ) ); + } }