Make WordPress Core

Opened 2 years ago

Last modified 4 weeks ago

#59446 new defect (bug)

Use script helper functions in admin to enable Content-Security-Policy opt-in

Reported by: westonruter's profile westonruter Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 5.7
Component: Administration Keywords: has-patch
Focuses: javascript Cc:

Description (last modified by westonruter)

In #58664 the script helper functions—wp_get_script_tag(), wp_print_inline_script_tag(), wp_get_inline_script_tag()—were leveraged to eliminate manual construction of script tags on the frontend and the login screen. These were introduced in #39941. This made it possible to opt-in (see example plugin) to a Strict Content-Security-Policy (Strict CSP) to guard against any possible XSS exploits. The scope in #58664 was limited to the frontend and the login screen because of the sheer number of inline scripts printed on the wp-admin. Additionally, the site editor and block editors make use of dynamically-constructed script tags in the editor iframe which is a Strict CSP violation.

Much of the work to rework inline scripts to use wp_print_inline_script() in the admin can be seen in an existing PR (now stale) from @enricocarraro.

See also #59444 which is about how to improve the developer experience of working with these JavaScript string literals.

Change History (17)

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


2 years ago

#2 follow-up: @anonymized_14808221
2 years ago

Can I ask a clarification? I am confused on the "version" of this trac
It says 5.7 and the selector only allows up to 5.7.2

Yet the trac was opened just 5 weeks ago, during a 6.x WP Version "timeframe". I am confused :)

What I am ultimately wondering is if there are ways already available for the _backend_ to become CSP ready, or if we only yet have https://core.trac.wordpress.org/ticket/39941 which implemented a front end filter allowing us to somewhat filter those script tags and add a nonce...

Thank you for any information!

#3 in reply to: ↑ 2 ; follow-up: @westonruter
2 years ago

Replying to bedas:

Can I ask a clarification? I am confused on the "version" of this trac
It says 5.7 and the selector only allows up to 5.7.2

Good question. The version field here refers to when the first applicable version of WordPress that the issue is relevant to. Since the functions were introduced in 5.7, that's why this version is used.

The target version for fixing the issue is captured in the "milestone" field, which is still just Future Release. There isn't currently an established way to opt-in to CSP for the backend, so that's what this ticket is for.

#4 in reply to: ↑ 3 ; follow-up: @anonymized_14808221
2 years ago

Replying to westonruter:

Thanks! Makes sense now.

I guess then there is some issue with wp_inline_script_attributes, because if it is intended for front end, then it shouldn't run in the backend - but does, and misses the array key type in that case.
I also notice that the documentation for the related wp_get_inline_script_tag is wrongly saying to use wp_script_attributes to filter the tags.

I guess I will open a commit/trac for the latter one and, should I open a trac for the first one? the very least we would need to specify on the doc page that this needs to be hooked explicitly to front end, or that an isset() should be run (which IMO is quite uncommon for filters/hooks, usually they run only where the data is available)

Finally, what about style tag? This is as far I see also not yet implemented (neither in the front nor in the backend)
Is there already a trac for that?

Please stop me if I am wrong. Thanks!

Last edited 2 years ago by anonymized_14808221 (previous) (diff)

#5 in reply to: ↑ 4 @westonruter
2 years ago

Replying to bedas:

Replying to westonruter:
I guess then there is some issue with wp_inline_script_attributes, because if it is intended for front end, then it shouldn't run in the backend - but does, and misses the array key type in that case. [...] the very least we would need to specify on the doc page that this needs to be hooked explicitly to front end, or that an isset() should be run (which IMO is quite uncommon for filters/hooks, usually they run only where the data is available)

This filter is not specific for the frontend. It is intended to be used in any context, whether frontend or admin. The type array key is only supplied automatically if the page is not HTML5. So yes, an isset() check should always be done for $attributes['type']. If it is not set, then it is assumed to be text/javascript, per the HTML spec.

I also notice that the documentation for the related wp_get_inline_script_tag is wrongly saying to use wp_script_attributes to filter the tags.

Good catch. The phpdoc for wp_get_inline_script_tag() needs to be updated.

#6 @westonruter
4 months ago

  • Description modified (diff)

This ticket was mentioned in PR #9416 on WordPress/wordpress-develop by @debarghyabanerjee.


4 months ago
#7

  • Keywords has-patch added; needs-patch removed

Trac Ticket: Core-63806

