diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 366f8f93667e7..cd8caac1d045c 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -2880,7 +2880,31 @@ function wp_get_script_tag( $attributes ) { */ $attributes = apply_filters( 'wp_script_attributes', $attributes ); - return sprintf( "\n", wp_sanitize_script_attributes( $attributes ) ); + $processor = new WP_HTML_Tag_Processor( '' ); + $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 `" );' ); + * + * // This data is unsafe and `text/plain` cannot be escaped. + * // The following will return `""` to indicate failure: + * wp_get_inline_script_tag( '', 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 $attributes Optional. Key-value pairs representing `\n", wp_sanitize_script_attributes( $attributes ), $data ); + $processor = new WP_HTML_Tag_Processor( '' ); + $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/tests/phpunit/tests/dependencies/wpInlineScriptTag.php b/tests/phpunit/tests/dependencies/wpInlineScriptTag.php index 9958a2468706b..e8fabd7226e84 100644 --- a/tests/phpunit/tests/dependencies/wpInlineScriptTag.php +++ b/tests/phpunit/tests/dependencies/wpInlineScriptTag.php @@ -164,4 +164,23 @@ public function test_script_tag_repeat_attributes() { ), ); } + + /** + * Test failure conditions setting inline script tag contents. + * + * @ticket 64500 + */ + public function test_script_tag_dangerous_unescapeable_contents() { + $this->setExpectedIncorrectUsage( 'wp_get_inline_script_tag' ); + /* + * cannot be printed inside a script tag + * the `example/example` type is an unknown type with no known escaping rules. + * The only choice is to abort. + */ + $result = wp_get_inline_script_tag( + '', + array( 'type' => 'example/example' ) + ); + $this->assertSame( '', $result ); + } }