diff --git a/src/wp-includes/customize/class-wp-customize-custom-css-setting.php b/src/wp-includes/customize/class-wp-customize-custom-css-setting.php index aab0e475304ea..0d8324e367a3a 100644 --- a/src/wp-includes/customize/class-wp-customize-custom-css-setting.php +++ b/src/wp-includes/customize/class-wp-customize-custom-css-setting.php @@ -144,35 +144,6 @@ public function value() { return $value; } - /** - * Validate a received value for being valid CSS. - * - * Checks for imbalanced braces, brackets, and comments. - * Notifications are rendered when the customizer state is saved. - * - * @since 4.7.0 - * @since 4.9.0 Checking for balanced characters has been moved client-side via linting in code editor. - * @since 5.9.0 Renamed `$css` to `$value` for PHP 8 named parameter support. - * - * @param string $value CSS to validate. - * @return true|WP_Error True if the input was validated, otherwise WP_Error. - */ - public function validate( $value ) { - // Restores the more descriptive, specific name for use within this method. - $css = $value; - - $validity = new WP_Error(); - - if ( preg_match( '#add( 'illegal_markup', __( 'Markup is not allowed in CSS.' ) ); - } - - if ( ! $validity->has_errors() ) { - $validity = parent::validate( $css ); - } - return $validity; - } - /** * Store the CSS setting value in the custom_css custom post type for the stylesheet. * diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index 0ff915cbe4263..bb6498f23dbae 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -2144,6 +2144,13 @@ function wp_update_custom_css_post( $css, $args = array() ) { // Update post if it already exists, otherwise create a new one. $post = wp_get_custom_css_post( $args['stylesheet'] ); + + // Remove KSES HTML filters to prevent CSS mangling. + $priority = has_filter( 'content_save_pre', 'wp_filter_post_kses' ); + if ( false !== $priority ) { + remove_filter( 'content_save_pre', 'wp_filter_post_kses', $priority ); + } + if ( $post ) { $post_data['ID'] = $post->ID; $r = wp_update_post( wp_slash( $post_data ), true ); @@ -2163,6 +2170,10 @@ function wp_update_custom_css_post( $css, $args = array() ) { } } + if ( false !== $priority ) { + add_filter( 'content_save_pre', 'wp_filter_post_kses', $priority ); + } + if ( is_wp_error( $r ) ) { return $r; } diff --git a/tests/phpunit/tests/customize/custom-css-setting.php b/tests/phpunit/tests/customize/custom-css-setting.php index 65cc3f717fe59..e478f73a41c3f 100644 --- a/tests/phpunit/tests/customize/custom-css-setting.php +++ b/tests/phpunit/tests/customize/custom-css-setting.php @@ -268,6 +268,27 @@ public function test_get_custom_css_post_queries_after_failed_lookup() { $this->assertSame( get_num_queries(), $queries_before ); } + /** + * Ensure that dangerous STYLE tag contents do not break HTML output. + * + * @ticket 64418 + */ + public function test_wp_custom_css_cb_escapes_dangerous_html() { + wp_update_custom_css_post( + '*::before { content: ""; }', + array( + 'stylesheet' => $this->setting->stylesheet, + ) + ); + $output = get_echo( 'wp_custom_css_cb' ); + $expected = <<<'HTML' + +HTML; + $this->assertEqualHTML( $expected, $output ); + } + /** * Test that wp_update_custom_css_post() updates the 'custom_css_post_id' theme mod. * @@ -373,29 +394,4 @@ public function filter_update_custom_css_data( $data, $args ) { $data['post_title'] = 'Ignored'; return $data; } - - /** - * Tests that validation errors are caught appropriately. - * - * Note that the $validity \WP_Error object must be reset each time - * as it picks up the Errors and passes them to the next assertion. - * - * @covers WP_Customize_Custom_CSS_Setting::validate - */ - public function test_validate() { - - // Empty CSS throws no errors. - $result = $this->setting->validate( '' ); - $this->assertTrue( $result ); - - // Basic, valid CSS throws no errors. - $basic_css = 'body { background: #f00; } h1.site-title { font-size: 36px; } a:hover { text-decoration: none; } input[type="text"] { padding: 1em; }'; - $result = $this->setting->validate( $basic_css ); - $this->assertTrue( $result ); - - // Check for markup. - $unclosed_comment = $basic_css . ''; - $result = $this->setting->validate( $unclosed_comment ); - $this->assertArrayHasKey( 'illegal_markup', $result->errors ); - } }