-
Notifications
You must be signed in to change notification settings - Fork 0
Fix inline script tag escaping #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from 9 commits
353fa40
adc0ab3
1639b2b
fe6434d
ced9c0f
4b194ef
f8df461
b26f45d
4b9aa42
4f6d9d3
7372021
bafb5fe
e60d592
092eecf
ed22488
529afad
981d8e1
a96d8ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3735,8 +3735,30 @@ public function set_modifiable_text( string $plaintext_content ): bool { | |||||
* properly escape these things, but this could mask regex patterns | ||||||
* that previously worked. Resolve this by not sending `</script` | ||||||
*/ | ||||||
if ( false !== stripos( $plaintext_content, '</script' ) ) { | ||||||
return false; | ||||||
if ( | ||||||
false !== stripos( $plaintext_content, '</script' ) || | ||||||
false !== stripos( $plaintext_content, '<script' ) | ||||||
) { | ||||||
/* | ||||||
* JavaScript can be safely escaped. | ||||||
* Non-JavaScript script tags have unknown semantics. | ||||||
* | ||||||
* @todo consider applying to JSON and importmap script tags as well. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This TODO comment suggests unfinished work. Consider either implementing the suggested feature for JSON and importmap script tags or removing this comment if it's not planned for this release.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
*/ | ||||||
if ( $this->is_javascript_script_tag() ) { | ||||||
$plaintext_content = preg_replace_callback( | ||||||
'~<(/?)(s)(cript)~i', | ||||||
static function ( $matches ) { | ||||||
$escaped_s_char = 's' === $matches[2] | ||||||
? '\u0073' | ||||||
: '\u0053'; | ||||||
return "<{$matches[1]}{$escaped_s_char}{$matches[3]}"; | ||||||
}, | ||||||
$plaintext_content | ||||||
); | ||||||
} else { | ||||||
return false; | ||||||
} | ||||||
} | ||||||
|
||||||
$this->lexical_updates['modifiable text'] = new WP_HTML_Text_Replacement( | ||||||
|
@@ -3794,6 +3816,123 @@ static function ( $tag_match ) { | |||||
return false; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Indicates if the currently matched tag is a JavaScript script tag. | ||||||
* | ||||||
* @see https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element | ||||||
* | ||||||
* @since {WP_VERSION} | ||||||
sirreal marked this conversation as resolved.
Show resolved
Hide resolved
sirreal marked this conversation as resolved.
Show resolved
Hide resolved
sirreal marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The @SInCE tag contains a placeholder '{WP_VERSION}' instead of an actual version number. This should be replaced with the specific WordPress version when this feature is released.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
* | ||||||
* @return boolean True if the script tag will be evaluated as JavaScript. | ||||||
sirreal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
*/ | ||||||
public function is_javascript_script_tag(): bool { | ||||||
if ( 'SCRIPT' !== $this->get_tag() || $this->get_namespace() !== 'html' ) { | ||||||
return false; | ||||||
} | ||||||
|
||||||
/* | ||||||
* > If any of the following are true: | ||||||
* > - el has a type attribute whose value is the empty string; | ||||||
* > - el has no type attribute but it has a language attribute and that attribute's | ||||||
* > value is the empty string; or | ||||||
* > - el has neither a type attribute nor a language attribute, | ||||||
* > then let the script block's type string for this script element be "text/javascript". | ||||||
*/ | ||||||
$type_attr = $this->get_attribute( 'type' ); | ||||||
$language_attr = $this->get_attribute( 'language' ); | ||||||
|
||||||
if ( true === $type_attr || '' === $type_attr ) { | ||||||
return true; | ||||||
} | ||||||
if ( | ||||||
null === $type_attr && ( | ||||||
true === $language_attr || | ||||||
'' === $language_attr || | ||||||
null === $language_attr | ||||||
) | ||||||
) { | ||||||
return true; | ||||||
} | ||||||
|
||||||
/* | ||||||
* > Otherwise, if el has a type attribute, then let the script block's type string be | ||||||
* > the value of that attribute with leading and trailing ASCII whitespace stripped. | ||||||
* > Otherwise, el has a non-empty language attribute; let the script block's type string | ||||||
* > be the concatenation of "text/" and the value of el's language attribute. | ||||||
*/ | ||||||
$type_string = $type_attr ? trim( $type_attr, " \t\f\r\n" ) : "text/{$language_attr}"; | ||||||
|
||||||
/* | ||||||
* > If the script block's type string is a JavaScript MIME type essence match, then | ||||||
* > set el's type to "classic". | ||||||
* | ||||||
* > A string is a JavaScript MIME type essence match if it is an ASCII case-insensitive | ||||||
* > match for one of the JavaScript MIME type essence strings. | ||||||
|
||||||
* > A JavaScript MIME type is any MIME type whose essence is one of the following: | ||||||
* > | ||||||
* > - application/ecmascript | ||||||
* > - application/javascript | ||||||
* > - application/x-ecmascript | ||||||
* > - application/x-javascript | ||||||
* > - text/ecmascript | ||||||
* > - text/javascript | ||||||
* > - text/javascript1.0 | ||||||
* > - text/javascript1.1 | ||||||
* > - text/javascript1.2 | ||||||
* > - text/javascript1.3 | ||||||
* > - text/javascript1.4 | ||||||
* > - text/javascript1.5 | ||||||
* > - text/jscript | ||||||
* > - text/livescript | ||||||
* > - text/x-ecmascript | ||||||
* > - text/x-javascript | ||||||
* | ||||||
* @see https://mimesniff.spec.whatwg.org/#javascript-mime-type | ||||||
* @see https://mimesniff.spec.whatwg.org/#javascript-mime-type-essence-match | ||||||
*/ | ||||||
switch ( strtolower( $type_string ) ) { | ||||||
case 'application/ecmascript': | ||||||
case 'application/javascript': | ||||||
case 'application/x-ecmascript': | ||||||
case 'application/x-javascript': | ||||||
case 'text/ecmascript': | ||||||
case 'text/javascript': | ||||||
case 'text/javascript1.0': | ||||||
case 'text/javascript1.1': | ||||||
case 'text/javascript1.2': | ||||||
case 'text/javascript1.3': | ||||||
case 'text/javascript1.4': | ||||||
case 'text/javascript1.5': | ||||||
case 'text/jscript': | ||||||
case 'text/livescript': | ||||||
case 'text/x-ecmascript': | ||||||
case 'text/x-javascript': | ||||||
return true; | ||||||
|
||||||
/* | ||||||
* > Otherwise, if the script block's type string is an ASCII case-insensitive match for | ||||||
* > the string "module", then set el's type to "module". | ||||||
* | ||||||
* A module is evaluated as JavaScript | ||||||
*/ | ||||||
sirreal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
case 'module': | ||||||
return true; | ||||||
} | ||||||
|
||||||
/* | ||||||
* > - 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". | ||||||
* | ||||||
* An importmap is JSON and not evaluated as JavaScript. This case is not handled here. | ||||||
*/ | ||||||
|
||||||
/* | ||||||
* > Otherwise, return. (No script is executed, and el's type is left as null.) | ||||||
*/ | ||||||
return false; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Updates or creates a new attribute on the currently matched tag with the passed value. | ||||||
* | ||||||
|
Uh oh!
There was an error while loading. Please reload this page.