This pull request updates all inline script outputs in the bundled themes to use WordPress’s script helper functions, specifically wp_print_inline_script_tag(), in place of manually constructed <script> tags.

---

## ✅ Why This Matters

As of #59446, WordPress Core has adopted the use of wp_get_script_tag(), wp_get_inline_script_tag(), and wp_print_inline_script_tag() to eliminate manually constructed <script> tags. This change was made to:

However, many default and third-party themes still use raw <script> tags, which prevents them from fully benefiting from these improvements.

---

## 🛠 What’s Changed

  • Replaced all instances of echo '<script>...</script>' with calls to wp_print_inline_script_tag().

---

## 🔙 Backward Compatibility
Since these helper functions were introduced in WordPress 5.7+, this PR also includes polyfill definitions in functions.php to ensure compatibility with earlier WordPress versions.

The polyfills conditionally define wp_print_inline_script_tag() and wp_get_inline_script_tag() functions only if they don’t already exist, making it safe for all supported versions.

#8 @jonsurrell
4 months ago

#52497 was marked as a duplicate.

@westonruter commented on PR #9416:


3 months ago
#9

@Debarghya-Banerjee Hello! Have you seen the latest PR feedback yet?

Also, note the comment I just added to the ticket, related the changes to Core-63887.

@debarghyabanerjee commented on PR #9416:


3 months ago
#10

Hi @westonruter , I have checked the comments and feedback, I am working on it, and will push the changes in sometime by today itself. Thanks.

@debarghyabanerjee commented on PR #9416:


3 months ago
#11

Hi @westonruter , Apologies for the late update. I’ve pushed the changes based on your and @sabernhardt 's feedback. Could you please take a look? Thanks!

@westonruter commented on PR #9416:


2 months ago
#12

@Debarghya-Banerjee Are you planning to address the remaining feedback or should we start pushing up commits to this PR? Thanks!

@debarghyabanerjee commented on PR #9416:


2 months ago
#13

Hi @westonruter , I have addressed the feedback. Sorry for getting back late. Can you please check once? Thanks.

@westonruter commented on PR #9416:


2 months ago
#14

I wrote a script that checked the output of rendered output of the homepages for each of the themes to verify that there were no regressions in the generated markup, and that only the expected changes occur:

<details><summary><code>grab-output.sh</code></summary>

#!/bin/bash

outdir=/tmp/trac-63806-output
mkdir -p "$outdir"
echo '' > "$outdir/report.md"

check_theme() {
	theme="$1"
	url="$2"
	theme_dir="/tmp/trac-63806-output/$1"
	echo "# $theme" >> "$outdir/report.md"
	mkdir -p "$theme_dir"
	npm run env:cli theme activate "$theme"
	git checkout trunk
	curl -s "$url" > "$theme_dir/before.html"
	git checkout fix/63806-update-themes-to-use-wp_print_script_tag
	curl -s "$url" > "$theme_dir/after.html"
	prettier "$theme_dir/before.html" > "$theme_dir/before.prettier.html"
	prettier "$theme_dir/after.html" > "$theme_dir/after.prettier.html"
	diff -u "$theme_dir/before.html" "$theme_dir/after.html" > "$theme_dir/raw.diff"
	diff -u "$theme_dir/before.prettier.html" "$theme_dir/after.prettier.html" > "$theme_dir/prettier.diff"

	{
		echo "URL: \`$url\`"
		echo
		echo 'Prettier Diff:'
		echo '{{{diff'
		cat "$theme_dir/prettier.diff"
		echo '}}}'
		echo
		echo '<details><summary>Raw Diff</summary>'
		echo
		echo '{{{diff'
		cat "$theme_dir/raw.diff"
		echo '}}}'
		echo
		echo '</details>'
		echo
	} >> "$outdir/report.md"
}

home_url="http://localhost:8000/?enable_plugins=none"

check_theme twentyfifteen "$home_url"
check_theme twentyseventeen "$home_url"
check_theme twentysixteen "$home_url"
check_theme twentytwenty "$home_url"
check_theme twentytwentyone "$home_url"

cat "$outdir/report.md"

</details>

Here is the output:

# twentyfifteen
URL: http://localhost:8000/?enable_plugins=none

Prettier Diff:

  • /tmp/trac-63806-output/twentyfifteen/

    old new  
    99      (function (html) {
    1010        html.className = html.className.replace(/\bno-js\b/, "js");
    1111      })(document.documentElement);
     12      //# sourceURL=twentyfifteen_javascript_detection
    1213    </script>
    1314    <title>WordPress Develop</title>
    1415    <meta name="robots" content="max-image-preview:large" />

