Make WordPress Core

Changeset 57496


Ignore:
Timestamp:
01/31/2024 10:53:48 AM (3 years 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.

Location:
trunk
Files:
2 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
  • trunk/tests/phpunit/tests/theme/wpThemeJson.php

    r57491 r57496  
    49794979                );
    49804980        }
     4981
     4982        /**
     4983         * Tests that invalid properties are removed from the theme.json inside indexed arrays as settings.typography.fontFamilies.
     4984         *
     4985         * @ticket 60360
     4986         */
     4987        public function test_sanitize_indexed_arrays() {
     4988                $theme_json = new WP_Theme_JSON(
     4989                        array(
     4990                                'version'  => '2',
     4991                                'badKey2'  => 'I am Evil!',
     4992                                'settings' => array(
     4993                                        'badKey3'    => 'I am Evil!',
     4994                                        'typography' => array(
     4995                                                'badKey4'      => 'I am Evil!',
     4996                                                'fontFamilies' => array(
     4997                                                        'custom' => array(
     4998                                                                array(
     4999                                                                        'badKey4'    => 'I am Evil!',
     5000                                                                        'name'       => 'Arial',
     5001                                                                        'slug'       => 'arial',
     5002                                                                        'fontFamily' => 'Arial, sans-serif',
     5003                                                                ),
     5004                                                        ),
     5005                                                        'theme'  => array(
     5006                                                                array(
     5007                                                                        'badKey5'    => 'I am Evil!',
     5008                                                                        'name'       => 'Piazzolla',
     5009                                                                        'slug'       => 'piazzolla',
     5010                                                                        'fontFamily' => 'Piazzolla',
     5011                                                                        'fontFace'   => array(
     5012                                                                                array(
     5013                                                                                        'badKey6'    => 'I am Evil!',
     5014                                                                                        'fontFamily' => 'Piazzolla',
     5015                                                                                        'fontStyle'  => 'italic',
     5016                                                                                        'fontWeight' => '400',
     5017                                                                                        'src'        => 'https://example.com/font.ttf',
     5018                                                                                ),
     5019                                                                                array(
     5020                                                                                        'badKey7'    => 'I am Evil!',
     5021                                                                                        'fontFamily' => 'Piazzolla',
     5022                                                                                        'fontStyle'  => 'italic',
     5023                                                                                        'fontWeight' => '400',
     5024                                                                                        'src'        => 'https://example.com/font.ttf',
     5025                                                                                ),
     5026                                                                        ),
     5027                                                                ),
     5028                                                                array(
     5029                                                                        'badKey8'    => 'I am Evil!',
     5030                                                                        'name'       => 'Inter',
     5031                                                                        'slug'       => 'Inter',
     5032                                                                        'fontFamily' => 'Inter',
     5033                                                                        'fontFace'   => array(
     5034                                                                                array(
     5035                                                                                        'badKey9'    => 'I am Evil!',
     5036                                                                                        'fontFamily' => 'Inter',
     5037                                                                                        'fontStyle'  => 'italic',
     5038                                                                                        'fontWeight' => '400',
     5039                                                                                        'src'        => 'https://example.com/font.ttf',
     5040                                                                                ),
     5041                                                                                array(
     5042                                                                                        'badKey10'   => 'I am Evil!',
     5043                                                                                        'fontFamily' => 'Inter',
     5044                                                                                        'fontStyle'  => 'italic',
     5045                                                                                        'fontWeight' => '400',
     5046                                                                                        'src'        => 'https://example.com/font.ttf',
     5047                                                                                ),
     5048                                                                        ),
     5049                                                                ),
     5050                                                        ),
     5051                                                ),
     5052                                        ),
     5053                                ),
     5054                        )
     5055                );
     5056
     5057                $expected_sanitized   = array(
     5058                        'version'  => '2',
     5059                        'settings' => array(
     5060                                'typography' => array(
     5061                                        'fontFamilies' => array(
     5062                                                'custom' => array(
     5063                                                        array(
     5064                                                                'name'       => 'Arial',
     5065                                                                'slug'       => 'arial',
     5066                                                                'fontFamily' => 'Arial, sans-serif',
     5067                                                        ),
     5068                                                ),
     5069                                                'theme'  => array(
     5070                                                        array(
     5071                                                                'name'       => 'Piazzolla',
     5072                                                                'slug'       => 'piazzolla',
     5073                                                                'fontFamily' => 'Piazzolla',
     5074                                                                'fontFace'   => array(
     5075                                                                        array(
     5076                                                                                'fontFamily' => 'Piazzolla',
     5077                                                                                'fontStyle'  => 'italic',
     5078                                                                                'fontWeight' => '400',
     5079                                                                                'src'        => 'https://example.com/font.ttf',
     5080                                                                        ),
     5081                                                                        array(
     5082                                                                                'fontFamily' => 'Piazzolla',
     5083                                                                                'fontStyle'  => 'italic',
     5084                                                                                'fontWeight' => '400',
     5085                                                                                'src'        => 'https://example.com/font.ttf',
     5086                                                                        ),
     5087                                                                ),
     5088                                                        ),
     5089                                                        array(
     5090                                                                'name'       => 'Inter',
     5091                                                                'slug'       => 'Inter',
     5092                                                                'fontFamily' => 'Inter',
     5093                                                                'fontFace'   => array(
     5094                                                                        array(
     5095                                                                                'fontFamily' => 'Inter',
     5096                                                                                'fontStyle'  => 'italic',
     5097                                                                                'fontWeight' => '400',
     5098                                                                                'src'        => 'https://example.com/font.ttf',
     5099                                                                        ),
     5100                                                                        array(
     5101                                                                                'fontFamily' => 'Inter',
     5102                                                                                'fontStyle'  => 'italic',
     5103                                                                                'fontWeight' => '400',
     5104                                                                                'src'        => 'https://example.com/font.ttf',
     5105                                                                        ),
     5106                                                                ),
     5107                                                        ),
     5108                                                ),
     5109                                        ),
     5110                                ),
     5111                        ),
     5112                );
     5113                $sanitized_theme_json = $theme_json->get_raw_data();
     5114                $this->assertSameSetsWithIndex( $expected_sanitized, $sanitized_theme_json, 'Sanitized theme.json does not match' );
     5115        }
    49815116}
Note: See TracChangeset for help on using the changeset viewer.