Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9af57b1

Browse files
committed
Core:Manipulation: Add basic TrustedHTML support
This ensures HTML wrapped in TrustedHTML can be used as an input to jQuerymanipulation methods in a way that doesn't violate the`require-trusted-types-for` Content Security Policy directive.This commit builds on previous work needed for trusted types support, includinggh-4642 andgh-4724.One restriction is that while any TrustedHTML wrapper should work as inputfor jQuery methods like `.html()` or `.append()`, for passing directly to the`jQuery` factory the string must start with `<` and end with `>`; no trailingor leading whitespaces are allowed. This is necessary as we cannot parse outa part of the input for further construction; that would violate the CSP rule -and that's what's done to HTML input not matching these constraints.No trusted types API is used explicitly in source; the majority of the work isensuring we don't pass the input converted to string to APIs that wouldeventually assign it to `innerHTML`. This extra cautiousness is caused by theAPI being Blink-only, at least for now.The ban on passing strings to `innerHTML` means support tests relying on suchassignments are impossible. We don't currently have such tests on the `main`branch but we used to have many of them in the 3.x & older lines. If there'sa need to re-add such a test, we'll need an escape hatch to skip them for appsneeding CSP-enforced TrustedHTML.Seehttps://web.dev/trusted-types/ for more information about TrustedHTML.Fixesgh-4409Refgh-4642Refgh-4724
1 parentefadfe9 commit9af57b1

File tree

12 files changed

+154
-46
lines changed

12 files changed

+154
-46
lines changed

‎src/core.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import hasOwn from "./var/hasOwn.js";
1010
importfnToStringfrom"./var/fnToString.js";
1111
importObjectFunctionStringfrom"./var/ObjectFunctionString.js";
1212
importsupportfrom"./var/support.js";
13-
importisWindowfrom"./var/isWindow.js";
13+
importisArrayLikefrom"./core/isArrayLike.js";
1414
importDOMEvalfrom"./core/DOMEval.js";
15-
importtoTypefrom"./core/toType.js";
1615

1716
varversion="@VERSION",
1817

@@ -398,17 +397,4 @@ jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symb
398397
class2type["[object "+name+"]"]=name.toLowerCase();
399398
});
400399

401-
functionisArrayLike(obj){
402-
403-
varlength=!!obj&&obj.length,
404-
type=toType(obj);
405-
406-
if(typeofobj==="function"||isWindow(obj)){
407-
returnfalse;
408-
}
409-
410-
returntype==="array"||length===0||
411-
typeoflength==="number"&&length>0&&(length-1)inobj;
412-
}
413-
414400
exportdefaultjQuery;

‎src/core/init.js

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
importjQueryfrom"../core.js";
33
importdocumentfrom"../var/document.js";
44
importrsingleTagfrom"./var/rsingleTag.js";
5+
importisObviousHtmlfrom"./isObviousHtml.js";
56

67
import"../traversing/findFilter.js";
78

@@ -26,20 +27,41 @@ var rootjQuery,
2627
// so migrate can support jQuery.sub (gh-2101)
2728
root=root||rootjQuery;
2829

