test_bug836922_npolicies.html (8041B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test for Content Security Policy multiple policy support (regular and Report-Only mode)</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 7 </head> 8 <body> 9 <p id="display"></p> 10 <div id="content" style="display: none"> 11 </div> 12 13 <iframe style="width:200px;height:200px;" id='cspframe'></iframe> 14 <script class="testbody" type="text/javascript"> 15 16 var path = "/tests/dom/security/test/csp/"; 17 18 // These are test results: verified indicates whether or not the test has run. 19 // true/false is the pass/fail result. 20 window.loads = { 21 css_self: {expected: true, verified: false}, 22 img_self: {expected: false, verified: false}, 23 script_self: {expected: true, verified: false}, 24 }; 25 26 window.violation_reports = { 27 css_self: 28 {expected: 0, expected_ro: 0}, /* totally fine */ 29 img_self: 30 {expected: 1, expected_ro: 0}, /* violates enforced CSP */ 31 script_self: 32 {expected: 0, expected_ro: 1}, /* violates report-only */ 33 }; 34 35 // This is used to watch the blocked data bounce off CSP and allowed data 36 // get sent out to the wire. This also watches for violation reports to go out. 37 function examiner() { 38 SpecialPowers.addObserver(this, "csp-on-violate-policy"); 39 SpecialPowers.addObserver(this, "specialpowers-http-notify-request"); 40 } 41 examiner.prototype = { 42 observe(subject, topic, data) { 43 var testpat = new RegExp("testid=([a-z0-9_]+)"); 44 45 if (topic === "specialpowers-http-notify-request") { 46 var uri = data; 47 if (!testpat.test(uri)) return; 48 var testid = testpat.exec(uri)[1]; 49 50 // violation reports don't come through here, but the requested resources do 51 // if the test has already finished, move on. Some things throw multiple 52 // requests (preloads and such) 53 try { 54 if (window.loads[testid].verified) return; 55 } catch(e) { return; } 56 57 // these are requests that were allowed by CSP 58 var testid = testpat.exec(uri)[1]; 59 window.testResult(testid, 'allowed', uri + " allowed by csp"); 60 } 61 62 if(topic === "csp-on-violate-policy") { 63 // if the violated policy was report-only, the resource will still be 64 // loaded even if this topic is notified. 65 var asciiSpec = SpecialPowers.getPrivilegedProps( 66 SpecialPowers.do_QueryInterface(subject, "nsIURI"), 67 "asciiSpec"); 68 if (!testpat.test(asciiSpec)) return; 69 var testid = testpat.exec(asciiSpec)[1]; 70 71 // if the test has already finished, move on. 72 try { 73 if (window.loads[testid].verified) return; 74 } catch(e) { return; } 75 76 // record the ones that were supposed to be blocked, but don't use this 77 // as an indicator for tests that are not blocked but do generate reports. 78 // We skip recording the result if the load is expected since a 79 // report-only policy will generate a request *and* a violation note. 80 if (!window.loads[testid].expected) { 81 window.testResult(testid, 82 'blocked', 83 asciiSpec + " blocked by \"" + data + "\""); 84 } 85 } 86 87 // if any test is unverified, keep waiting 88 for (var v in window.loads) { 89 if(!window.loads[v].verified) { 90 return; 91 } 92 } 93 94 window.bug836922examiner.remove(); 95 window.resultPoller.pollForFinish(); 96 }, 97 98 // must eventually call this to remove the listener, 99 // or mochitests might get borked. 100 remove() { 101 SpecialPowers.removeObserver(this, "csp-on-violate-policy"); 102 SpecialPowers.removeObserver(this, "specialpowers-http-notify-request"); 103 } 104 } 105 window.bug836922examiner = new examiner(); 106 107 108 // Poll for results and see if enough reports came in. Keep trying 109 // for a few seconds before failing with lack of reports. 110 // Have to do this because there's a race between the async reporting 111 // and this test finishing, and we don't want to win the race. 112 window.resultPoller = { 113 114 POLL_ATTEMPTS_LEFT: 14, 115 116 pollForFinish() { 117 var vr = resultPoller.tallyReceivedReports(); 118 if (resultPoller.verifyReports(vr, resultPoller.POLL_ATTEMPTS_LEFT < 1)) { 119 // report success condition. 120 resultPoller.resetReportServer(); 121 SimpleTest.finish(); 122 } else { 123 resultPoller.POLL_ATTEMPTS_LEFT--; 124 // try again unless we reached the threshold. 125 setTimeout(resultPoller.pollForFinish, 100); 126 } 127 }, 128 129 resetReportServer() { 130 var xhr = new XMLHttpRequest(); 131 var xhr_ro = new XMLHttpRequest(); 132 xhr.open("GET", "file_bug836922_npolicies_violation.sjs?reset", false); 133 xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?reset", false); 134 xhr.send(null); 135 xhr_ro.send(null); 136 }, 137 138 tallyReceivedReports() { 139 var xhr = new XMLHttpRequest(); 140 var xhr_ro = new XMLHttpRequest(); 141 xhr.open("GET", "file_bug836922_npolicies_violation.sjs?results", false); 142 xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?results", false); 143 xhr.send(null); 144 xhr_ro.send(null); 145 146 var received = JSON.parse(xhr.responseText); 147 var received_ro = JSON.parse(xhr_ro.responseText); 148 149 var results = {enforced: {}, reportonly: {}}; 150 for (var r in window.violation_reports) { 151 results.enforced[r] = 0; 152 results.reportonly[r] = 0; 153 } 154 155 for (var r in received) { 156 results.enforced[r] += received[r]; 157 } 158 for (var r in received_ro) { 159 results.reportonly[r] += received_ro[r]; 160 } 161 162 return results; 163 }, 164 165 verifyReports(receivedCounts, lastAttempt) { 166 for (var r in window.violation_reports) { 167 var exp = window.violation_reports[r].expected; 168 var exp_ro = window.violation_reports[r].expected_ro; 169 var rec = receivedCounts.enforced[r]; 170 var rec_ro = receivedCounts.reportonly[r]; 171 172 // if this test breaks, these are helpful dumps: 173 //dump(">>> Verifying " + r + "\n"); 174 //dump(" > Expected: " + exp + " / " + exp_ro + " (ro)\n"); 175 //dump(" > Received: " + rec + " / " + rec_ro + " (ro) \n"); 176 177 // in all cases, we're looking for *at least* the expected number of 178 // reports of each type (there could be more in some edge cases). 179 // If there are not enough, we keep waiting and poll the server again 180 // later. If there are enough, we can successfully finish. 181 182 if (exp == 0) 183 is(rec, 0, 184 "Expected zero enforced-policy violation " + 185 "reports for " + r + ", got " + rec); 186 else if (lastAttempt) 187 ok(rec >= exp, 188 "Received (" + rec + "/" + exp + ") " + 189 "enforced-policy reports for " + r); 190 else if (rec < exp) 191 return false; // continue waiting for more 192 193 if(exp_ro == 0) 194 is(rec_ro, 0, 195 "Expected zero report-only-policy violation " + 196 "reports for " + r + ", got " + rec_ro); 197 else if (lastAttempt) 198 ok(rec_ro >= exp_ro, 199 "Received (" + rec_ro + "/" + exp_ro + ") " + 200 "report-only-policy reports for " + r); 201 else if (rec_ro < exp_ro) 202 return false; // continue waiting for more 203 } 204 205 // if we complete the loop, we've found all of the violation 206 // reports we expect. 207 if (lastAttempt) return true; 208 209 // Repeat successful tests once more to record successes via ok() 210 return resultPoller.verifyReports(receivedCounts, true); 211 } 212 }; 213 214 window.testResult = function(testname, result, msg) { 215 // otherwise, make sure the allowed ones are expected and blocked ones are not. 216 if (window.loads[testname].expected) { 217 is(result, 'allowed', ">> " + msg); 218 } else { 219 is(result, 'blocked', ">> " + msg); 220 } 221 window.loads[testname].verified = true; 222 } 223 224 225 SimpleTest.waitForExplicitFinish(); 226 SimpleTest.requestFlakyTimeout("untriaged"); 227 228 // save this for last so that our listeners are registered. 229 // ... this loads the testbed of good and bad requests. 230 document.getElementById('cspframe').src = 'http://mochi.test:8888' + path + 'file_bug836922_npolicies.html'; 231 232 </script> 233 </pre> 234 </body> 235 </html>