Make WordPress Core


Ignore:
Timestamp:
06/11/2026 03:16:43 AM (4 months ago)
Author:
dmsnell
Message:

Charset: Replace polyfill wp_has_noncharacters() with direct PCRE version.

Found during fuzzing work on the HTML API and adjacent code. The previous version of this function used a Unicode PCRE to detect noncharacter code points, but that invocation failed if the input string contained sequences of invalid UTF-8 bytes.

This patch replaces the Unicode PCRE with a mapped sequence of raw bytes. This version works in environments without Unicode support and it works when invalid bytes are present, making it possible to remove the fallback function as well.

Developed in: ​https://github.com/WordPress/wordpress-develop/pull/12148
Discussed in: https://core.trac.wordpress.org/ticket/65372

Follow-up to [61000].

Props dmsnell, jonsurrell.
See #65372.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/unicode/wpHasNoncharacters.php

    r62207 r62485  
    4242
    4343        /**
    44          * Ensures that a noncharacter inside a string will be properly detected
    45          * using the fallback function when Unicode PCRE support is missing.
     44         * Ensures that invalid UTF-8 does not prevent noncharacter detection.
    4645         *
    47          * @ticket 63863
    48          *
    49          * @dataProvider data_noncharacters
    50          *
    51          * @param string $noncharacter Noncharacter as a UTF-8 string.
     46         * @ticket 65372
    5247         */
    53         public function test_fallback_detects_non_characters( string $noncharacter ) {
     48        public function test_detects_non_characters_when_string_contains_invalid_utf8() {
    5449                $this->assertTrue(
    55                         _wp_has_noncharacters_fallback( $noncharacter ),
    56                         'Failed to detect entire string as noncharacter.'
     50                        wp_has_noncharacters( "Invalid byte \xF1 before \u{FDD0}." ),
     51                        'Failed to detect noncharacter after invalid UTF-8.'
    5752                );
    5853
    5954                $this->assertTrue(
    60                         _wp_has_noncharacters_fallback( "{$noncharacter} and more." ),
    61                         'Failed to detect noncharacter prefix.'
     55                        wp_has_noncharacters( "Noncharacter \u{10FFFF} before invalid byte \xF1." ),
     56                        'Failed to detect noncharacter before invalid UTF-8.'
    6257                );
    6358
    64                 $this->assertTrue(
    65                         _wp_has_noncharacters_fallback( "Some text and then a {$noncharacter} and more." ),
    66                         'Failed to detect medial noncharacter.'
    67                 );
    68 
    69                 $this->assertTrue(
    70                         _wp_has_noncharacters_fallback( "Some text and a {$noncharacter}." ),
    71                         'Failed to detect noncharacter suffix.'
     59                $this->assertFalse(
     60                        wp_has_noncharacters( "Invalid byte \xF1 without noncharacters." ),
     61                        'Falsely detected noncharacter in invalid UTF-8.'
    7262                );
    7363        }
    … …  
    119109
    120110        /**
    121          * Ensures that Unicode characters are not falsely detect as noncharacters
    122          * using the fallback function when Unicode PCRE support is missing.
    123          *
    124          * @ticket 63863
    125          */
    126         public function test_fallback_avoids_false_positives() {
    127                 // Get all the noncharacters in one long string, each surrounded on both sides by null bytes.
    128                 $noncharacters = implode(
    129                         "\x00",
    130                         array_map(
    131                                 static function ( $c ) {
    132                                         return "\x00{$c}";
    133                                 },
    134                                 array_column( array_values( iterator_to_array( self::data_noncharacters() ) ), 0 )
    135                         )
    136                 ) . "\x00";
    137 
    138                 $this->assertFalse(
    139                         _wp_has_noncharacters_fallback( "\x00" ),
    140                         'Falsely detected noncharacter in U+0000'
    141                 );
    142 
    143                 for ( $code_point = 1; $code_point <= 0x10FFFF; $code_point++ ) {
    144                         // Surrogate halves are invalid UTF-8.
    145                         if ( $code_point >= 0xD800 && $code_point <= 0xDFFF ) {
    146                                 continue;
    147                         }
    148 
    149                         $char     = mb_chr( $code_point );
    150                         $hex_char = strtoupper( str_pad( dechex( $code_point ), 4, '0', STR_PAD_LEFT ) );
    151 
    152                         if ( str_contains( $noncharacters, $char ) ) {
    153                                 $this->assertTrue(
    154                                         _wp_has_noncharacters_fallback( $char ),
    155                                         "Failed to detect noncharacter as test verification for U+{$hex_char}"
    156                                 );
    157                         } else {
    158                                 $this->assertFalse(
    159                                         _wp_has_noncharacters_fallback( $char ),
    160                                         "Falsely detected noncharacter in U+{$hex_char}."
    161                                 );
    162                         }
    163                 }
    164         }
    165 
    166         /**
    167111         * Data provider
    168112         *
Note: See TracChangeset for help on using the changeset viewer.