29-
// Handle HTML strings
30-
if(typeofselector==="string"){
31-
if(selector[0]==="<"&&
32-
selector[selector.length-1]===">"&&
33-
selector.length>=3){
30+
// HANDLE: $(DOMElement)
31+
if(selector.nodeType){
32+
this[0]=selector;
33+
this.length=1;
34+
returnthis;
35+
36+
// HANDLE: $(function)
37+
// Shortcut for document ready
38+
}elseif(typeofselector==="function"){
39+
returnroot.ready!==undefined ?
40+
root.ready(selector) :
41+
42+
// Execute immediately if ready is not present
43+
selector(jQuery);
44+
45+
}else{
3446

35-
// Assume that strings that start and end with <> are HTML and skip the regex check
47+
// Handle obvious HTML strings
48+
match=selector+"";
49+
if(isObviousHtml(match)){
50+
51+
// Assume that strings that start and end with <> are HTML and skip
52+
// the regex check. This also handles browser-supported HTML wrappers
53+
// like TrustedHTML.
3654
match=[null,selector,null];
3755

38-
}else{
56+
// Handle HTML strings or selectors
57+
}elseif(typeofselector==="string"){
3958
match=rquickExpr.exec(selector);
59+
}else{
60+
returnjQuery.makeArray(selector,this);
4061
}
4162

4263
// Match html or make sure no context is specified for #id
64+
// Note: match[1] may be a string or a TrustedHTML wrapper
4365
if(match&&(match[1]||!context)){
4466

4567
// HANDLE: $(html) -> $(array)
@@ -84,7 +106,7 @@ var rootjQuery,
84106
returnthis;
85107
}
86108

87-
// HANDLE: $(expr, $(...))
109+
// HANDLE: $(expr) & $(expr, $(...))
88110
}elseif(!context||context.jquery){
89111
return(context||root).find(selector);
90112

@@ -93,24 +115,8 @@ var rootjQuery,
93115
}else{
94116
returnthis.constructor(context).find(selector);
95117
}
96-
97-
// HANDLE: $(DOMElement)
98-
}elseif(selector.nodeType){
99-
this[0]=selector;
100-
this.length=1;
101-
returnthis;
102-
103-
// HANDLE: $(function)
104-
// Shortcut for document ready
105-
}elseif(typeofselector==="function"){
106-
returnroot.ready!==undefined ?
107-
root.ready(selector) :
108-
109-
// Execute immediately if ready is not present
110-
selector(jQuery);
111118
}
112119

113-
returnjQuery.makeArray(selector,this);
114120
};
115121

116122
// Give the init function the jQuery prototype for later instantiation

‎src/core/isArrayLike.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
importtoTypefrom"./toType.js";
2+
importisWindowfrom"../var/isWindow.js";
3+
4+
functionisArrayLike(obj){
5+
6+
varlength=!!obj&&obj.length,
7+
type=toType(obj);
8+
9+
if(typeofobj==="function"||isWindow(obj)){
10+
returnfalse;
11+
}
12+
13+
returntype==="array"||length===0||
14+
typeoflength==="number"&&length>0&&(length-1)inobj;
15+
}
16+
17+
exportdefaultisArrayLike;

‎src/core/isObviousHtml.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
functionisObviousHtml(input){
2+
returninput[0]==="<"&&
3+
input[input.length-1]===">"&&
4+
input.length>=3;
5+
}
6+
7+
exportdefaultisObviousHtml;

‎src/core/parseHTML.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import jQuery from "../core.js";
22
importdocumentfrom"../var/document.js";
33
importrsingleTagfrom"./var/rsingleTag.js";
44
importbuildFragmentfrom"../manipulation/buildFragment.js";
5+
importisObviousHtmlfrom"./isObviousHtml.js";
56

