Commit 0f1223ce1f for wordpress.org
commit 0f1223ce1f43ad287d3c2ae2275aa80b9a1a50a5
Author: jonsurrell <jonsurrell@git.wordpress.org>
Date: Thu Jan 15 11:13:54 2026 +0000
Script Loader: Use HTML API to generate SCRIPT tags.
Script tags have complicated and unintuitive parsing rules that make them difficult to author correctly. The HTML API automatically escapes script tag contents as necessary and will set attributes correctly. Using the HTML API to generate SCRIPT tags improves safety when working with SCRIPT tags, resolving a class of issues that have manifested repeatedly.
Changeset [61418] applied the HTML API to generate style tags in a similar way.
Developed in https://github.com/WordPress/wordpress-develop/pull/10639.
Props jonsurrell, dmsnell, westonruter.
Fixes #64500. See #64419, #40737, #62797, #63851, #51159.
Built from https://develop.svn.wordpress.org/trunk@61485
git-svn-id: http://core.svn.wordpress.org/trunk@60797 1a063a9b-81f0-0310-95a4-ce76da25c4cd
diff --git a/wp-includes/css/dist/index.php b/wp-includes/css/dist/index.php
index 9886e44eb7..e0020d536c 100644
--- a/wp-includes/css/dist/index.php
+++ b/wp-includes/css/dist/index.php
@@ -7,11 +7,6 @@
*/
return array(
- array(
- 'handle' => 'wp-preferences',
- 'path' => 'preferences/style',
- 'dependencies' => array('wp-components'),
- ),
array(
'handle' => 'wp-nux',
'path' => 'nux/style',
@@ -23,8 +18,8 @@ return array(
'dependencies' => array('wp-components'),
),
array(
- 'handle' => 'wp-commands',
- 'path' => 'commands/style',
+ 'handle' => 'wp-preferences',
+ 'path' => 'preferences/style',
'dependencies' => array('wp-components'),
),
array(
@@ -37,6 +32,11 @@ return array(
'path' => 'patterns/style',
'dependencies' => array('wp-block-editor', 'wp-components'),
),
+ array(
+ 'handle' => 'wp-commands',
+ 'path' => 'commands/style',
+ 'dependencies' => array('wp-components'),
+ ),
array(
'handle' => 'wp-widgets',
'path' => 'widgets/style',
@@ -57,20 +57,20 @@ return array(
'path' => 'block-directory/style',
'dependencies' => array('wp-block-editor', 'wp-components', 'wp-editor'),
),
- array(
- 'handle' => 'wp-customize-widgets',
- 'path' => 'customize-widgets/style',
- 'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-preferences', 'wp-widgets'),
- ),
array(
'handle' => 'wp-media-utils',
'path' => 'media-utils/style',
'dependencies' => array('wp-components'),
),
array(
- 'handle' => 'wp-block-library',
- 'path' => 'block-library/style',
- 'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
+ 'handle' => 'wp-customize-widgets',
+ 'path' => 'customize-widgets/style',
+ 'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-preferences', 'wp-widgets'),
+ ),
+ array(
+ 'handle' => 'wp-edit-widgets',
+ 'path' => 'edit-widgets/style',
+ 'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-patterns', 'wp-preferences', 'wp-widgets'),
),
array(
'handle' => 'wp-edit-post',
@@ -78,9 +78,9 @@ return array(
'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-commands', 'wp-components', 'wp-editor', 'wp-preferences', 'wp-widgets'),
),
array(
- 'handle' => 'wp-edit-widgets',
- 'path' => 'edit-widgets/style',
- 'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-patterns', 'wp-preferences', 'wp-widgets'),
+ 'handle' => 'wp-block-library',
+ 'path' => 'block-library/style',
+ 'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
),
array(
'handle' => 'wp-editor',
diff --git a/wp-includes/js/dist/script-modules/index.php b/wp-includes/js/dist/script-modules/index.php
index 2b7732924a..98bb486813 100644
--- a/wp-includes/js/dist/script-modules/index.php
+++ b/wp-includes/js/dist/script-modules/index.php
@@ -7,16 +7,6 @@
*/
return array(
- array(
- 'id' => '@wordpress/core-abilities',
- 'path' => 'core-abilities/index',
- 'asset' => 'core-abilities/index.min.asset.php',
- ),
- array(
- 'id' => '@wordpress/a11y',
- 'path' => 'a11y/index',
- 'asset' => 'a11y/index.min.asset.php',
- ),
array(
'id' => '@wordpress/interactivity-router',
'path' => 'interactivity-router/index',
@@ -28,19 +18,19 @@ return array(
'asset' => 'interactivity-router/full-page.min.asset.php',
),
array(
- 'id' => '@wordpress/interactivity',
- 'path' => 'interactivity/index',
- 'asset' => 'interactivity/index.min.asset.php',
+ 'id' => '@wordpress/core-abilities',
+ 'path' => 'core-abilities/index',
+ 'asset' => 'core-abilities/index.min.asset.php',
),
array(
- 'id' => '@wordpress/latex-to-mathml',
- 'path' => 'latex-to-mathml/index',
- 'asset' => 'latex-to-mathml/index.min.asset.php',
+ 'id' => '@wordpress/a11y',
+ 'path' => 'a11y/index',
+ 'asset' => 'a11y/index.min.asset.php',
),
array(
- 'id' => '@wordpress/latex-to-mathml/loader',
- 'path' => 'latex-to-mathml/loader',
- 'asset' => 'latex-to-mathml/loader.min.asset.php',
+ 'id' => '@wordpress/interactivity',
+ 'path' => 'interactivity/index',
+ 'asset' => 'interactivity/index.min.asset.php',
),
array(
'id' => '@wordpress/abilities',
@@ -52,6 +42,16 @@ return array(
'path' => 'route/index',
'asset' => 'route/index.min.asset.php',
),
+ array(
+ 'id' => '@wordpress/latex-to-mathml',
+ 'path' => 'latex-to-mathml/index',
+ 'asset' => 'latex-to-mathml/index.min.asset.php',
+ ),
+ array(
+ 'id' => '@wordpress/latex-to-mathml/loader',
+ 'path' => 'latex-to-mathml/loader',
+ 'asset' => 'latex-to-mathml/loader.min.asset.php',
+ ),
array(
'id' => '@wordpress/edit-site-init',
'path' => 'edit-site-init/index',
diff --git a/wp-includes/script-loader.php b/wp-includes/script-loader.php
index 366f8f9366..cd8caac1d0 100644
--- a/wp-includes/script-loader.php
+++ b/wp-includes/script-loader.php
@@ -2880,7 +2880,31 @@ function wp_get_script_tag( $attributes ) {
*/
$attributes = apply_filters( 'wp_script_attributes', $attributes );
- return sprintf( "<script%s></script>\n", wp_sanitize_script_attributes( $attributes ) );
+ $processor = new WP_HTML_Tag_Processor( '<script></script>' );
+ $processor->next_tag();
+ foreach ( $attributes as $name => $value ) {
+ /*
+ * Lexical variations of an attribute name may represent the
+ * same attribute in HTML, therefore it’s possible that the
+ * input array might contain duplicate attributes even though
+ * it’s keyed on their name. Calling code should rewrite an
+ * attribute’s value rather than sending a duplicate attribute.
+ *
+ * Example:
+ *
+ * array( 'id' => 'main', 'ID' => 'nav' )
+ *
+ * In this example, there are two keys both describing the `id`
+ * attribute. PHP array iteration is in key-insertion order so
+ * the 'id' value will be set in the SCRIPT tag.
+ */
+ if ( null !== $processor->get_attribute( $name ) ) {
+ continue;
+ }
+
+ $processor->set_attribute( $name, $value ?? true );
+ }
+ return "{$processor->get_updated_html()}\n";
}
/**
@@ -2901,13 +2925,27 @@ function wp_print_script_tag( $attributes ) {
* Constructs an inline script tag.
*
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_inline_script_attributes'} filter.
- * Automatically injects type attribute if needed.
+ *
+ * If the `$data` is unsafe to embed in a `<script>` tag, an empty script tag with the provided
+ * attributes will be returned. JavaScript and JSON contents can be escaped, so this is only likely
+ * to be a problem with unusual content types.
+ *
+ * Example:
+ *
+ * // The dangerous JavaScript in this example will be safely escaped.
+ * // A string with the script tag and the desired contents will be returned.
+ * wp_get_inline_script_tag( 'console.log( "</script>" );' );
+ *
+ * // This data is unsafe and `text/plain` cannot be escaped.
+ * // The following will return `""` to indicate failure:
+ * wp_get_inline_script_tag( '</script>', array( 'type' => 'text/plain' ) );
*
* @since 5.7.0
+ * @since 7.0.0 Returns an empty string if the data cannot be safely embedded in a script tag.
*
* @param string $data Data for script tag: JavaScript, importmap, speculationrules, etc.
* @param array<string, string|bool> $attributes Optional. Key-value pairs representing `<script>` tag attributes.
- * @return string String containing inline JavaScript code wrapped around `<script>` tag.
+ * @return string HTML script tag containing the provided $data or the empty string `""` if the data cannot be safely embedded in a script tag.
*/
function wp_get_inline_script_tag( $data, $attributes = array() ) {
$data = "\n" . trim( $data, "\n\r " ) . "\n";
@@ -2924,7 +2962,41 @@ function wp_get_inline_script_tag( $data, $attributes = array() ) {
*/
$attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $data );
- return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $data );
+ $processor = new WP_HTML_Tag_Processor( '<script></script>' );
+ $processor->next_tag();
+ foreach ( $attributes as $name => $value ) {
+ /*
+ * Lexical variations of an attribute name may represent the
+ * same attribute in HTML, therefore it’s possible that the
+ * input array might contain duplicate attributes even though
+ * it’s keyed on their name. Calling code should rewrite an
+ * attribute’s value rather than sending a duplicate attribute.
+ *
+ * Example:
+ *
+ * array( 'id' => 'main', 'ID' => 'nav' )
+ *
+ * In this example, there are two keys both describing the `id`
+ * attribute. PHP array iteration is in key-insertion order so
+ * the 'id' value will be set in the SCRIPT tag.
+ */
+ if ( null !== $processor->get_attribute( $name ) ) {
+ continue;
+ }
+
+ $processor->set_attribute( $name, $value ?? true );
+ }
+
+ if ( ! $processor->set_modifiable_text( $data ) ) {
+ _doing_it_wrong(
+ __FUNCTION__,
+ __( 'Unable to set inline script data.' ),
+ '7.0.0'
+ );
+ return '';
+ }
+
+ return "{$processor->get_updated_html()}\n";
}
/**
diff --git a/wp-includes/version.php b/wp-includes/version.php
index a2560f6461..99d0f66e2c 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '7.0-alpha-61484';
+$wp_version = '7.0-alpha-61485';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.