reporting-to-document-reporting-endpoint.https.window.js (5329B)
1 // META: script=/common/get-host-info.sub.js 2 // META: script=/common/utils.js 3 4 // This file consists of tests for COEP reporting using Reporting-Endpoints 5 // header. It exclusively tests that reports can be sent to Reporting-Endpoint 6 // configured endpoint. 7 const { REMOTE_ORIGIN } = get_host_info(); 8 9 const REPORT_ENDPOINT = token(); 10 const REPORT_ONLY_ENDPOINT = token(); 11 const FRAME_URL = `resources/reporting-empty-frame.html` + 12 `?pipe=header(cross-origin-embedder-policy,require-corp;report-to="endpoint")` + 13 `|header(cross-origin-embedder-policy-report-only,require-corp;report-to="report-only-endpoint")` + 14 `|header(reporting-endpoints, endpoint="/html/cross-origin-embedder-policy/resources/report.py?key=${REPORT_ENDPOINT}"\\, report-only-endpoint="/html/cross-origin-embedder-policy/resources/report.py?key=${REPORT_ONLY_ENDPOINT}")`; 15 16 function wait(ms) { 17 return new Promise(resolve => step_timeout(resolve, ms)); 18 } 19 20 async function fetchReports(endpoint) { 21 const res = await fetch(`resources/report.py?key=${endpoint}`, { 22 cache: 'no-store' 23 }); 24 if (res.status == 200) { 25 return await res.json(); 26 } 27 return []; 28 } 29 30 async function fetchCoepReport( 31 endpoint, type, blockedUrl, contextUrl, disposition) { 32 blockedUrl = new URL(blockedUrl, location).href; 33 contextUrl = new URL(contextUrl, location).href; 34 const reports = await fetchReports(endpoint); 35 return reports.find(r => ( 36 r.type == 'coep' && 37 r.url == contextUrl && 38 r.body.type == type && 39 r.body.blockedURL === blockedUrl && 40 r.body.disposition === disposition)); 41 } 42 43 async function checkCorpReportExists( 44 endpoint, blockedUrl, contextUrl, destination, disposition) { 45 blockedUrl = new URL(blockedUrl, location).href; 46 contextUrl = new URL(contextUrl, location).href; 47 contextUrl.replace(REPORT_ENDPOINT, "REPORT_ENDPOINT_UUID"); 48 contextUrl.replace(REPORT_ONLY_ENDPOINT, "REPORT_ONLY_ENDPOINT_UUID"); 49 const report = await fetchCoepReport( 50 endpoint, 'corp', blockedUrl, contextUrl, disposition); 51 assert_true(!!report, 52 `A corp report with blockedURL ${blockedUrl.split("?")[0]} ` + 53 `and url ${contextUrl} is not found.`); 54 assert_equals(report.body.destination, destination); 55 } 56 57 async function checkNavigationReportExists( 58 endpoint, blockedUrl, contextUrl, disposition) { 59 blockedUrl = new URL(blockedUrl, location).href; 60 contextUrl = new URL(contextUrl, location).href; 61 contextUrl.replace(REPORT_ENDPOINT, "REPORT_ENDPOINT_UUID"); 62 contextUrl.replace(REPORT_ONLY_ENDPOINT, "REPORT_ONLY_ENDPOINT_UUID"); 63 const report = await fetchCoepReport( 64 endpoint, 'navigation', blockedUrl, contextUrl, disposition); 65 assert_true(!!report, 66 `A navigation report with blockedURL ${blockedUrl.split("?")[0]} ` + 67 `and url ${contextUrl} is not found.`); 68 } 69 70 promise_test(async t => { 71 const iframe = document.createElement('iframe'); 72 t.add_cleanup(() => iframe.remove()); 73 74 iframe.src = FRAME_URL; 75 await new Promise(resolve => { 76 iframe.addEventListener('load', resolve, { once: true }); 77 document.body.appendChild(iframe); 78 }); 79 80 const url = `${REMOTE_ORIGIN}/common/text-plain.txt?${token()}`; 81 const init = { mode: 'no-cors', cache: 'no-store' }; 82 // The response comes from cross-origin, and doesn't have a CORP 83 // header, so it is blocked. 84 iframe.contentWindow.fetch(url, init).catch(() => { }); 85 86 // Wait for reports to be uploaded. 87 await wait(1000); 88 await checkCorpReportExists( 89 REPORT_ENDPOINT, url, iframe.src, '', 'enforce'); 90 await checkCorpReportExists( 91 REPORT_ONLY_ENDPOINT, url, iframe.src, '', 'reporting'); 92 }, 'subresource CORP'); 93 94 promise_test(async t => { 95 const iframe = document.createElement('iframe'); 96 t.add_cleanup(() => iframe.remove()); 97 98 iframe.src = FRAME_URL; 99 await new Promise(resolve => { 100 iframe.addEventListener('load', resolve, { once: true }); 101 document.body.appendChild(iframe); 102 }); 103 104 const url = `${REMOTE_ORIGIN}/common/blank.html?${token()}`; 105 // The nested frame comes from cross-origin and doesn't have a CORP 106 // header, so it is blocked. 107 const nested = iframe.contentWindow.document.createElement('iframe'); 108 nested.src = url; 109 iframe.contentWindow.document.body.appendChild(nested); 110 111 // Wait for reports to be uploaded. 112 await wait(1000); 113 await checkCorpReportExists( 114 REPORT_ENDPOINT, url, iframe.src, 'iframe', 'enforce'); 115 await checkCorpReportExists( 116 REPORT_ONLY_ENDPOINT, url, iframe.src, 'iframe', 'reporting'); 117 }, 'navigation CORP on cross origin'); 118 119 promise_test(async (t) => { 120 const iframe = document.createElement('iframe'); 121 t.add_cleanup(() => iframe.remove()); 122 123 iframe.src = FRAME_URL; 124 const targetUrl = `/common/blank.html?${token()}`; 125 iframe.addEventListener('load', t.step_func(() => { 126 const nested = iframe.contentDocument.createElement('iframe'); 127 nested.src = targetUrl; 128 // |nested| doesn't have COEP whereas |iframe| has, so it is blocked. 129 iframe.contentDocument.body.appendChild(nested); 130 }), { once: true }); 131 132 document.body.appendChild(iframe); 133 134 // Wait for reports to be uploaded. 135 await wait(1000); 136 await checkNavigationReportExists( 137 REPORT_ENDPOINT, targetUrl, iframe.src, 'enforce'); 138 await checkNavigationReportExists( 139 REPORT_ONLY_ENDPOINT, targetUrl, iframe.src, 'reporting'); 140 }, 'navigation CORP on same origin');