Make WordPress Core

Changeset 59515


Ignore:
Timestamp:
12/14/2024 11:51:09 PM (3 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.