Make WordPress Core

Opened 6 months ago

Last modified 3 weeks ago

#62797 new defect (bug)

wp_add_inline_script does not properly escape '<!-- <script>' in contents

Reported by: artpi's profile artpi Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.0
Component: Editor Keywords: has-patch has-unit-tests
Focuses: administration Cc:

Description

I am adding this issue in the "Editor" because this is how it's easiest to reproduce and how I stumbled onto it, but it is possible to replace the issue outside of editor context.

Reproduction path

  1. Start writing a post in Gutenberg
  2. Enter code editor
  3. Paste following code

<p><!-- <script> </p>

   </p>

  1. Save
  2. Exit code editor into block editor
  3. Reload the page
  4. Observe the editor crash and burn

Here is video of a reproduction on a fresh site:

https://screen.studio/share/ELc9d6zJ

What is going on?

block_editor_rest_api_preload is using wp_add_inline_script to preload the currently edited block content into the page
https://github.com/WordPress/WordPress/blob/0086f4ba4080285c90c08854a5d085c76b6d5109/wp-includes/block-editor.php#L767

Because the <script> tag is hidden in an comment that has not been properly terminated, it is impossible to get out of the script tag now, and nothing gets rendered afterwards.

Tested up to WordPress version 6.7.1

Attachments (2)

testing-without-patch.mp4 (1.2 MB) - added by shanemuir 6 months ago.
Testing without patch applied.
testing-with-patch.mp4 (3.6 MB) - added by shanemuir 6 months ago.
Testing with patch applied.

Change History (21)

#1 @ankitkumarshah
6 months ago

Reproduction Report

Description

This report validates whether the issue can be reproduced.

Environment

  • WordPress: 6.8-alpha-59593
  • PHP: 8.1.29
  • Server: nginx/1.16.0
  • Database: mysqli (Server: 8.0.16 / Client: mysqlnd 8.1.29)
  • Browser: Chrome 131.0.0.0
  • OS: macOS
  • Theme: Twenty Seventeen 3.8
  • MU Plugins: None activated
  • Plugins:
    • Gutenslider — The last WordPress slider you will ever need. 6.1.0
    • Test Plugin
    • Test Reports 1.2.0
    • WordPress Beta Tester 3.6.2

Actual Results

✅ Error condition occurs (reproduced).
Hi @artpi,

Thank you for bringing this up. By following the provided steps, I reproduced the issue successfully.

Screencast

https://ppgtp1rtd3.ufs.sh/f/TnWMEUzoUd85Fb83cfTuejSJhDdioPMH6NAwRtYmfvQBT3W9

#2 @abcd95
6 months ago

Hey @artpi, Thanks for bringing this up.

I was able to reproduce the issue - Screencast

The problem occurs because wp_add_inline_script() injects the block content inside a <script> tag for preloading. When there's an unclosed HTML comment containing <script> in the content, it breaks out of the string context since browsers parse this as the start of a new script tag, causing the editor to crash.

I will work on this to find a fix.

Additional Note: This happens with all variants of the script like <!-- <SCRIPT>

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


6 months ago
#3

  • Keywords has-patch added

#4 @dilipbheda
6 months ago

  • Focuses administration added
  • Keywords needs-testing added

@artpi, thank you for the report. I've addressed it with the attached PR.

@abcd95 @ankitkumarshah, could you please review the attached PR and let me know if you find any other issues?

Thanks!

#5 @sainathpoojary
6 months ago

Hey @dilipbheda,

I have tested the PR. While trying to reproduce the issue, I encountered this warning. It might be related to a regex issue in this line

https://rioudcpuyg.ufs.sh/f/PL8E4NiPUWyO9PFMNgfEFACY0dej4LmhwrRp2qTtuWnBUoas

Last edited 6 months ago by sainathpoojary (previous) (diff)

#7 @jonsurrell
6 months ago

A fix for all cases from the wp_add_inline_script side will be difficult. It may be something the HTML API could handle.

The problem in this case is with the JSON encoding. Use of the correct flags should fix the problem, namely JSON_HEX_TAG.

A good example to follow is this code used by script modules.

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


6 months ago
#8

Fixes #62797 by correctly escaping JSON included inside JavaScript in a script tag.

