Make WordPress Core


Ignore:
Timestamp:
01/31/2024 10:53:48 AM (5 months ago)
Author:
youknowriad
Message:

Editor: Sanitize nested array in theme.json properly.

WP_Theme_JSON sanitization is now able to sanitize data contained on indexed arrays.
So certain data from theme.json, for example, settings.typography.fontFamilies which is a JSON array will be sanitized.

Props mmaattiiaass, mukesh27.
Fixes #60360.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-theme-json.php

    r57491 r57496  
    430430    );
    431431
     432    /*
     433     * The valid properties for fontFamilies under settings key.
     434     *
     435     * @since 6.5.0
     436     *
     437     * @var array
     438     */
     439    const FONT_FAMILY_SCHEMA = array(
     440        array(
     441            'fontFamily' => null,
     442            'name'       => null,
     443            'slug'       => null,
     444            'fontFace'   => array(
     445                array(
     446                    'ascentOverride'        => null,
     447                    'descentOverride'       => null,
     448                    'fontDisplay'           => null,
     449                    'fontFamily'            => null,
     450                    'fontFeatureSettings'   => null,
     451                    'fontStyle'             => null,
     452                    'fontStretch'           => null,
     453                    'fontVariationSettings' => null,
     454                    'fontWeight'            => null,
     455                    'lineGapOverride'       => null,
     456                    'sizeAdjust'            => null,
     457                    'src'                   => null,
     458                    'unicodeRange'          => null,
     459                ),
     460            ),
     461        ),
     462    );
     463
    432464    /**
    433465     * The valid properties under the styles key.
     
    558590
    559591    /**
     592     * Return the input schema at the root and per origin.
     593     *
     594     * @since 6.5.0
     595     *
     596     * @param array $schema The base schema.
     597     * @return array The schema at the root and per origin.
     598     *
     599     * Example:
     600     * schema_in_root_and_per_origin(
     601     *   array(
     602     *    'fontFamily' => null,
     603     *    'slug' => null,
     604     *   )
     605     * )
     606     *
     607     * Returns:
     608     * array(
     609     *  'fontFamily' => null,
     610     *  'slug' => null,
     611     *  'default' => array(
     612     *    'fontFamily' => null,
     613     *    'slug' => null,
     614     *  ),
     615     *  'blocks' => array(
     616     *    'fontFamily' => null,
     617     *    'slug' => null,
     618     *  ),
     619     *  'theme' => array(
     620     *     'fontFamily' => null,
     621     *     'slug' => null,
     622     *  ),
     623     *  'custom' => array(
     624     *     'fontFamily' => null,
     625     *     'slug' => null,
     626     *  ),
     627     * )
     628     */
     629    protected static function schema_in_root_and_per_origin( $schema ) {
     630        $schema_in_root_and_per_origin = $schema;
     631        foreach ( static::VALID_ORIGINS as $origin ) {
     632            $schema_in_root_and_per_origin[ $origin ] = $schema;
     633        }
     634        return $schema_in_root_and_per_origin;
     635    }
     636
     637    /**
    560638     * Returns a class name by an element name.
    561639     *
     
    798876        }
    799877
    800         $schema['styles']             = static::VALID_STYLES;
    801         $schema['styles']['blocks']   = $schema_styles_blocks;
    802         $schema['styles']['elements'] = $schema_styles_elements;
    803         $schema['settings']           = static::VALID_SETTINGS;
    804         $schema['settings']['blocks'] = $schema_settings_blocks;
     878        $schema['styles']                                 = static::VALID_STYLES;
     879        $schema['styles']['blocks']                       = $schema_styles_blocks;
     880        $schema['styles']['elements']                     = $schema_styles_elements;
     881        $schema['settings']                               = static::VALID_SETTINGS;
     882        $schema['settings']['blocks']                     = $schema_settings_blocks;
     883        $schema['settings']['typography']['fontFamilies'] = static::schema_in_root_and_per_origin( static::FONT_FAMILY_SCHEMA );
    805884
    806885        // Remove anything that's not present in the schema.
     
    9751054     */
    9761055    protected static function remove_keys_not_in_schema( $tree, $schema ) {
    977         $tree = array_intersect_key( $tree, $schema );
    978 
    979         foreach ( $schema as $key => $data ) {
    980             if ( ! isset( $tree[ $key ] ) ) {
     1056        if ( ! is_array( $tree ) ) {
     1057            return $tree;
     1058        }
     1059
     1060        foreach ( $tree as $key => $value ) {
     1061            // Remove keys not in the schema or with null/empty values.
     1062            if ( ! array_key_exists( $key, $schema ) ) {
     1063                unset( $tree[ $key ] );
    9811064                continue;
    9821065            }
    9831066
    984             if ( is_array( $schema[ $key ] ) && is_array( $tree[ $key ] ) ) {
    985                 $tree[ $key ] = static::remove_keys_not_in_schema( $tree[ $key ], $schema[ $key ] );
    986 
    987                 if ( empty( $tree[ $key ] ) ) {
    988                     unset( $tree[ $key ] );
     1067            // Check if the value is an array and requires further processing.
     1068            if ( is_array( $value ) && is_array( $schema[ $key ] ) ) {
     1069                // Determine if it is an associative or indexed array.
     1070                $schema_is_assoc = self::is_assoc( $value );
     1071
     1072                if ( $schema_is_assoc ) {
     1073                    // If associative, process as a single object.
     1074                    $tree[ $key ] = self::remove_keys_not_in_schema( $value, $schema[ $key ] );
     1075
     1076                    if ( empty( $tree[ $key ] ) ) {
     1077                        unset( $tree[ $key ] );
     1078                    }
     1079                } else {
     1080                    // If indexed, process each item in the array.
     1081                    foreach ( $value as $item_key => $item_value ) {
     1082                        if ( isset( $schema[ $key ][0] ) && is_array( $schema[ $key ][0] ) ) {
     1083                            $tree[ $key ][ $item_key ] = self::remove_keys_not_in_schema( $item_value, $schema[ $key ][0] );
     1084                        } else {
     1085                            // If the schema does not define a further structure, keep the value as is.
     1086                            $tree[ $key ][ $item_key ] = $item_value;
     1087                        }
     1088                    }
    9891089                }
    9901090            } elseif ( is_array( $schema[ $key ] ) && ! is_array( $tree[ $key ] ) ) {
     
    9941094
    9951095        return $tree;
     1096    }
     1097
     1098    /**
     1099     * Checks if the given array is associative.
     1100     *
     1101     * @since 6.5.0
     1102     * @param array $data The array to check.
     1103     * @return bool True if the array is associative, false otherwise.
     1104     */
     1105    protected static function is_assoc( $data ) {
     1106        if ( array() === $data ) {
     1107            return false;
     1108        }
     1109        return array_keys( $data ) !== range( 0, count( $data ) - 1 );
    9961110    }
    9971111
Note: See TracChangeset for help on using the changeset viewer.