@@ -2075,16 +2075,28 @@ function _filter_block_content_callback( $matches ) {
20752075 * @return array The filtered and sanitized block object result.
20762076 */
20772077function filter_block_kses ( $ block , $ allowed_html , $ allowed_protocols = array () ) {
2078+ /*
2079+ * Per-block custom CSS (attrs.style.css) may contain characters like <, >,
2080+ * and & that are valid in CSS but would be mangled by wp_kses(), which treats
2081+ * all values as HTML. Encode these characters as JSON unicode escapes before
2082+ * KSES runs, then decode afterwards. This is the same approach used for
2083+ * Global Styles custom CSS (see r61486).
2084+ */
2085+ $ has_block_css = isset ( $ block ['attrs ' ]['style ' ]['css ' ] );
2086+ if ( $ has_block_css ) {
2087+ // wp_json_encode wraps the string in quotes: "encoded content".
2088+ // Trim them to get the raw escaped content as a PHP string.
2089+ $ block ['attrs ' ]['style ' ]['css ' ] = trim (
2090+ wp_json_encode ( $ block ['attrs ' ]['style ' ]['css ' ], JSON_HEX_TAG | JSON_HEX_AMP ),
2091+ '" '
2092+ );
2093+ }
2094+
20782095 $ block ['attrs ' ] = filter_block_kses_value ( $ block ['attrs ' ], $ allowed_html , $ allowed_protocols , $ block );
20792096
2080- // Per-block custom CSS (attrs.style.css) may contain & and > as valid
2081- // CSS selectors. wp_kses() entity-encodes these because it treats the
2082- // value as HTML. Decode them after KSES has already stripped any
2083- // dangerous HTML tags, so the CSS round-trips correctly through
2084- // serialize_block_attributes().
2085- if ( isset ( $ block ['attrs ' ]['style ' ]['css ' ] ) ) {
2086- $ block ['attrs ' ]['style ' ]['css ' ] = undo_block_custom_css_kses_entities (
2087- $ block ['attrs ' ]['style ' ]['css ' ]
2097+ if ( $ has_block_css && isset ( $ block ['attrs ' ]['style ' ]['css ' ] ) ) {
2098+ $ block ['attrs ' ]['style ' ]['css ' ] = json_decode (
2099+ '" ' . $ block ['attrs ' ]['style ' ]['css ' ] . '" '
20882100 );
20892101 }
20902102
@@ -2135,40 +2147,6 @@ function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = ar
21352147 return $ value ;
21362148}
21372149
2138- /**
2139- * Decodes HTML entities in per-block custom CSS that were incorrectly
2140- * introduced by wp_kses() during the block KSES filtering pipeline.
2141- *
2142- * Per-block custom CSS (stored in attrs.style.css) may contain & and >
2143- * as valid CSS selectors (nesting and child combinator). When wp_kses()
2144- * processes this CSS string as if it were HTML, it entity-encodes these
2145- * characters (&, >). If the block is then re-serialized via
2146- * serialize_block_attributes(), the entity's ampersand is escaped again
2147- * (\u0026amp;), producing a double-encoded value that corrupts the CSS
2148- * on subsequent editor loads.
2149- *
2150- * This reverses only the specific named entities that wp_kses() may
2151- * introduce, intentionally narrower than wp_specialchars_decode() to
2152- * avoid decoding numeric/hex references that KSES intentionally preserved.
2153- *
2154- * @since 7.0
2155- *
2156- * @param string $value Per-block custom CSS string potentially containing
2157- * KSES-introduced entities.
2158- * @return string CSS string with KSES-introduced entities decoded.
2159- */
2160- function undo_block_custom_css_kses_entities ( $ value ) {
2161- if ( ! is_string ( $ value ) || false === strpos ( $ value , '& ' ) ) {
2162- return $ value ;
2163- }
2164-
2165- return str_replace (
2166- array ( '& ' , '> ' , '" ' , '' ' ),
2167- array ( '& ' , '> ' , '" ' , "' " ),
2168- $ value
2169- );
2170- }
2171-
21722150/**
21732151 * Sanitizes the value of the Template Part block's `tagName` attribute.
21742152 *
0 commit comments