wp_add_inline_script will correctly escape closing script tags </script> which prevents script contents from breaking out of the script, it does not handle cases where some content may enter the HTML script data double escaped state and prevent the real script closing tag from closing the script tag as expected, leading to a broken document where the script tag remains open.

This can be tested by applying the patch and following the reproduction steps in #62797.

Trac ticket: https://core.trac.wordpress.org/ticket/62797

@shanemuir
6 months ago

Testing without patch applied.

@shanemuir
6 months ago

Testing with patch applied.

#9 @shanemuir
6 months ago

  • Keywords needs-testing removed

Test Report

Description

This report validates whether the indicated patch works as expected.

Patch tested: https://patch-diff.githubusercontent.com/raw/WordPress/wordpress-develop/pull/8145.diff

Environment

  • WordPress: 6.8-alpha-59274-src
  • PHP: 8.3.15
  • Server: Apache/2.4.62 (Unix) OpenSSL/3.4.0 PHP/8.3.15
  • Database: mysqli (Server: 8.0.39 / Client: mysqlnd 8.3.15)
  • Browser: Chrome 132.0.0.0
  • OS: macOS
  • Theme: Twenty Twenty-Five 1.0
  • MU Plugins: None activated
  • Plugins:
    • Test Reports 1.2.0

Actual Results

  1. ✅ Issue resolved with patch.

Additional Notes

Only testing using @jonsurrell patch, let me know if we need to test the other patch too?

Supplemental Artifacts

Added videos of testing with and without patch applied.

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


6 weeks ago
#10

  • Keywords has-unit-tests added
  • Fix json encode that may break enclosing script tag
  • Update or fix tests
  • Try encoding '<' in inline script tags

Trac ticket:

@jonsurrell commented on PR #8856:


6 weeks ago
#11

This won't work, SCRIPT tags have special parsing rules and HTML entities are not escaped.

@jonsurrell commented on PR #8106:


6 weeks ago
#12

Will you explain the intention of this change and how it addresses the issue?

@jonsurrell commented on PR #8145:


5 weeks ago
#13

I plan to add tests to this using assertEqualMarkup when it lands in https://github.com/WordPress/wordpress-develop/pull/8882.

@jonsurrell commented on PR #8145:


5 weeks ago
#14

I'm concerned this pattern is very common. I wonder if there should be some dedicated functions.

Here are some examples of this exact pattern in the site editor that are susceptible to the same problem:

https://github.com/WordPress/wordpress-develop/blob/f83e66389dbe3164ccf0fb2a1132064fb161b8e4/src/wp-admin/site-editor.php#L254-L292

@dmsnell commented on PR #8145:


5 weeks ago
#15

I'm concerned this pattern is very common. I wonder if there should be some dedicated functions.

What’s your concern? that it will contain </script> tag closers?

@jonsurrell commented on PR #8145:


5 weeks ago
#16

What’s your concern? that it will contain </script> tag closers?

That's the problem here that may produce a broken page. I'm unaware of others, but wp_add_inline_script + wp_json_encode seem common to use together but risky with the default arguments.

@jonsurrell commented on PR #8145:


4 weeks ago
#17

I've pushed a test leveraging assertEqualHTML with the fix reverted. That test will fail on CI, then I'll push the fix again.

The failure appears as Error: Paused at incomplete token. because the <script> tag remains unclosed.

@jonsurrell commented on PR #8145:


4 weeks ago
#18

Example failures on CI can be observed here:

1) Tests_Blocks_Editor::test_preload_closes_script_tag
Error: Paused at incomplete token.

(I'm updating the test name after the CI run, but the result is the same).

@dmsnell commented on PR #8145:


3 weeks ago
#19

That's the problem here that may produce a broken page. I'm unaware of others, but wp_add_inline_script + wp_json_encode seem common to use together but risky with the default arguments.

Could we perhaps create a new Issue or document where we can start enumerating the situations in which this can go bad? perhaps a new test suite with no new code, in a PR.

I find that it’s hard for me to grasp unless it’s a particularly good day and I have my full concentration.

There are differences, aren’t there, between printing these three situations?

<script type="application/json">
{ "some": "json" }
</script>
<script>
const data = { "some": "json" };
</script>
<script>
callMeMaybe( { "some": "json" } );
</script>

I thought these were distinct cases with different edges but I can’t remember how.

Note: See TracTickets for help on using tickets.