Skip to content

Commit

Permalink
Global Styles: Improve sanitization of block variation styles.
Browse files Browse the repository at this point in the history
Fixes an issue where block style variations containing inner block type and element styles would have those inner styles stripped when the user attempting to save Global Styles does not have the `unfiltered_html` capability.

Props aaronrobertshaw, mukesh27, andrewserong.
Fixes #62372.


git-svn-id: https://develop.svn.wordpress.org/trunk@59802 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
peterwilsoncc committed Feb 10, 2025
1 parent f71d5f0 commit d71f29f
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 19 deletions.
77 changes: 58 additions & 19 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -3552,26 +3552,12 @@ public static function remove_insecure_properties( $theme_json, $origin = 'theme

$variation_output = static::remove_insecure_styles( $variation_input );

// Process a variation's elements and element pseudo selector styles.
if ( isset( $variation_input['elements'] ) ) {
foreach ( $valid_element_names as $element_name ) {
$element_input = $variation_input['elements'][ $element_name ] ?? null;
if ( $element_input ) {
$element_output = static::remove_insecure_styles( $element_input );

if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
if ( isset( $element_input[ $pseudo_selector ] ) ) {
$element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
}
}
}
if ( isset( $variation_input['blocks'] ) ) {
$variation_output['blocks'] = static::remove_insecure_inner_block_styles( $variation_input['blocks'] );
}

if ( ! empty( $element_output ) ) {
_wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output );
}
}
}
if ( isset( $variation_input['elements'] ) ) {
$variation_output['elements'] = static::remove_insecure_element_styles( $variation_input['elements'] );
}

if ( ! empty( $variation_output ) ) {
Expand Down Expand Up @@ -3609,6 +3595,59 @@ public static function remove_insecure_properties( $theme_json, $origin = 'theme
return $theme_json;
}

/**
* Remove insecure element styles within a variation or block.
*
* @since 6.8.0
*
* @param array $elements The elements to process.
* @return array The sanitized elements styles.
*/
protected static function remove_insecure_element_styles( $elements ) {
$sanitized = array();
$valid_element_names = array_keys( static::ELEMENTS );

foreach ( $valid_element_names as $element_name ) {
$element_input = $elements[ $element_name ] ?? null;
if ( $element_input ) {
$element_output = static::remove_insecure_styles( $element_input );

if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
if ( isset( $element_input[ $pseudo_selector ] ) ) {
$element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
}
}
}

$sanitized[ $element_name ] = $element_output;
}
}
return $sanitized;
}

/**
* Remove insecure styles from inner blocks and their elements.
*
* @since 6.8.0
*
* @param array $blocks The block styles to process.
* @return array Sanitized block type styles.
*/
protected static function remove_insecure_inner_block_styles( $blocks ) {
$sanitized = array();
foreach ( $blocks as $block_type => $block_input ) {
$block_output = static::remove_insecure_styles( $block_input );

if ( isset( $block_input['elements'] ) ) {
$block_output['elements'] = static::remove_insecure_element_styles( $block_input['elements'] );
}

$sanitized[ $block_type ] = $block_output;
}
return $sanitized;
}

/**
* Processes a setting node and returns the same node
* without the insecure settings.
Expand Down
184 changes: 184 additions & 0 deletions tests/phpunit/tests/theme/wpThemeJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -4706,6 +4706,190 @@ public function test_block_style_variations_with_invalid_properties() {
$this->assertSameSetsWithIndex( $expected, $actual );
}

/**
* Test ensures that inner block type styles and their element styles are
* preserved for block style variations when removing insecure properties.
*
* @ticket 62372
*/
public function test_block_style_variations_with_inner_blocks_and_elements() {
wp_set_current_user( static::$administrator_id );
register_block_style(
array( 'core/group' ),
array(
'name' => 'custom-group',
'label' => 'Custom Group',
)
);

$expected = array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/group' => array(
'color' => array(
'background' => 'blue',
),
'variations' => array(
'custom-group' => array(
'color' => array(
'background' => 'purple',
),
'blocks' => array(
'core/paragraph' => array(
'color' => array(
'text' => 'red',
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'blue',
),
':hover' => array(
'color' => array(
'text' => 'green',
),
),
),
),
),
'core/heading' => array(
'typography' => array(
'fontSize' => '24px',
),
),
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'yellow',
),
':hover' => array(
'color' => array(
'text' => 'orange',
),
),
),
),
),
),
),
),
),
);

$actual = WP_Theme_JSON::remove_insecure_properties( $expected );

// The sanitization processes blocks in a specific order which might differ to the theme.json input.
$this->assertEqualsCanonicalizing(
$expected,
$actual,
'Block style variations data does not match when inner blocks or element styles present'
);
}

/**
* Test ensures that inner block type styles and their element styles for block
* style variations have all unsafe values removed.
*
* @ticket 62372
*/
public function test_block_style_variations_with_invalid_inner_block_or_element_styles() {
wp_set_current_user( static::$administrator_id );
register_block_style(
array( 'core/group' ),
array(
'name' => 'custom-group',
'label' => 'Custom Group',
)
);

$input = array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/group' => array(
'variations' => array(
'custom-group' => array(
'blocks' => array(
'core/paragraph' => array(
'color' => array(
'text' => 'red',
),
'typography' => array(
'fontSize' => 'alert(1)', // Should be removed.
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'blue',
),
'css' => 'unsafe-value', // Should be removed.
),
),
'custom' => 'unsafe-value', // Should be removed.
),
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'yellow',
),
'javascript' => 'alert(1)', // Should be removed.
),
),
),
),
),
),
),
);

$expected = array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/group' => array(
'variations' => array(
'custom-group' => array(
'blocks' => array(
'core/paragraph' => array(
'color' => array(
'text' => 'red',
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'blue',
),
),
),
),
),
'elements' => array(
'link' => array(
'color' => array(
'text' => 'yellow',
),
),
),
),
),
),
),
),
);

$actual = WP_Theme_JSON::remove_insecure_properties( $input );

// The sanitization processes blocks in a specific order which might differ to the theme.json input.
$this->assertEqualsCanonicalizing(
$expected,
$actual,
'Insecure properties were not removed from block style variation inner block types or elements'
);
}

/**
* Tests generating the spacing presets array based on the spacing scale provided.
*
Expand Down

0 comments on commit d71f29f

Please sign in to comment.