Make WordPress Core

Changeset 59515


Ignore:
Timestamp:
12/14/2024 11:51:09 PM (20 months ago)
Author:
SergeyBiryukov
Message:

Formatting: Check the result of preg_split() in convert_smilies().

This aims to avoid a fatal error from count() when preg_split() fails on large input.

Includes:

  • Optimizing the regular expression used to split the input by tags to avoid unlimited backtracking for better performance.
  • Adjusting the function logic for better readability.

Follow-up to [340], [4380], [26191].

Props podpirate, nathkrill, rajinsharwar, dmsnell, bjorsch, q0rban, audrasjb, rupw, Ov3rfly, jorbin, nhrrob, chaion07, mcqueen22, azaozz, narenin, roybellingan, SergeyBiryukov.
See #51019.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/formatting.php

    r59333 r59515  
    34743474function convert_smilies( $text ) {
    34753475        global $wp_smiliessearch;
     3476
     3477        if ( ! get_option( 'use_smilies' ) || empty( $wp_smiliessearch ) ) {
     3478                // Return default text.
     3479                return $text;
     3480        }
     3481
     3482        // HTML loop taken from texturize function, could possible be consolidated.
     3483        $textarr = preg_split( '/(<[^>]*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // Capture the tags as well as in between.
     3484
     3485        if ( false === $textarr ) {
     3486                // Return default text.
     3487                return $text;
     3488        }
     3489
     3490        // Loop stuff.
     3491        $stop   = count( $textarr );
    34763492        $output = '';
    3477         if ( get_option( 'use_smilies' ) && ! empty( $wp_smiliessearch ) ) {
    3478                 // HTML loop taken from texturize function, could possible be consolidated.
    3479                 $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // Capture the tags as well as in between.
    3480                 $stop    = count( $textarr ); // Loop stuff.
    3481 
    3482                 // Ignore processing of specific tags.
    3483                 $tags_to_ignore       = 'code|pre|style|script|textarea';
    3484                 $ignore_block_element = '';
    3485 
    3486                 for ( $i = 0; $i < $stop; $i++ ) {
    3487                         $content = $textarr[ $i ];
    3488 
    3489                         // If we're in an ignore block, wait until we find its closing tag.
    3490                         if ( '' === $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')[^>]*>/', $content, $matches ) ) {
    3491                                 $ignore_block_element = $matches[1];
    3492                         }
    3493 
    3494                         // If it's not a tag and not in ignore block.
    3495                         if ( '' === $ignore_block_element && strlen( $content ) > 0 && '<' !== $content[0] ) {
    3496                                 $content = preg_replace_callback( $wp_smiliessearch, 'translate_smiley', $content );
    3497                         }
    3498 
    3499                         // Did we exit ignore block?
    3500                         if ( '' !== $ignore_block_element && '</' . $ignore_block_element . '>' === $content ) {
    3501                                 $ignore_block_element = '';
    3502                         }
    3503 
    3504                         $output .= $content;
    3505                 }
    3506         } else {
    3507                 // Return default text.
    3508                 $output = $text;
    3509         }
     3493
     3494        // Ignore processing of specific tags.
     3495        $tags_to_ignore       = 'code|pre|style|script|textarea';
     3496        $ignore_block_element = '';
     3497
     3498        for ( $i = 0; $i < $stop; $i++ ) {
     3499                $content = $textarr[ $i ];
     3500
     3501                // If we're in an ignore block, wait until we find its closing tag.
     3502                if ( '' === $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')[^>]*>/', $content, $matches ) ) {
     3503                        $ignore_block_element = $matches[1];
     3504                }
     3505
     3506                // If it's not a tag and not in ignore block.
     3507                if ( '' === $ignore_block_element && strlen( $content ) > 0 && '<' !== $content[0] ) {
     3508                        $content = preg_replace_callback( $wp_smiliessearch, 'translate_smiley', $content );
     3509                }
     3510
     3511                // Did we exit ignore block?
     3512                if ( '' !== $ignore_block_element && '</' . $ignore_block_element . '>' === $content ) {
     3513                        $ignore_block_element = '';
     3514                }
     3515
     3516                $output .= $content;
     3517        }
     3518
    35103519        return $output;
    35113520}
  • trunk/tests/phpunit/tests/formatting/convertSmilies.php

    r57987 r59515  
    394394                return $wpsmiliestrans;
    395395        }
     396
     397
     398        /**
     399         * Tests that the function does not throw a fatal error from count()
     400         * when preg_split() fails on large input.
     401         *
     402         * @ticket 51019
     403         */
     404        public function test_smilies_with_large_text_input() {
     405                $text = '<p><img alt="" src="data:image/png;base64,' . str_repeat( 'iVBORw0KGgoAAAAN', 65536 ) . '="></p> :)';
     406                $this->assertStringContainsString( "\xf0\x9f\x99\x82", convert_smilies( $text ) );
     407        }
    396408}
Note: See TracChangeset for help on using the changeset viewer.