Skip to content

Commit cad8ac9

Browse files
committed
Add general escaping to JSON tags
1 parent cbd4fd8 commit cad8ac9

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

src/wp-includes/html-api/class-wp-html-tag-processor.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3867,6 +3867,15 @@ static function ( $matches ) {
38673867
},
38683868
$plaintext_content
38693869
);
3870+
} elseif ( $this->is_json_script_tag() ) {
3871+
/*
3872+
* To JSON escape JSON, the `<` character can be replaced
3873+
* everywhere.
3874+
*/
3875+
$plaintext_content = strtr(
3876+
$plaintext_content,
3877+
array( '<' => '\\u003C' )
3878+
);
38703879
} else {
38713880
return false;
38723881
}
@@ -4044,6 +4053,50 @@ public function is_javascript_script_tag(): bool {
40444053
return false;
40454054
}
40464055

4056+
/**
4057+
* Indicates if the currently matched tag is a JSON script tag.
4058+
*
4059+
* @since {WP_VERSION}
4060+
*
4061+
* @return bool True if the script tag should be treated as JSON.
4062+
*/
4063+
public function is_json_script_tag(): bool {
4064+
if ( 'SCRIPT' !== $this->get_tag() || $this->get_namespace() !== 'html' ) {
4065+
return false;
4066+
}
4067+
4068+
$type_attr = $this->get_attribute( 'type' );
4069+
4070+
if ( empty( $type_attr ) || true === $type_attr ) {
4071+
return false;
4072+
}
4073+
4074+
$type_string = strtolower( trim( $type_attr, " \t\f\r\n" ) );
4075+
4076+
/*
4077+
* > …
4078+
* > Otherwise, if the script block's type string is an ASCII case-insensitive match for the string "importmap", then set el's type to "importmap".
4079+
* > Otherwise, if the script block's type string is an ASCII case-insensitive match for the string "speculationrules", then set el's type to "speculationrules".
4080+
* @see https://html.spec.whatwg.org/#script-processing-model
4081+
*
4082+
* > A JSON MIME type is any MIME type whose subtype ends in "+json" or whose essence
4083+
* > is "application/json" or "text/json".
4084+
*
4085+
* @see https://mimesniff.spec.whatwg.org/#json-mime-type
4086+
*/
4087+
if (
4088+
'application/json' === $type_string
4089+
|| 'importmap' === $type_string
4090+
|| 'speculationrules' === $type_string
4091+
|| 'text/json' === $type_string
4092+
|| str_ends_with( $type_string, '+json' )
4093+
) {
4094+
return true;
4095+
}
4096+
4097+
return false;
4098+
}
4099+
40474100
/**
40484101
* Updates or creates a new attribute on the currently matched tag with the passed value.
40494102
*

tests/phpunit/tests/html-api/wpHtmlTagProcessorModifiableText.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,4 +539,38 @@ public static function data_script_tag_text_updates(): array {
539539
'Non-JS script, save HTML-like content' => array( '<script type="text/html"></script>', '<h1>This & that</h1>', '<script type="text/html"><h1>This & that</h1></script>' ),
540540
);
541541
}
542+
543+
544+
/**
545+
* @ticket 62797
546+
*/
547+
public function test_javascript_and_json_escaping() {
548+
$processor = new WP_HTML_Tag_Processor( "<script></script>\n<script></script>\n<h1>OK</h1>" );
549+
$processor->next_tag( 'SCRIPT' );
550+
$processor->set_attribute( 'type', 'importmap' );
551+
$importmap = array(
552+
'imports' => array(
553+
'</SCRIPT>\\<!--\\<script>' => "./script",
554+
),
555+
);
556+
$importmap = json_encode(
557+
$importmap,
558+
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS
559+
);
560+
561+
$processor->set_modifiable_text( $importmap );
562+
$processor->next_tag( 'SCRIPT' );
563+
$processor->set_attribute( 'type', 'module' );
564+
$javascript = <<<'JS'
565+
import '</SCRIPT>\\<!--\\<script>';
566+
JS;
567+
$processor->set_modifiable_text( $javascript );
568+
569+
$expected = <<<'HTML'
570+
<script type="importmap">{"imports":{"\u003C/SCRIPT>\\\u003C!--\\\u003Cscript>":"./script"}}</script>
571+
<script type="module">import '</\u0053CRIPT>\\<!--\\<\u0073cript>';</script>
572+
<h1>OK</h1>
573+
HTML;
574+
$this->assertEqualHTML( $expected, $processor->get_updated_html() );
575+
}
542576
}

0 commit comments

Comments
 (0)