<details><summary>Raw Diff</summary>

  • /tmp/trac-63806-output/twentyfifteen/

    old new  
    55        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    66        <link rel="profile" href="https://gmpg.org/xfn/11">
    77        <link rel="pingback" href="http://localhost:8000/xmlrpc.php">
    8         <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
     8        <script>
     9(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);
     10//# sourceURL=twentyfifteen_javascript_detection
     11</script>
    912<title>WordPress Develop</title>
    1013<meta name='robots' content='max-image-preview:large' />
    1114<link rel="alternate" type="application/rss+xml" title="WordPress Develop &raquo; Feed" href="http://localhost:8000/feed/" />

</details>

# twentyseventeen
URL: http://localhost:8000/?enable_plugins=none

Prettier Diff:

  • /tmp/trac-63806-output/twentyseventeen/

    old new  
    99      (function (html) {
    1010        html.className = html.className.replace(/\bno-js\b/, "js");
    1111      })(document.documentElement);
     12      //# sourceURL=twentyseventeen_javascript_detection
    1213    </script>
    1314    <title>WordPress Develop</title>
    1415    <meta name="robots" content="max-image-preview:large" />

<details><summary>Raw Diff</summary>

  • /tmp/trac-63806-output/twentyseventeen/

    old new  
    55<meta name="viewport" content="width=device-width, initial-scale=1.0">
    66<link rel="profile" href="https://gmpg.org/xfn/11">
    77
    8 <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
     8<script>
     9(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);
     10//# sourceURL=twentyseventeen_javascript_detection
     11</script>
    912<title>WordPress Develop</title>
    1013<meta name='robots' content='max-image-preview:large' />
    1114<link rel="alternate" type="application/rss+xml" title="WordPress Develop &raquo; Feed" href="http://localhost:8000/feed/" />

</details>

# twentysixteen
URL: http://localhost:8000/?enable_plugins=none

Prettier Diff:

  • /tmp/trac-63806-output/twentysixteen/

    old new  
    88      (function (html) {
    99        html.className = html.className.replace(/\bno-js\b/, "js");
    1010      })(document.documentElement);
     11      //# sourceURL=twentysixteen_javascript_detection
    1112    </script>
    1213    <title>WordPress Develop</title>
    1314    <meta name="robots" content="max-image-preview:large" />

<details><summary>Raw Diff</summary>

  • /tmp/trac-63806-output/twentysixteen/

    old new  
    44        <meta charset="UTF-8">
    55        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    66        <link rel="profile" href="https://gmpg.org/xfn/11">
    7                 <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
     7                <script>
     8(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);
     9//# sourceURL=twentysixteen_javascript_detection
     10</script>
    811<title>WordPress Develop</title>
    912<meta name='robots' content='max-image-preview:large' />
    1013<link rel="alternate" type="application/rss+xml" title="WordPress Develop &raquo; Feed" href="http://localhost:8000/feed/" />

</details>

# twentytwenty
URL: http://localhost:8000/?enable_plugins=none