6-
// Argument "data" should be string of html
7+
// Argument "data" should be string of html or a TrustedHTML wrapper of obvious HTML
78
// context (optional): If specified, the fragment will be created in this context,
89
// defaults to document
910
// keepScripts (optional): If true, will include scripts passed in the html string
1011
jQuery.parseHTML=function(data,context,keepScripts){
11-
if(typeofdata!=="string"){
12+
if(typeofdata!=="string"&&!isObviousHtml(data+"")){
1213
return[];
1314
}
1415
if(typeofcontext==="boolean"){

‎src/manipulation/buildFragment.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import rscriptType from "./var/rscriptType.js";
77
importwrapMapfrom"./wrapMap.js";
88
importgetAllfrom"./getAll.js";
99
importsetGlobalEvalfrom"./setGlobalEval.js";
10+
importisArrayLikefrom"../core/isArrayLike.js";
1011

1112
varrhtml=/<|&#?\w+;/;
1213

@@ -23,7 +24,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
2324
if(elem||elem===0){
2425

2526
// Add nodes directly
26-
if(toType(elem)==="object"){
27+
if(toType(elem)==="object"&&(elem.nodeType||isArrayLike(elem))){
2728
jQuery.merge(nodes,elem.nodeType ?[elem] :elem);
2829

2930
// Convert non-html into a text node

‎test/.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"require":false,
1515
"Promise":false,
1616
"Symbol":false,
17+
"trustedTypes":false,
1718
"QUnit":false,
1819
"ajaxTest":false,
1920
"testIframe":false,

‎test/data/csp.include.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<metahttp-equiv="Content-Type"content="text/html; charset=utf-8"/>
55
<title>CSP Test Page</title>
6-
<scriptsrc="../jquery.js"></script>
6+
<scriptsrc="../../dist/jquery.min.js"></script>
77
<scriptsrc="iframeTest.js"></script>
88
<scriptsrc="support/csp.js"></script>
99
<scriptsrc="support/getComputedSupport.js"></script>

‎test/data/mock.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ protected function testHTML( $req ) {
215215
}
216216

217217
protectedfunctioncspFrame($req ) {
218-
header("Content-Security-Policy: default-src 'self'; report-uri ./mock.php?action=cspLog" );
218+
header("Content-Security-Policy: default-src 'self';require-trusted-types-for 'script';report-uri ./mock.php?action=cspLog" );
219219
header('Content-type: text/html' );
220220
echofile_get_contents(__DIR__ .'/csp.include.html' );
221221
}
@@ -228,7 +228,7 @@ protected function cspNonce( $req ) {
228228
}
229229

230230
protectedfunctioncspAjaxScript($req ) {
231-
header("Content-Security-Policy: script-src 'self'; report-uri/base/test/data/mock.php?action=cspLog" );
231+
header("Content-Security-Policy: script-src 'self'; report-uri./mock.php?action=cspLog" );
232232
header('Content-type: text/html' );
233233
echofile_get_contents(__DIR__ .'/csp-ajax-script.html' );
234234
}
@@ -242,6 +242,12 @@ protected function cspClean( $req ) {
242242
unlink($this->cspFile );
243243
}
244244

245+
protectedfunctiontrustedHtml($req ) {
246+
header("Content-Security-Policy: require-trusted-types-for 'script'; report-uri ./mock.php?action=cspLog" );
247+
header('Content-type: text/html' );
248+
echofile_get_contents(__DIR__ .'/trusted-html.html' );
249+
}
250+
245251
protectedfunctionerrorWithScript($req ) {
246252
header('HTTP/1.0 404 Not Found' );
247253
if (isset($req->query['withScriptContentType'] ) ) {

‎test/data/trusted-html.html

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<metacharset=utf-8/>
5+
<title>body</title>
6+
</head>
7+
<body>
8+
<divid="qunit-fixture"></div>
9+
<scriptsrc="../../dist/jquery.min.js"></script>
10+
<scriptsrc="iframeTest.js"></script>
11+
<script>
12+
if(typeoftrustedTypes==="undefined"){
13+
startIframeTest([{
14+
actual:"",
15+
expected:"trustedTypes support",
16+
message:"trustedTypes supported"
17+
}]);
18+
thrownewError("No trustedTypes support; this test should be skipped");
19+
}
20+
21+
vari,input,elem,tags,
22+
results=[],
23+
policy=trustedTypes.createPolicy("jquery-test-policy",{
24+
createHTML:function(html){
25+
returnhtml;
26+
}
27+
}),
28+
inputs=[
29+
["<div></div>","<div class='test'></div>",["div"]],
30+
["<div></div>","<div class='test'></div><span class='test'></span>",
31+
["div","span"]],
32+
["<table></table>","<td class='test'></td>",["td"]],
33+
["<select></select>","<option class='test'></option>",["option"]]
34+
];
35+
36+
for(i=0;i<inputs.length;i++){
37+
input=inputs[i];
38+
elem=jQuery(policy.createHTML(input[0]));
39+
elem.append(policy.createHTML(input[1]));
40+
tags=elem.find(".test").toArray().map(function(node){
41+
returnnode.nodeName.toLowerCase();
42+
});
43+
results.push({
44+
actual:tags,
45+
expected:input[2],
46+
message:input[2].join(", ")
47+
});
48+
}
49+
50+
elem=jQuery(policy.createHTML("<div></div>"));
51+
elem.append(policy.createHTML("text content"));
52+
results.push({
53+
actual:elem.html(),
54+
expected:"text content",
55+
message:"Text content properly appended"
56+
});
57+
58+
startIframeTest(results);
59+
</script>
60+
</body>
61+
</html>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp