Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
7c01020
Relax style tag contents validation
sirreal Dec 17, 2025
511284e
Apply suggestions from code review
sirreal Dec 17, 2025
edca2a2
Update comments and document in since annotation
sirreal Dec 17, 2025
db273b7
Update customizer error message
sirreal Dec 17, 2025
62d59c2
Update wp_custom_css_cb to rely on HTML API for safe SCRIPT tag print…
sirreal Dec 17, 2025
f3a1716
Wrap customizer CSS test in newlines
sirreal Dec 17, 2025
e793caa
Use HTML API for style tags in script-loader
sirreal Dec 17, 2025
aa7852c
Use HTML Tag Processor to produce WP_Styles style tags
sirreal Dec 17, 2025
93cddc3
Use HTML Tag Processor for STYLE tags in theme.php
sirreal Dec 17, 2025
41a1fe3
PICKME: Update styles tests to use assertEqualHTML
sirreal Dec 17, 2025
99b8733
Simplify conditional style no-print test
sirreal Dec 17, 2025
2ea6a12
Correctly check presence with stripos
sirreal Dec 18, 2025
81679af
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 19, 2025
e3a6d78
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 22, 2025
2c19861
Update failing test, add test for allowed CSS
sirreal Dec 22, 2025
05a45e1
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 23, 2025
c124eb5
Update wp_custom_css_cb to rely on HTML API for safe SCRIPT tag print…
sirreal Dec 17, 2025
e055156
Wrap customizer CSS test in newlines
sirreal Dec 17, 2025
33f9616
Use HTML API for style tags in script-loader
sirreal Dec 17, 2025
606539e
Use HTML Tag Processor to produce WP_Styles style tags
sirreal Dec 17, 2025
c938d4c
Use HTML Tag Processor for STYLE tags in theme.php
sirreal Dec 17, 2025
dd919f1
Build font style tags with HTML API
sirreal Dec 23, 2025
d29900a
PICKME: Update font tests to use semantic HTML comparison
sirreal Dec 23, 2025
6c6a72b
Use HTML API for hide header text
sirreal Dec 23, 2025
aad4744
Revert "Use HTML API for hide header text"
sirreal Dec 23, 2025
96849ff
Disable KSES content filter, prevent mangling CSS-in-JSON
sirreal Dec 26, 2025
1ab58ec
Revert "Disable KSES content filter, prevent mangling CSS-in-JSON"
sirreal Dec 26, 2025
ac2c8e6
Escape "<" and ">" in JSON to protect tag-like syntax
sirreal Dec 26, 2025
c3ae9a9
Merge branch 'trunk' into styles/use-html-api-for-style-tags
sirreal Dec 26, 2025
4e88745
Fix lint
sirreal Dec 26, 2025
c8f8a4d
Add test with bare & to trick KSES more
sirreal Dec 26, 2025
731bce7
Escape "&" in JSON to protect & and things that appear to be HTML cha…
sirreal Dec 26, 2025
1eb76e7
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 29, 2025
99b1ba4
Fix lint
sirreal Dec 26, 2025
d296d6c
Merge branch 'trunk' into styles/use-html-api-for-style-tags
sirreal Dec 29, 2025
0050789
Revert HTML API STYLE tag generation
sirreal Dec 29, 2025
d3ac6a8
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 29, 2025
b185a8c
Improve invalid CSS message.
sirreal Dec 29, 2025
96ea3c4
Remove arbitrary CSS content validation
sirreal Dec 29, 2025
56e19b5
Revert customizer custom_css changes
sirreal Dec 29, 2025
d9ae831
Encode data in the prepare_for_database method
sirreal Dec 29, 2025
12a6775
Merge branch 'styles/use-html-api-for-style-tags' into 64418/improve-…
sirreal Dec 29, 2025
0141653
Restore STYLE tag trailing newline
sirreal Dec 29, 2025
6585099
Restore STYLE tag trailing newlines in theme.php
sirreal Dec 29, 2025
5a8ce12
Merge branch 'styles/use-html-api-for-style-tags' into 64418/improve-…
sirreal Dec 29, 2025
30ca7cb
Cross-reference escaping in REST controller
sirreal Dec 29, 2025
e1322b0
Deprecate validate_custom_css method
sirreal Dec 30, 2025
407d43f
Move trailing newline out of Tag Processor
sirreal Dec 30, 2025
ccf1625
Merge branch 'styles/use-html-api-for-style-tags' into 64418/improve-…
sirreal Dec 30, 2025
adb0a13
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Dec 30, 2025
6a9a7b0
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Jan 2, 2026
793eed8
Revert "Deprecate validate_custom_css method"
sirreal Jan 2, 2026
cc5c89e
Restore check that CSS does not contain "</style"
sirreal Jan 2, 2026
4eb526b
Implement containin or ending in partial style close tag algoritm
sirreal Jan 5, 2026
0bd4e51
lints
sirreal Jan 5, 2026
0c8e3a0
Add another not close tag tst
sirreal Jan 5, 2026
9d65051
Improve error messages
sirreal Jan 5, 2026
846d9d2
Restore illegal markup test
sirreal Jan 5, 2026
23a1c96
Fix up error messages, add error message tests
sirreal Jan 5, 2026
0f43cc1
Fix ::class fatal error
sirreal Jan 5, 2026
0768d90
fixup! Fix ::class fatal error
sirreal Jan 5, 2026
c3cda32
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Jan 8, 2026
3e5d018
Fix typo in comment
sirreal Jan 8, 2026
b9b5315
Pesky comment alignment
sirreal Jan 9, 2026
a36dc2f
Add comment describing full or partial style tag matching
sirreal Jan 12, 2026
842e35e
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Jan 12, 2026
e8b1ea5
Styles docs updates, add link to spec, add examples, update @since desc
dmsnell Jan 13, 2026
4c492c3
Add trailing parens to function name in PHPdoc
sirreal Jan 13, 2026
fc47b77
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Jan 13, 2026
82a4b3f
Add function documentation to data providers
sirreal Jan 13, 2026
5e891a2
Merge branch 'trunk' into 64418/improve-custom-css-handling
sirreal Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/wp-includes/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -2386,7 +2386,13 @@ function wp_filter_global_styles_post( $data ) {
$data_to_encode = WP_Theme_JSON::remove_insecure_properties( $decoded_data, 'custom' );

$data_to_encode['isGlobalStylesUserThemeJSON'] = true;
return wp_slash( wp_json_encode( $data_to_encode ) );
/**
* JSON encode the data stored in post content.
* Escape characters that are likely to be mangled by HTML filters: "<>&".
*
* This matches the escaping in {@see WP_REST_Global_Styles_Controller::prepare_item_for_database()}.
*/
return wp_slash( wp_json_encode( $data_to_encode, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is a surprising function, and one perhaps that warrants considerably more refactoring. if my understanding is correct, this is attempting to JSON-parse every single post of every single kind of post type on save, even though its name seems to suggest that it’s designed to process global styles posts.

maybe @jorgefilipecosta has some context that is’t obvious.

at a minimum, it surprises me we aren’t even checking if the first and last bytes are { and }, since those are the only allowable JSON. on the positive side, I guess json_decode() will fail quickly, but only after cloning the string and performing string replacements on it via wp_unslash().


on to the question I wanted to discuss on this line, which is a question about backwards compatibility: will this change any existing code? or should it preserve properly given the fact that this should run directly into json_decode() on the other end and parse to the same values?

I would want us to be careful about anything else in the pipeline which might start interpreting content differently given the escaping. my guess is that this is fine, but I’d like to have some confirmation on some of the differences in encoding and how those will be interpreted later on and in the editor and on render.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this change any existing code? or should it preserve properly given the fact that this should run directly into json_decode() on the other end and parse to the same values?

I'm not deeply familiar with global styles, so I was happy to get @ramonjd's review (and more are welcome).

It's hard to imagine using this data without parsing the JSON. Compliant JSON parsers will parse this correctly. Anything that relies on this data being JSON encoded in a specific way (with specific escaping) would be surprising and does not seem like behavior that needs to be supported.

That said, I know that the global styles system will parse the JSON:

$decoded_data = json_decode( $user_cpt['post_content'], true );

As will the REST endpoint:

$raw_config = json_decode( $post->post_content, true );

The REST API later responds with a JSON body, but that encoding happens elsewhere and the client is expected to parse the JSON response.

Copy link
Member

@ramonjd ramonjd Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this change any existing code? or should it preserve properly given the fact that this should run directly into json_decode() on the other end and parse to the same values?

In my limited knowledge, and as far as I'm aware it's expected that the content will eventually meet json_decode(), which handles things identically.

Should there be a test case to confirm this?

if my understanding is correct, this is attempting to JSON-parse every single post of every single kind of post type on save

Oh I see it's running through _save_pre hooks, so I understand the same. Bit before my time, but I like your optimization idea to check for { and } before parsing.

My intellectual crutch on these matters is usually @oandregal and @andrewserong

That said, I know that the global styles system will parse the JSON:
As will the REST endpoint:

This is the state as I understand it as well:

  • json_decode in WP_Theme_JSON_Resolver to parse and merge user data for the most recent published post for internal processing, rendering CSS etc
  • json_decode in WP_REST_Global_Styles_Controller to parse handle a specific wp_global_styles post by ID in REST

Cheers!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if my understanding is correct, this is attempting to JSON-parse every single post of every single kind of post type on save, even though its name seems to suggest that it’s designed to process global styles posts.
(...)
at a minimum, it surprises me we aren’t even checking if the first and last bytes are { and }, since those are the only allowable JSON

Yeah, this is correct. IIRC, it was introduced to take into account theme.json data that didn't come from the Global Styles sidebar and so that we didn't control: for example, importing it.

The suggested improvement could be nice as an early return. There's no situation in which theme.json is not a JSON structured data.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unfamiliar with how custom CSS was implemented in global styles, but checking WP_Theme_JSON::remove_insecure_properties that acts as a security step for every property, I've noticed that custom CSS skipped and returned verbatim. I wonder if we could sanitize it at that point instead. That way, we'll be dealing with any present and future input variant because remove_insecure_properties is expected to be used in all paths that deal with saving theme.json content.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That verbatim check is gated on edit_css which is analogous to unfiltered_html. I think it's common for users with the unfiltered_html capability to bypass checks on content.

I'd prefer to keep any changes to that location separate from this PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is a surprising function, and one perhaps that warrants considerably more refactoring. if my understanding is correct, this is attempting to JSON-parse every single post of every single kind of post type on save, even though its name seems to suggest that it’s designed to process global styles posts.

Yah we do that for any type of post type, that happens because the WordPress security sanitization is per field, so content is sanitized without knowing what the post type field is. I guess this is intentional to avoid cases where there is unsafe content in a post type (e.g: a malicious global styles payload) that content is inserted using a different post type, and then somehow e.g: plug-in or something changes the post type.
We do this filtering at the kses level because as @oandregal referred we don't control all the cases that could be used to create a post, could be rpc, imports etc there are many ways.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn’t help, it kept staring at me, but I didn’t push any changes.

diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php
index bff9cc38ba..ad0da1e0e3 100644
--- a/src/wp-includes/kses.php
+++ b/src/wp-includes/kses.php
@@ -2376,6 +2376,22 @@ function wp_filter_post_kses( $data ) {
  * @return string Filtered post content with unsafe rules removed.
  */
 function wp_filter_global_styles_post( $data ) {
+	// As only JSON objects are allowed, skip attempting the parse if it’s not possible.
+	if ( ! is_string( $data ) ) {
+		return $data;
+	}
+
+	/**
+	 * PHP implements RFC7159, which skips leading and trailing whitespace.
+	 * After this initial whitespace, the first character _must_ be %x7B.
+	 *
+	 * @see https://datatracker.ietf.org/doc/html/rfc7159
+	 */
+	$first_non_ws_at = strspn( $data, " \t\r\n" );
+	if ( $first_non_ws_at >= strlen( $data ) || '{' !== $data[ $first_non_ws_at ] ) {
+		return $data;
+	}
+
 	$decoded_data        = json_decode( wp_unslash( $data ), true );
 	$json_decoding_error = json_last_error();
 	if (

I don’t have time to collect the data, but this avoids the initial processing and allocation costs for strings which cannot be JSON objects. it would involve some review to assert that the code is right.

I did check the implementation inside of PHP and the json_decode() function does look specifically at those characters. And blimey, that part of that function was changed last week, though only in a way to provide location data for the whitespace.

    NL      = "\r"? "\n" ;
    WS      = [ \t\r]+ ;

https://github.com/php/php-src/blob/c34b84ed81aacc80bb800b3aca15d71eef2e84c9/ext/json/json_scanner.re#L131-L132

    <JS>NL                   {
        PHP_JSON_TOKEN_LOCATION(last_line)++;
        PHP_JSON_TOKEN_LOCATION(last_column) = 1;
        goto std;
    }
    <JS>WS                   {
        PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH();
        goto std;
    }

https://github.com/php/php-src/blob/c34b84ed81aacc80bb800b3aca15d71eef2e84c9/ext/json/json_scanner.re#L230-L238

}
return $data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,14 @@ protected function prepare_item_for_database( $request ) {
}
$config['isGlobalStylesUserThemeJSON'] = true;
$config['version'] = WP_Theme_JSON::LATEST_SCHEMA;
$changes->post_content = wp_json_encode( $config );
/**
* JSON encode the data stored in post content.
* Escape characters that are likely to be mangled by HTML filters: "<>&".
*
* This data is later re-encoded by {@see wp_filter_global_styles_post()}.
* The escaping is also applied here as a precaution.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thus bypassing the need to do any KSES dance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly.

*/
$changes->post_content = wp_json_encode( $config, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
}

// Post title.
Expand Down Expand Up @@ -659,22 +666,87 @@ public function get_theme_items( $request ) {
/**
* Validate style.css as valid CSS.
*
* Currently just checks for invalid markup.
* Currently just checks that CSS will not break an HTML STYLE tag.
*
* @since 6.2.0
* @since 6.4.0 Changed method visibility to protected.
* @since 7.0.0 Only restricts contents which risk prematurely closing the STYLE element,
* either through a STYLE end tag or a prefix of one which might become a
* full end tag when combined with the contents of other styles.
*
* @param string $css CSS to validate.
* @return true|WP_Error True if the input was validated, otherwise WP_Error.
*/
protected function validate_custom_css( $css ) {
if ( preg_match( '#</?\w+#', $css ) ) {
return new WP_Error(
'rest_custom_css_illegal_markup',
__( 'Markup is not allowed in CSS.' ),
array( 'status' => 400 )
$length = strlen( $css );
for (
$at = strcspn( $css, '<' );
$at < $length;
$at += strcspn( $css, '<', ++$at )
) {
$remaining_strlen = $length - $at;
/**
* Custom CSS text is expected to render inside an HTML STYLE element.
* A STYLE closing tag must not appear within the CSS text because it
* would close the element prematurely.
*
* The text must also *not* end with a partial closing tag (e.g., `<`,
* `</`, … `</style`) because subsequent styles which are concatenated
* could complete it, forming a valid `</style>` tag.
*
* Example:
*
* $style_a = 'p { font-weight: bold; </sty';
* $style_b = 'le> gotcha!';
* $combined = "{$style_a}{$style_b}";
*
* $style_a = 'p { font-weight: bold; </style';
* $style_b = 'p > b { color: red; }';
* $combined = "{$style_a}\n{$style_b}";
*
* Note how in the second example, both of the style contents are benign
* when analyzed on their own. The first style was likely the result of
* improper truncation, while the second is perfectly sound. It was only
* through concatenation that these two scripts combined to form content
* that would have broken out of the containing STYLE element, thus
* corrupting the page and potentially introducing security issues.
*
* @see https://html.spec.whatwg.org/multipage/parsing.html#rawtext-end-tag-name-state
*/
$possible_style_close_tag = 0 === substr_compare(
$css,
'</style',
$at,
min( 7, $remaining_strlen ),
true
);
if ( $possible_style_close_tag ) {
if ( $remaining_strlen < 8 ) {
return new WP_Error(
'rest_custom_css_illegal_markup',
sprintf(
/* translators: %s is the CSS that was provided. */
__( 'The CSS must not end in "%s".' ),
esc_html( substr( $css, $at ) )
),
array( 'status' => 400 )
);
}

if ( 1 === strspn( $css, " \t\f\r\n/>", $at + 7, 1 ) ) {
return new WP_Error(
'rest_custom_css_illegal_markup',
sprintf(
/* translators: %s is the CSS that was provided. */
__( 'The CSS must not contain "%s".' ),
esc_html( substr( $css, $at, 8 ) )
),
array( 'status' => 400 )
);
}
}
}

return true;
}
}
122 changes: 121 additions & 1 deletion tests/phpunit/tests/rest-api/rest-global-styles-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ public function test_update_item_valid_styles_css() {
/**
* @covers WP_REST_Global_Styles_Controller::update_item
* @ticket 57536
* @ticket 64418
*/
public function test_update_item_invalid_styles_css() {
wp_set_current_user( self::$admin_id );
Expand All @@ -659,7 +660,7 @@ public function test_update_item_invalid_styles_css() {
$request = new WP_REST_Request( 'PUT', '/wp/v2/global-styles/' . self::$global_styles_id );
$request->set_body_params(
array(
'styles' => array( 'css' => '<p>test</p> body { color: red; }' ),
'styles' => array( 'css' => '</style>' ),
)
);
$response = rest_get_server()->dispatch( $request );
Expand Down Expand Up @@ -826,4 +827,123 @@ public function test_global_styles_route_args_schema() {
$this->assertArrayHasKey( 'type', $route_data[0]['args']['id'] );
$this->assertSame( 'integer', $route_data[0]['args']['id']['type'] );
}

/**
* @covers WP_REST_Global_Styles_Controller::update_item
* @ticket 64418
*/
public function test_update_allows_valid_css_with_more_syntax() {
wp_set_current_user( self::$admin_id );
if ( is_multisite() ) {
grant_super_admin( self::$admin_id );
}
$request = new WP_REST_Request( 'PUT', '/wp/v2/global-styles/' . self::$global_styles_id );
$css = <<<'CSS'
@property --animate {
syntax: "<custom-ident>";
inherits: true;
initial-value: false;
}
h1::before { content: "fun & games"; }
CSS;
$request->set_body_params(
array(
'styles' => array( 'css' => $css ),
)
);

$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( $css, $data['styles']['css'] );

// Compare expected API output to WP internal values.
$request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id );
$response = rest_get_server()->dispatch( $request );
$this->assertSame( $css, $response->get_data()['styles']['css'] );
}

/**
* @covers WP_REST_Global_Styles_Controller::validate_custom_css
* @ticket 64418
*
* @dataProvider data_custom_css_allowed
*/
public function test_validate_custom_css_allowed( string $custom_css ) {
$controller = new WP_REST_Global_Styles_Controller();
$validate = Closure::bind(
function ( $css ) {
return $this->validate_custom_css( $css );
},
$controller,
$controller
);

$this->assertTrue( $validate( $custom_css ) );
}

/**
* Data provider.
*
* @return array<string, string[]>
*/
public static function data_custom_css_allowed(): array {
return array(
'@property declaration' => array(
'@property --prop { syntax: "<custom-ident>"; inherits: true; initial-value: false; }',
),
'Different close tag' => array( '</stylesheet>' ),
'Not a style close tag' => array( '/*</style*/' ),
'Not a style close tag 2' => array( '/*</style_' ),
'Empty' => array( '' ),
'Short content' => array( '/**/' ),
);
}

/**
* @covers WP_REST_Global_Styles_Controller::validate_custom_css
* @ticket 64418
*
* @dataProvider data_custom_css_disallowed
*/
public function test_validate_custom_css( string $custom_css, string $expected_error_message ) {
$controller = new WP_REST_Global_Styles_Controller();
$validate = Closure::bind(
function ( $css ) {
return $this->validate_custom_css( $css );
},
$controller,
$controller
);

$result = $validate( $custom_css );
$this->assertWPError( $result );
$this->assertSame( $expected_error_message, $result->get_error_message() );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this isn’t the biggest deal, but it seems fragile to hard-code error messages into our test assertions. perhaps there’s a more-durable code we could check for? or html-decode the error message and look for the offending contents (which still would be fragile because who know if we’ll enforce that aspect in the error message or switch it to something else).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the hard-coding either. I do want to confirm the correct behavior with the "contain" or "end in" messages and the relevant text snippet that helps make the problem actionable.

If you have a good alternative, I'm happy to take a different approach. I didn't want to spend a lot of effort building abstractions to address this. If these tests fail and need to be updated, it should be easy to find and fix them.

}

/**
* Data provider.
*
* @return array<string, string[]>
*/
public static function data_custom_css_disallowed(): array {
return array(
'style close tag' => array( 'css…</style>…css', 'The CSS must not contain "&lt;/style&gt;".' ),
'style close tag upper case' => array( '</STYLE>', 'The CSS must not contain "&lt;/STYLE&gt;".' ),
'style close tag mixed case' => array( '</sTyLe>', 'The CSS must not contain "&lt;/sTyLe&gt;".' ),
'style close tag in comment' => array( '/*</style>*/', 'The CSS must not contain "&lt;/style&gt;".' ),
'style close tag (/)' => array( '</style/', 'The CSS must not contain "&lt;/style/".' ),
'style close tag (\t)' => array( "</style\t", "The CSS must not contain \"&lt;/style\t\"." ),
'style close tag (\f)' => array( "</style\f", "The CSS must not contain \"&lt;/style\f\"." ),
'style close tag (\r)' => array( "</style\r", "The CSS must not contain \"&lt;/style\r\"." ),
'style close tag (\n)' => array( "</style\n", "The CSS must not contain \"&lt;/style\n\"." ),
'style close tag (" ")' => array( '</style ', 'The CSS must not contain "&lt;/style ".' ),
'truncated "<"' => array( '<', 'The CSS must not end in "&lt;".' ),
'truncated "</"' => array( '</', 'The CSS must not end in "&lt;/".' ),
'truncated "</s"' => array( '</s', 'The CSS must not end in "&lt;/s".' ),
'truncated "</ST"' => array( '</ST', 'The CSS must not end in "&lt;/ST".' ),
'truncated "</sty"' => array( '</sty', 'The CSS must not end in "&lt;/sty".' ),
'truncated "</STYL"' => array( '</STYL', 'The CSS must not end in "&lt;/STYL".' ),
'truncated "</stYle"' => array( '</stYle', 'The CSS must not end in "&lt;/stYle".' ),
);
}
}
Loading