Prettier Diff:

  • /tmp/trac-63806-output/twentytwenty/

    old new  
    618618    <script>
    619619      document.documentElement.className =
    620620        document.documentElement.className.replace("no-js", "js");
     621      //# sourceURL=twentytwenty_no_js_class
    621622    </script>
    622623    <style id="custom-background-css">
    623624      body.custom-background {

<details><summary>Raw Diff</summary>

  • /tmp/trac-63806-output/twentytwenty/

    old new  
    7878<script src="http://localhost:8000/wp-content/themes/twentytwenty/assets/js/index.js?ver=2.9" id="twentytwenty-js-js" defer data-wp-strategy="defer"></script>
    7979<link rel="https://api.w.org/" href="http://localhost:8000/wp-json/" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://localhost:8000/xmlrpc.php?rsd" />
    8080<meta name="generator" content="WordPress 6.9-alpha-60093-src" />
    81         <script>document.documentElement.className = document.documentElement.className.replace( 'no-js', 'js' );</script>
    82         <style id="custom-background-css">
     81<script>
     82document.documentElement.className = document.documentElement.className.replace( 'no-js', 'js' );
     83//# sourceURL=twentytwenty_no_js_class
     84</script>
     85<style id="custom-background-css">
    8386body.custom-background { background-color: #fff; }
    8487</style>

</details>

# twentytwentyone
URL: http://localhost:8000/?enable_plugins=none

Prettier Diff:

  • /tmp/trac-63806-output/twentytwentyone/

    old new  
    15241524    </script>
    15251525    <script>
    15261526      document.body.classList.remove("no-js");
     1527      //# sourceURL=twenty_twenty_one_supports_js
    15271528    </script>
    15281529    <button
    15291530      id="dark-mode-toggler"
     
    16221623
    16231624      darkModeInitialLoad();
    16241625      darkModeRepositionTogglerOnScroll();
     1626      //# sourceURL=http://localhost:8000/wp-content/themes/twentytwentyone/assets/js/dark-mode-toggler.js
    16251627    </script>
    16261628    <script>
    16271629      if (
     
    16301632      ) {
    16311633        document.body.classList.add("is-IE");
    16321634      }
     1635      //# sourceURL=twentytwentyone_add_ie_class
    16331636    </script>
    16341637    <style id="core-block-supports-inline-css">
    16351638      /**

<details><summary>Raw Diff</summary>

  • /tmp/trac-63806-output/twentytwentyone/

    old new  
    470470<script type="speculationrules">
    471471{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/twentytwentyone/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
    472472</script>
    473 <script>document.body.classList.remove("no-js");</script><button id="dark-mode-toggler" class="fixed-bottom" aria-pressed="false" onClick="toggleDarkMode()">Dark Mode: <span aria-hidden="true"></span></button>               <style>
     473<script>
     474document.body.classList.remove('no-js');
     475//# sourceURL=twenty_twenty_one_supports_js
     476</script>
     477<button id="dark-mode-toggler" class="fixed-bottom" aria-pressed="false" onClick="toggleDarkMode()">Dark Mode: <span aria-hidden="true"></span></button>                <style>
    474478                        #dark-mode-toggler > span {
    475479                                margin-left: 5px;
    476480                        }
     
    482486                        }
    483487                                        </style>
    484488
    485                 <script>function toggleDarkMode() { // jshint ignore:line
     489                <script>
     490function toggleDarkMode() { // jshint ignore:line
    486491        var toggler = document.getElementById( 'dark-mode-toggler' );
    487492
    488493        if ( 'false' === toggler.getAttribute( 'aria-pressed' ) ) {
     
    553558
    554559darkModeInitialLoad();
    555560darkModeRepositionTogglerOnScroll();
    556 </script>       <script>
    557         if ( -1 !== navigator.userAgent.indexOf( 'MSIE' ) || -1 !== navigator.appVersion.indexOf( 'Trident/' ) ) {
    558                 document.body.classList.add( 'is-IE' );
    559         }
    560         </script>
    561         <style id='core-block-supports-inline-css'>
     561//# sourceURL=http://localhost:8000/wp-content/themes/twentytwentyone/assets/js/dark-mode-toggler.js
     562</script>
     563<script>
     564                if ( -1 !== navigator.userAgent.indexOf('MSIE') || -1 !== navigator.appVersion.indexOf('Trident/') ) {
     565                        document.body.classList.add('is-IE');
     566                }
     567        //# sourceURL=twentytwentyone_add_ie_class
     568</script>
     569<style id='core-block-supports-inline-css'>
    562570/**
    563571 * Core styles: block-supports
    564572 */

</details>

#15 @westonruter
2 months ago

In 60913:

Bundled Themes: Use wp_print_inline_script_tag() when available and include sourceURL for JS.

Instead of manually constructing the markup for SCRIPT tags, leverage wp_print_inline_script_tag() when available to do the construction while also ensuring filters may inject additional attributes on the SCRIPT tags, such as nonce for CSP. When the function is not available (prior to WP 5.7), fall back to the manual markup construction.

This also adds the sourceURL comments to the inline scripts to facilitate debugging, per #63887.

Developed in https://github.com/WordPress/wordpress-develop/pull/9416.

Follow-up to [60909], [60899].

Props debarghyabanerjee, westonruter, hbhalodia, peterwilsoncc, sabernhardt, poena.
See #63887, #59446.
Fixes #63806.

This ticket was mentioned in Slack in #core by jorbin. View the logs.


7 weeks ago

This ticket was mentioned in Slack in #core by westonruter. View the logs.


4 weeks ago

Note: See TracTickets for help on using tickets.