From 2e60da8ac7ec17960f67a15dda4a741d3f90c97d Mon Sep 17 00:00:00 2001 From: Su Yang Date: Mon, 31 Aug 2026 04:03:02 +0800 Subject: [PATCH] fix: reject Unicode whitespace in recovery URLs --- tests/test-tool-update-site-url.php | 7 +++++++ tool-update-site-url.php | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test-tool-update-site-url.php b/tests/test-tool-update-site-url.php index db8fa53..c1a1548 100644 --- a/tests/test-tool-update-site-url.php +++ b/tests/test-tool-update-site-url.php @@ -224,6 +224,7 @@ function () use ( $state_time ) { 'local URL' => array( 'http://localhost:8080/wordpress/', 'http://localhost:8080/wordpress' ), 'private IPv4 URL' => array( 'http://192.168.1.20:8080', 'http://192.168.1.20:8080' ), 'IPv6 URL' => array( 'https://[::1]:8443/wp', 'https://[::1]:8443/wp' ), + 'Unicode path' => array( 'https://example.com/路径', 'https://example.com/路径' ), 'maximum length' => array( $max_length_url, $max_length_url ), ); foreach ( $valid_urls as $label => $case ) { @@ -246,6 +247,12 @@ function () use ( $state_time ) { 'internal whitespace' => 'https://example.com/a b', 'leading whitespace' => ' https://example.com', 'trailing whitespace' => "https://example.com\n", + 'non-breaking space' => "https://example.com/a\u{00A0}b", + 'next-line whitespace' => "https://example.com/a\u{0085}b", + 'em space' => "https://example.com/a\u{2003}b", + 'line separator' => "https://example.com/a\u{2028}b", + 'paragraph separator' => "https://example.com/a\u{2029}b", + 'invalid UTF-8' => "https://example.com/\xC3\x28", 'backslash' => 'https://example.com\\admin', 'dot path segment' => 'https://example.com/a/../admin', 'zero port' => 'https://example.com:0', diff --git a/tool-update-site-url.php b/tool-update-site-url.php index 9127ca0..b8c5fd1 100644 --- a/tool-update-site-url.php +++ b/tool-update-site-url.php @@ -588,7 +588,10 @@ function sqlite_wordpress_site_url_tool_validate_url( $value, $label ) { if ( '' === $value || strlen( $value ) > 2048 ) { throw new InvalidArgumentException( $label . ' must contain a URL no longer than 2048 bytes.' ); } - if ( preg_match( '/[\x00-\x20\x7F\\\\]/', $value ) ) { + if ( 1 !== preg_match( '//u', $value ) ) { + throw new InvalidArgumentException( $label . ' must contain valid UTF-8.' ); + } + if ( preg_match( '/(?:[\x00-\x20\x7F\\\\]|\p{Z}|\x{0085})/u', $value ) ) { throw new InvalidArgumentException( $label . ' must not contain whitespace, control characters, or backslashes.' ); }