script.https.html (10813B)
1 <!doctype html> 2 <meta name="timeout" content="long"> 3 <meta name="variant" content="?reporting=false"> 4 <meta name="variant" content="?reporting=true"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/common/dispatcher/dispatcher.js"></script> 8 <script src="/common/utils.js"></script> 9 <script src="/common/get-host-info.sub.js"></script> 10 <script src="/reporting/resources/report-helper.js"></script> 11 12 <body> 13 <script> 14 const params = new URLSearchParams(location.search); 15 const with_reporting = params.get('reporting') === "true"; 16 const {ORIGIN} = get_host_info(); 17 const getAbsoluteUrl = url => { 18 return new URL(url, window.location.href).href; 19 } 20 21 const check_report = async (reporting_endpoint, reporting_uuid, iframe_url, url, report_only) => { 22 const reports = await pollReports(reporting_endpoint, reporting_uuid); 23 const abs_iframe_url = getAbsoluteUrl(iframe_url); 24 checkReportExists(reports, 'integrity-violation', abs_iframe_url); 25 const abs_blocked_url = getAbsoluteUrl(url); 26 const report = getReport(reports, 'integrity-violation', abs_iframe_url, abs_blocked_url); 27 assert_not_equals(report, null); 28 assert_equals(report.body.documentURL, abs_iframe_url); 29 assert_equals(report.body.blockedURL, abs_blocked_url); 30 assert_equals(report.body.destination, "script"); 31 assert_equals(report.body.reportOnly, report_only); 32 }; 33 const blob = new Blob([`window.ran=true;`], 34 { type: 'application/javascript' }); 35 36 const blob_url = URL.createObjectURL(blob); 37 38 const test_cases = [ 39 { 40 description: "Ensure that a script without integrity did not run", 41 url: "/content-security-policy/resources/ran.js", 42 cross_origin: true, 43 integrity: "", 44 policy_violation: true, 45 add_blocking_header: true, 46 endpoints: true, 47 expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false}, 48 }, 49 { 50 description: "Ensure that a script with unknown integrity algorithm did not run", 51 url: "/content-security-policy/resources/ran.js", 52 cross_origin: true, 53 integrity: "foobar-AAAAAAAAAAAAAAAAAAAa", 54 policy_violation: true, 55 add_blocking_header: true, 56 endpoints: true, 57 expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false}, 58 }, 59 { 60 description: "Ensure that a script without integrity algorithm runs and gets reported in report-only mode", 61 url: "/content-security-policy/resources/ran.js", 62 cross_origin: true, 63 integrity: "", 64 policy_violation: true, 65 add_blocking_header: false, 66 endpoints: true, 67 expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: true}, 68 }, 69 { 70 description: "Ensure that a no-cors script gets blocked", 71 url: "/content-security-policy/resources/ran.js", 72 cross_origin: false, 73 integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak", 74 policy_violation: true, 75 add_blocking_header: true, 76 endpoints: true, 77 expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false}, 78 }, 79 { 80 description: "Ensure that ReportingObserver gets called without endpoints", 81 url: "/content-security-policy/resources/ran.js", 82 cross_origin: false, 83 integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak", 84 policy_violation: true, 85 add_blocking_header: true, 86 endpoints: false, 87 expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false}, 88 }, 89 { 90 description: "Ensure that a script with integrity runs", 91 url: "/content-security-policy/resources/ran.js", 92 cross_origin: true, 93 integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak", 94 policy_violation: false, 95 add_blocking_header: true, 96 endpoints: true, 97 expected: {blocked: "", ran: true}, 98 }, 99 { 100 description: "Ensure that a data URI script with no integrity runs", 101 url: "data:application/javascript,window.ran=true", 102 cross_origin: true, 103 integrity: "", 104 policy_violation: false, 105 add_blocking_header: true, 106 endpoints: true, 107 expected: {blocked: "", ran: true}, 108 }, 109 { 110 description: "Ensure that a no-CORS data URI script with no integrity runs", 111 url: "data:application/javascript,window.ran=true", 112 cross_origin: false, 113 integrity: "", 114 policy_violation: false, 115 add_blocking_header: true, 116 endpoints: true, 117 expected: {blocked: "", ran: true}, 118 }, 119 { 120 description: "Ensure that a blob URL script with no integrity runs", 121 url: blob_url, 122 cross_origin: true, 123 integrity: "", 124 policy_violation: false, 125 add_blocking_header: true, 126 endpoints: true, 127 expected: {blocked: "", ran: true}, 128 }, 129 { 130 description: "Ensure that a no-CORS blob URL script with no integrity runs", 131 url: blob_url, 132 cross_origin: false, 133 integrity: "", 134 policy_violation: false, 135 add_blocking_header: true, 136 endpoints: true, 137 expected: {blocked: "", ran: true}, 138 }, 139 { 140 description: "Ensure that an about:blank URL script with no integrity does not trigger a report", 141 url: "about:blank", 142 cross_origin: true, 143 integrity: "", 144 policy_violation: false, 145 add_blocking_header: true, 146 endpoints: false, 147 expected: {blocked: "", ran: false}, 148 }, 149 { 150 description: "Ensure that a no-CORS about:blank URL script with no integrity does not trigger a report", 151 url: "about:blank", 152 cross_origin: false, 153 integrity: "", 154 policy_violation: false, 155 add_blocking_header: true, 156 endpoints: false, 157 expected: {blocked: "", ran: false}, 158 } 159 ]; 160 test_cases.map(test_case => { 161 promise_test(async () => { 162 const REMOTE_EXECUTOR = 163 `/common/dispatcher/remote-executor.html?pipe=`; 164 const iframe_uuid = token(); 165 166 const params = new URLSearchParams(location.search); 167 if (params.get('type') === "report") { 168 if (test_case.expected.blocked) { 169 return; 170 } 171 header_name += "-Report-Only"; 172 } 173 const reporting_uuid_1 = token(); 174 const reporting_uuid_2 = token(); 175 const reporting_uuid_3 = token(); 176 const reporting_endpoint = `${ORIGIN}/reporting/resources/report.py`; 177 let header = ""; 178 if (test_case.add_blocking_header) { 179 header += 180 `header(Integrity-Policy,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-1 integrity-endpoint-2\\))`; 181 } 182 header += 183 `|header(Integrity-Policy-Report-Only,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-3\\))`; 184 if (test_case.endpoints) { 185 header += 186 `|header(Reporting-Endpoints, integrity-endpoint-1=\"${reporting_endpoint}?reportID=${reporting_uuid_1}\"\\, ` + 187 `integrity-endpoint-2=\"${reporting_endpoint}?reportID=${reporting_uuid_2}\"\\, ` + 188 `integrity-endpoint-3=\"${reporting_endpoint}?reportID=${reporting_uuid_3}\")`; 189 } 190 const iframe_url = `${REMOTE_EXECUTOR}${encodeURIComponent(header)}&uuid=${iframe_uuid}`; 191 192 const iframe = document.createElement('iframe'); 193 iframe.src = iframe_url; 194 document.body.appendChild(iframe); 195 196 // Execute code directly from the iframe. 197 const ctx = new RemoteContext(iframe_uuid); 198 const result = await ctx.execute_script(async (test_case, with_reporting) => { 199 window.ran = false; 200 201 // Always set up report observer to catch any unexpected reports 202 const report_observed_promise = new Promise(r => { 203 (new ReportingObserver((reports, observer) => { 204 reports.forEach(report => { 205 // Match the tested URL against the stripped one: 206 // https://www.w3.org/TR/reporting-1/#strip-url-for-use-in-reports 207 // 1. If url’s scheme is not an HTTP(S) scheme, then return url’s scheme. 208 const test_url = test_case.url.startsWith("http") ? 209 test_case.url : 210 test_case.url.split(":")[0]; 211 if (report.body.blockedURL.endsWith(test_url)) { 212 r(report.body); 213 observer.disconnect(); 214 } 215 }); 216 })).observe('integrity-violation'); 217 }); 218 219 // Load the script 220 await new Promise(resolve => { 221 const script = document.createElement('script'); 222 if (test_case.cross_origin) { 223 script.crossOrigin="anonymous"; 224 } 225 if (test_case.integrity) { 226 script.integrity = test_case.integrity; 227 } 228 script.onload = resolve; 229 script.onerror = resolve; 230 script.src = test_case.url; 231 document.body.appendChild(script); 232 }); 233 let report_body = null; 234 if (with_reporting) { 235 if (test_case.policy_violation) { 236 report_body = await report_observed_promise; 237 } else { 238 const timeout_promise = new Promise(r => setTimeout(() => r('timeout'), 100)); 239 const race_result = await Promise.race([report_observed_promise, timeout_promise]); 240 if (race_result !== 'timeout') { 241 report_body = race_result; 242 } 243 } 244 } 245 return { body: report_body, ran: window.ran }; 246 }, [test_case, with_reporting]); 247 248 assert_equals(result.ran, test_case.expected.ran, "Ran"); 249 if (with_reporting) { 250 if (test_case.policy_violation) { 251 assert_not_equals(result.body, null, "Expected a policy violation report"); 252 assert_equals(result.body.blockedURL, test_case.expected.blocked); 253 assert_true(result.body.documentURL.endsWith(iframe_url)); 254 assert_equals(result.body.destination, "script"); 255 assert_equals(result.body.reportOnly, !test_case.add_blocking_header); 256 } else { 257 assert_equals(result.body, null, "No policy violation report should be observed"); 258 } 259 260 if (test_case.endpoints && test_case.policy_violation) { 261 if (test_case.add_blocking_header) { 262 await check_report(reporting_endpoint, reporting_uuid_1, iframe_url, test_case.url, !test_case.add_blocking_header); 263 await check_report(reporting_endpoint, reporting_uuid_2, iframe_url, test_case.url, !test_case.add_blocking_header); 264 } 265 await check_report(reporting_endpoint, reporting_uuid_3, iframe_url, test_case.url, true); 266 } 267 } 268 }, test_case.description); 269 }); 270 </script>