| @@ -182,8 +182,22 @@ | ||
| 182 | 182 | * @return string |
| 183 | 183 | */ |
| 184 | 184 | public static function sanitize_url( $value, $allowed_protocols = [ 'http', 'https' ] ) { |
| 185 | 185 | |
| 186 | + // Percent-encode non-ASCII bytes in the path/query/fragment before parsing, so wp_parse_url() | |
| 187 | + // does not corrupt multibyte UTF-8 characters. The authority (userinfo + host) is left untouched | |
| 188 | + // to avoid double-encoding it during the sanitization below. | |
| 189 | + if ( preg_match( '/[\x80-\xff]/', $value ) === 1 ) { | |
| 190 | + preg_match( '`^((?:[a-z][a-z0-9+.\-]*:)?//[^/?#]*)?(.*)$`is', $value, $split ); | |
| 191 | + $value = $split[1] . preg_replace_callback( | |
| 192 | + '/[\x80-\xff]/', | |
| 193 | + static function ( $bytes ) { | |
| 194 | + return rawurlencode( $bytes[0] ); | |
| 195 | + }, | |
| 196 | + $split[2], | |
| 197 | + ); | |
| 198 | + } | |
| 199 | + | |
| 186 | 200 | $url = ''; |
| 187 | 201 | $parts = wp_parse_url( $value ); |
| 188 | 202 | |
| 189 | 203 | if ( isset( $parts['scheme'], $parts['host'] ) ) { |