From 292f17d1024a14f06a4e1cd6636f856ee5781c2c Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Sun, 1 Mar 2026 16:39:58 -0600 Subject: [PATCH 1/2] Fix problem with encoding of css entities when post with block level custom css is edited by user without unfiltered_html --- src/wp-includes/blocks.php | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 89007d0d0d036..ac3b0b1b7f33c 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -2077,6 +2077,17 @@ function _filter_block_content_callback( $matches ) { function filter_block_kses( $block, $allowed_html, $allowed_protocols = array() ) { $block['attrs'] = filter_block_kses_value( $block['attrs'], $allowed_html, $allowed_protocols, $block ); + // Per-block custom CSS (attrs.style.css) may contain & and > as valid + // CSS selectors. wp_kses() entity-encodes these because it treats the + // value as HTML. Decode them after KSES has already stripped any + // dangerous HTML tags, so the CSS round-trips correctly through + // serialize_block_attributes(). + if ( isset( $block['attrs']['style']['css'] ) ) { + $block['attrs']['style']['css'] = undo_block_custom_css_kses_entities( + $block['attrs']['style']['css'] + ); + } + if ( is_array( $block['innerBlocks'] ) ) { foreach ( $block['innerBlocks'] as $i => $inner_block ) { $block['innerBlocks'][ $i ] = filter_block_kses( $inner_block, $allowed_html, $allowed_protocols ); @@ -2124,6 +2135,40 @@ function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = ar return $value; } +/** + * Decodes HTML entities in per-block custom CSS that were incorrectly + * introduced by wp_kses() during the block KSES filtering pipeline. + * + * Per-block custom CSS (stored in attrs.style.css) may contain & and > + * as valid CSS selectors (nesting and child combinator). When wp_kses() + * processes this CSS string as if it were HTML, it entity-encodes these + * characters (&, >). If the block is then re-serialized via + * serialize_block_attributes(), the entity's ampersand is escaped again + * (\u0026amp;), producing a double-encoded value that corrupts the CSS + * on subsequent editor loads. + * + * This reverses only the specific named entities that wp_kses() may + * introduce, intentionally narrower than wp_specialchars_decode() to + * avoid decoding numeric/hex references that KSES intentionally preserved. + * + * @since 6.9.0 + * + * @param string $value Per-block custom CSS string potentially containing + * KSES-introduced entities. + * @return string CSS string with KSES-introduced entities decoded. + */ +function undo_block_custom_css_kses_entities( $value ) { + if ( ! is_string( $value ) || false === strpos( $value, '&' ) ) { + return $value; + } + + return str_replace( + array( '&', '>', '"', ''' ), + array( '&', '>', '"', "'" ), + $value + ); +} + /** * Sanitizes the value of the Template Part block's `tagName` attribute. * From 51f3c125f41c8b9c01f7b5b656bfba5509c46a38 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Sun, 1 Mar 2026 17:00:07 -0600 Subject: [PATCH 2/2] Fix WP version --- src/wp-includes/blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index ac3b0b1b7f33c..501ebb383c8a3 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -2151,7 +2151,7 @@ function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = ar * introduce, intentionally narrower than wp_specialchars_decode() to * avoid decoding numeric/hex references that KSES intentionally preserved. * - * @since 6.9.0 + * @since 7.0 * * @param string $value Per-block custom CSS string potentially containing * KSES-introduced entities.