[wp-trac] [WordPress Trac] #64418: Valid CSS is causing failure in the Additional CSS panel
WordPress Trac
noreply at wordpress.org
Thu Dec 18 18:48:49 UTC 2025
#64418: Valid CSS is causing failure in the Additional CSS panel
--------------------------+-------------------------
Reporter: drw158 | Owner: jonsurrell
Type: defect (bug) | Status: assigned
Priority: normal | Milestone: 7.0
Component: Customize | Version: 4.7
Severity: normal | Resolution:
Keywords: has-patch | Focuses: css
--------------------------+-------------------------
Comment (by westonruter):
Replying to [comment:11 westonruter]:
> @dmsnell @jonsurrell I'm not sure that KSES applies here? The
`wp_custom_css_cb()` function outputs the CSS without KSES. It just uses
`strip_tags()`.
Actually, after looking at #38715, I am reminded that KSES may actually be
a problem here. It's not that that we need to fix KSES but rather to
prevent KSES from running in the first place.
For the Customizer's Custom CSS, the CSS is stored in a `custom_css` CPT
via `wp_update_custom_css_post()` which does:
{{{#!php
<?php
$r = wp_insert_post( wp_slash( $post_data ), true );
}}}
This means if a user can't do `unfiltered_html`, the data will get
sanitized with KSES.
I believe we need a workaround similar to what was done in
`WP_Customize_Manager::save_changeset_post()` for storing Customizer
changesets (the `customize_changeset` CPT) where it wraps the call to
`wp_update_post()` as follows:
{{{#!php
<?php
add_filter( 'wp_insert_post_data', array( $this,
'preserve_insert_changeset_post_content' ), 5, 3 );
// ...
$r = wp_update_post( wp_slash( $post_array ), true );
// ...
remove_filter( 'wp_insert_post_data', array( $this,
'preserve_insert_changeset_post_content' ), 5 );
}}}
The `WP_Customize_Manager::preserve_insert_changeset_post_content()`
method looks like this:
{{{#!php
<?php
public function preserve_insert_changeset_post_content( $data,
$postarr, $unsanitized_postarr ) {
if (
isset( $data['post_type'] ) &&
isset( $unsanitized_postarr['post_content'] ) &&
'customize_changeset' === $data['post_type'] ||
(
'revision' === $data['post_type'] &&
! empty( $data['post_parent'] ) &&
'customize_changeset' === get_post_type(
$data['post_parent'] )
)
) {
$data['post_content'] =
$unsanitized_postarr['post_content'];
}
return $data;
}
}}}
It's ugly, but it bypasses KSES corruption. It also prevents other plugins
from corrupting the JSON stored in the `post_content` in arbitrary ways,
which I recall being the added benefit to this approach. Otherwise, this
could seem to be achieved via:
{{{#!php
<?php
$priority = has_filter( 'content_save_pre', 'wp_filter_post_kses' );
if ( false !== $priority ) {
remove_filter( 'content_save_pre', 'wp_filter_post_kses', $priority );
}
$r = wp_update_post( wp_slash( $post_array ), true );
if ( false !== $priority ) {
add_filter( 'content_save_pre', 'wp_filter_post_kses', $priority );
}
}}}
I see this is also the case for the `wp_global_styles` CPT in addition to
`custom_css`. Note how `WP_Theme_JSON_Resolver::get_user_data()` somewhat
anticipates this by checking for JSON decoding errors. It doesn't seem
that `WP_REST_Global_Styles_Controller` is doing anything to prevent KSES
from applying. In contrast, `kses_init_filters()` actually adds a filter
specifically for `wp_global_styles`:
{{{#!php
<?php
// Global Styles filtering: Global Styles filters should be executed
before normal post_kses HTML filters.
add_filter( 'content_save_pre', 'wp_filter_global_styles_post', 9 );
add_filter( 'content_filtered_save_pre', 'wp_filter_global_styles_post', 9
);
}}}
Note that I can't seem to access Additional CSS in the Site Editor when
`DISALLOW_UNFILTERED_HTML` is enabled, so this KSES issue may not be
relevant here.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/64418#comment:16>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list