Global Styles: Improve sanitization of block variation styles.
Some checks are pending
Cleanup Pull Requests / Clean up pull requests (push) Waiting to run
Coding Standards / PHP coding standards (push) Waiting to run
Coding Standards / JavaScript coding standards (push) Waiting to run
Coding Standards / Slack Notifications (push) Blocked by required conditions
Coding Standards / Failed workflow tasks (push) Blocked by required conditions
End-to-end Tests / Test with SCRIPT_DEBUG disabled (push) Waiting to run
End-to-end Tests / Test with SCRIPT_DEBUG enabled (push) Waiting to run
End-to-end Tests / Slack Notifications (push) Blocked by required conditions
End-to-end Tests / Failed workflow tasks (push) Blocked by required conditions
JavaScript Tests / QUnit Tests (push) Waiting to run
JavaScript Tests / Slack Notifications (push) Blocked by required conditions
JavaScript Tests / Failed workflow tasks (push) Blocked by required conditions
Performance Tests / Determine Matrix (push) Waiting to run
Performance Tests / ${{ matrix.multisite && 'Multisite' || 'Single Site' }} ${{ matrix.memcached && 'Memcached' || 'Default' }} (push) Blocked by required conditions
Performance Tests / Compare (push) Blocked by required conditions
Performance Tests / Slack Notifications (push) Blocked by required conditions
Performance Tests / Failed workflow tasks (push) Blocked by required conditions
PHP Compatibility / Check PHP compatibility (push) Waiting to run
PHP Compatibility / Slack Notifications (push) Blocked by required conditions
PHP Compatibility / Failed workflow tasks (push) Blocked by required conditions
PHPUnit Tests / PHP 7.2 (push) Waiting to run
PHPUnit Tests / PHP 7.3 (push) Waiting to run
PHPUnit Tests / PHP 7.4 (push) Waiting to run
PHPUnit Tests / PHP 8.0 (push) Waiting to run
PHPUnit Tests / PHP 8.1 (push) Waiting to run
PHPUnit Tests / PHP 8.2 (push) Waiting to run
PHPUnit Tests / PHP 8.3 (push) Waiting to run
PHPUnit Tests / PHP 8.4 (push) Waiting to run
PHPUnit Tests / html-api-html5lib-tests (push) Waiting to run
PHPUnit Tests / Slack Notifications (push) Blocked by required conditions
PHPUnit Tests / Failed workflow tasks (push) Blocked by required conditions
Test Build Processes / Core running from build (push) Waiting to run
Test Build Processes / Core running from src (push) Waiting to run
Test Build Processes / Gutenberg running from build (push) Waiting to run
Test Build Processes / Gutenberg running from src (push) Waiting to run
Test Build Processes / Slack Notifications (push) Blocked by required conditions
Test Build Processes / Failed workflow tasks (push) Blocked by required conditions

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
This commit is contained in:
Peter Wilson 2025-02-10 22:27:49 +00:00
parent f71d5f06b2
commit d71f29ff08
2 changed files with 242 additions and 19 deletions

View File

@ -3552,26 +3552,12 @@ class WP_Theme_JSON {
$variation_output = static::remove_insecure_styles( $variation_input );
// Process a variation's elements and element pseudo selector styles.
if ( isset( $variation_input['blocks'] ) ) {
$variation_output['blocks'] = static::remove_insecure_inner_block_styles( $variation_input['blocks'] );
}
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 ( ! empty( $element_output ) ) {
_wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output );
}
}
}
$variation_output['elements'] = static::remove_insecure_element_styles( $variation_input['elements'] );
}
if ( ! empty( $variation_output ) ) {
@ -3609,6 +3595,59 @@ class WP_Theme_JSON {
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.

View File

@ -4706,6 +4706,190 @@ class Tests_Theme_wpThemeJson extends WP_UnitTestCase {
$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.
*