Make WordPress Core


Ignore:
Timestamp:
06/09/2026 12:47:16 AM (3 months ago)
Author:
westonruter
Message:

Editor: Guard against non-string className in wp_render_elements_class_name() block render filter callback.

The wp_render_elements_class_name() function reads the className block attribute and passes it straight to preg_match(). While className is expected to be a string, malformed or corrupted stored block data can hold another type, such as an array, which triggers a fatal TypeError on PHP 8+. Guard against this by returning the block content unchanged when className is not a string.

While here, align the implementation with wp_render_custom_css_class_name() from r62359: replace the regular expression with a str_contains() short-circuit followed by an HTML-spec-compliant strtok() walk over the class tokens. This also corrects a latent matching bug: the previous \bwp-elements-\S+\b pattern treated the hyphen as a word boundary, so a class such as my-wp-elements-foo was erroneously matched. Tokenizing the attribute first ensures only a standalone wp-elements-* class is applied.

Add regression tests for the non-string and substring-prefix cases, and resolve PHPStan errors at rule level 10 (missingType.iterableValue, offsetAccess.nonOffsetAccessible, argument.type) by adding an array shape to the @phpstan-param tag.

Developed in https://github.com/WordPress/wordpress-develop/pull/12028 and https://github.com/WordPress/gutenberg/pull/78841.
Follow-up to r58074, r62359.

Props aaronrobertshaw, andrewserong, mukesh27, westonruter.
Fixes #65379.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-supports/elements.php

    r61557 r62475  
    238238 * @since 6.6.0
    239239 *
    240  * @param  string $block_content Rendered block content.
    241  * @param  array  $block         Block object.
    242  * @return string                Filtered block content.
     240 * @param string $block_content Rendered block content.
     241 * @param array  $block         Block object.
     242 * @return string               Filtered block content.
     243 *
     244 * @phpstan-param array{
     245 *     attrs: array{
     246 *         className?: string,
     247 *         ...
     248 *     },
     249 *     ...
     250 * } $block
    243251 */
    244252function wp_render_elements_class_name( $block_content, $block ) {
    245         $class_string = $block['attrs']['className'] ?? '';
    246         preg_match( '/\bwp-elements-\S+\b/', $class_string, $matches );
    247 
    248         if ( empty( $matches ) ) {
     253        $class_name_attr   = $block['attrs']['className'] ?? null;
     254        $class_name_prefix = 'wp-elements-';
     255        if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
    249256                return $block_content;
    250257        }
    251258
     259        // Parse out the 'wp-elements-*' class name.
     260        $matched_class_name = null;
     261        $token_delimiter    = " \t\f\r\n";
     262        $class_token        = strtok( $class_name_attr, $token_delimiter );
     263        while ( false !== $class_token ) {
     264                if ( str_starts_with( $class_token, $class_name_prefix ) ) {
     265                        $matched_class_name = $class_token;
     266                        break;
     267                }
     268                $class_token = strtok( $token_delimiter );
     269        }
     270        if ( null === $matched_class_name ) {
     271                return $block_content;
     272        }
     273
    252274        $tags = new WP_HTML_Tag_Processor( $block_content );
    253 
    254275        if ( $tags->next_tag() ) {
    255                 $tags->add_class( $matches[0] );
     276                $tags->add_class( $matched_class_name );
    256277        }
    257278
Note: See TracChangeset for help on using the changeset viewer.