test-case.sub.js (4247B)
1 // https://w3c.github.io/webappsec-referrer-policy/#strip-url 2 function stripUrlForUseAsReferrer(url, originOnly) { 3 // Step 2. If url’s scheme is a local scheme, then return no referrer. 4 const parsedUrl = new URL(url); 5 6 if (["about:", "blob:", "data:"].includes(parsedUrl.protocol)) 7 return undefined; 8 9 // Step 3. Set url’s username to the empty string. 10 parsedUrl.username = ''; 11 12 // Step 4. Set url’s password to null. 13 parsedUrl.password = ''; 14 15 // Step 5. Set url’s fragment to null. 16 parsedUrl.hash = ''; 17 18 // Step 6. If the origin-only flag is true, then: 19 if (originOnly) { 20 // Step 6.1. Set url’s path to null. 21 parsedUrl.pathname = ''; 22 // Step 6.2. Set url’s query to null. 23 parsedUrl.search = ''; 24 } 25 return parsedUrl.href; 26 } 27 28 function invokeScenario(scenario) { 29 const urls = getRequestURLs( 30 scenario.subresource, 31 scenario.origin, 32 scenario.redirection); 33 /** @type {Subresource} */ 34 const subresource = { 35 subresourceType: scenario.subresource, 36 url: urls.testUrl, 37 policyDeliveries: scenario.subresource_policy_deliveries, 38 }; 39 40 return invokeRequest(subresource, scenario.source_context_list); 41 } 42 43 const referrerUrlResolver = { 44 // The spec allows UAs to "enforce arbitrary policy considerations in the 45 // interests of minimizing data leakage"; to start to vaguely approximate 46 // this, we allow stronger policies to be used instead of what's specificed. 47 "omitted": function(sourceUrl) { 48 return [undefined]; 49 }, 50 "origin": function(sourceUrl) { 51 return [stripUrlForUseAsReferrer(sourceUrl, true), 52 undefined]; 53 }, 54 "stripped-referrer": function(sourceUrl) { 55 return [stripUrlForUseAsReferrer(sourceUrl, false), 56 stripUrlForUseAsReferrer(sourceUrl, true), 57 undefined]; 58 } 59 }; 60 61 function checkResult(scenario, expectation, result) { 62 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer 63 let referrerSource = result.sourceContextUrl; 64 const sentFromSrcdoc = scenario.source_context_list.length > 0 && 65 scenario.source_context_list[scenario.source_context_list.length - 1] 66 .sourceContextType === 'srcdoc'; 67 if (sentFromSrcdoc) { 68 // Step 3. While document is an iframe srcdoc document, let document be 69 // document's browsing context's browsing context container's node 70 // document. [spec text] 71 72 // Workaround for srcdoc cases. Currently we only test <iframe srcdoc> 73 // inside the top-level Document, so |document| in the spec here is 74 // the top-level Document. 75 // This doesn't work if e.g. we test <iframe srcdoc> inside another 76 // external <iframe>. 77 referrerSource = location.toString(); 78 } 79 const possibleReferrerUrls = 80 referrerUrlResolver[expectation](referrerSource); 81 82 // Check the reported URL. 83 assert_in_array(result.referrer, 84 possibleReferrerUrls, 85 "document.referrer"); 86 assert_in_array(result.headers.referer, 87 possibleReferrerUrls, 88 "HTTP Referer header"); 89 } 90 91 function runLengthTest(scenario, urlLength, expectation, testDescription) { 92 // `Referer` headers with length over 4k are culled down to an origin, so, 93 // let's test around that boundary for tests that would otherwise return 94 // the complete URL. 95 history.pushState(null, null, "/"); 96 history.replaceState(null, null, 97 "A".repeat(urlLength - location.href.length)); 98 99 promise_test(t => { 100 assert_equals(scenario.expectation, "stripped-referrer"); 101 // Only on top-level Window, due to navigations using `history`. 102 assert_equals(scenario.source_context_list.length, 0); 103 104 return invokeScenario(scenario) 105 .then(result => checkResult(scenario, expectation, result)); 106 }, testDescription); 107 } 108 109 function TestCase(scenarios, sanityChecker) { 110 function runTest(scenario) { 111 // This check is A NOOP in release. 112 sanityChecker.checkScenario(scenario); 113 114 promise_test(_ => { 115 return invokeScenario(scenario) 116 .then(result => checkResult(scenario, scenario.expectation, result)); 117 }, scenario.test_description); 118 } 119 120 function runTests() { 121 for (const scenario of scenarios) { 122 runTest(scenario); 123 } 124 } 125 126 return {start: runTests}; 127 }