1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/test/csp/test_bug836922_npolicies.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,249 @@ 1.4 +<!DOCTYPE HTML> 1.5 +<html> 1.6 +<head> 1.7 + <title>Test for Content Security Policy multiple policy support (regular and Report-Only mode)</title> 1.8 + <script type="text/javascript" src="/MochiKit/packed.js"></script> 1.9 + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 1.10 + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 1.11 +</head> 1.12 +<body> 1.13 +<p id="display"></p> 1.14 +<div id="content" style="display: none"> 1.15 +</div> 1.16 + 1.17 +<iframe style="width:200px;height:200px;" id='cspframe'></iframe> 1.18 +<script class="testbody" type="text/javascript"> 1.19 + 1.20 +var path = "/tests/content/base/test/csp/"; 1.21 + 1.22 +// These are test results: verified indicates whether or not the test has run. 1.23 +// true/false is the pass/fail result. 1.24 +window.loads = { 1.25 + css_self: {expected: true, verified: false}, 1.26 + css_examplecom: {expected: false, verified: false}, 1.27 + img_self: {expected: false, verified: false}, 1.28 + img_examplecom: {expected: false, verified: false}, 1.29 + script_self: {expected: true, verified: false}, 1.30 +}; 1.31 + 1.32 +window.violation_reports = { 1.33 + css_self: 1.34 + {expected: 0, expected_ro: 0}, /* totally fine */ 1.35 + css_examplecom: 1.36 + {expected: 1, expected_ro: 0}, /* violates enforced CSP */ 1.37 + img_self: 1.38 + {expected: 1, expected_ro: 0}, /* violates enforced CSP */ 1.39 + img_examplecom: 1.40 + {expected: 1, expected_ro: 1}, /* violates both CSPs */ 1.41 + script_self: 1.42 + {expected: 0, expected_ro: 1}, /* violates report-only */ 1.43 +}; 1.44 + 1.45 +// This is used to watch the blocked data bounce off CSP and allowed data 1.46 +// get sent out to the wire. This also watches for violation reports to go out. 1.47 +function examiner() { 1.48 + SpecialPowers.addObserver(this, "csp-on-violate-policy", false); 1.49 + SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false); 1.50 +} 1.51 +examiner.prototype = { 1.52 + observe: function(subject, topic, data) { 1.53 + var testpat = new RegExp("testid=([a-z0-9_]+)"); 1.54 + 1.55 + if (topic === "specialpowers-http-notify-request") { 1.56 + var uri = data; 1.57 + if (!testpat.test(uri)) return; 1.58 + var testid = testpat.exec(uri)[1]; 1.59 + 1.60 + // violation reports don't come through here, but the requested resources do 1.61 + // if the test has already finished, move on. Some things throw multiple 1.62 + // requests (preloads and such) 1.63 + try { 1.64 + if (window.loads[testid].verified) return; 1.65 + } catch(e) { return; } 1.66 + 1.67 + // these are requests that were allowed by CSP 1.68 + var testid = testpat.exec(uri)[1]; 1.69 + window.testResult(testid, 'allowed', uri + " allowed by csp"); 1.70 + } 1.71 + 1.72 + if(topic === "csp-on-violate-policy") { 1.73 + // if the violated policy was report-only, the resource will still be 1.74 + // loaded even if this topic is notified. 1.75 + var asciiSpec = SpecialPowers.getPrivilegedProps( 1.76 + SpecialPowers.do_QueryInterface(subject, "nsIURI"), 1.77 + "asciiSpec"); 1.78 + if (!testpat.test(asciiSpec)) return; 1.79 + var testid = testpat.exec(asciiSpec)[1]; 1.80 + 1.81 + // if the test has already finished, move on. 1.82 + try { 1.83 + if (window.loads[testid].verified) return; 1.84 + } catch(e) { return; } 1.85 + 1.86 + // record the ones that were supposed to be blocked, but don't use this 1.87 + // as an indicator for tests that are not blocked but do generate reports. 1.88 + // We skip recording the result if the load is expected since a 1.89 + // report-only policy will generate a request *and* a violation note. 1.90 + if (!window.loads[testid].expected) { 1.91 + window.testResult(testid, 1.92 + 'blocked', 1.93 + asciiSpec + " blocked by \"" + data + "\""); 1.94 + } 1.95 + } 1.96 + 1.97 + // if any test is unverified, keep waiting 1.98 + for (var v in window.loads) { 1.99 + if(!window.loads[v].verified) { 1.100 + return; 1.101 + } 1.102 + } 1.103 + 1.104 + window.bug836922examiner.remove(); 1.105 + window.resultPoller.pollForFinish(); 1.106 + }, 1.107 + 1.108 + // must eventually call this to remove the listener, 1.109 + // or mochitests might get borked. 1.110 + remove: function() { 1.111 + SpecialPowers.removeObserver(this, "csp-on-violate-policy"); 1.112 + SpecialPowers.removeObserver(this, "specialpowers-http-notify-request"); 1.113 + } 1.114 +} 1.115 +window.bug836922examiner = new examiner(); 1.116 + 1.117 + 1.118 +// Poll for results and see if enough reports came in. Keep trying 1.119 +// for a few seconds before failing with lack of reports. 1.120 +// Have to do this because there's a race between the async reporting 1.121 +// and this test finishing, and we don't want to win the race. 1.122 +window.resultPoller = { 1.123 + 1.124 + POLL_ATTEMPTS_LEFT: 14, 1.125 + 1.126 + pollForFinish: 1.127 + function() { 1.128 + var vr = resultPoller.tallyReceivedReports(); 1.129 + if (resultPoller.verifyReports(vr, resultPoller.POLL_ATTEMPTS_LEFT < 1)) { 1.130 + // report success condition. 1.131 + resultPoller.resetReportServer(); 1.132 + SimpleTest.finish(); 1.133 + } else { 1.134 + resultPoller.POLL_ATTEMPTS_LEFT--; 1.135 + // try again unless we reached the threshold. 1.136 + setTimeout(resultPoller.pollForFinish, 100); 1.137 + } 1.138 + }, 1.139 + 1.140 + resetReportServer: 1.141 + function() { 1.142 + var xhr = new XMLHttpRequest(); 1.143 + var xhr_ro = new XMLHttpRequest(); 1.144 + xhr.open("GET", "file_bug836922_npolicies_violation.sjs?reset", false); 1.145 + xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?reset", false); 1.146 + xhr.send(null); 1.147 + xhr_ro.send(null); 1.148 + }, 1.149 + 1.150 + tallyReceivedReports: 1.151 + function() { 1.152 + var xhr = new XMLHttpRequest(); 1.153 + var xhr_ro = new XMLHttpRequest(); 1.154 + xhr.open("GET", "file_bug836922_npolicies_violation.sjs?results", false); 1.155 + xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?results", false); 1.156 + xhr.send(null); 1.157 + xhr_ro.send(null); 1.158 + 1.159 + var received = JSON.parse(xhr.responseText); 1.160 + var received_ro = JSON.parse(xhr_ro.responseText); 1.161 + 1.162 + var results = {enforced: {}, reportonly: {}}; 1.163 + for (var r in window.violation_reports) { 1.164 + results.enforced[r] = 0; 1.165 + results.reportonly[r] = 0; 1.166 + } 1.167 + 1.168 + for (var r in received) { 1.169 + results.enforced[r] += received[r]; 1.170 + } 1.171 + for (var r in received_ro) { 1.172 + results.reportonly[r] += received_ro[r]; 1.173 + } 1.174 + 1.175 + return results; 1.176 + }, 1.177 + 1.178 + verifyReports: 1.179 + function(receivedCounts, lastAttempt) { 1.180 + for (var r in window.violation_reports) { 1.181 + var exp = window.violation_reports[r].expected; 1.182 + var exp_ro = window.violation_reports[r].expected_ro; 1.183 + var rec = receivedCounts.enforced[r]; 1.184 + var rec_ro = receivedCounts.reportonly[r]; 1.185 + 1.186 + // if this test breaks, these are helpful dumps: 1.187 + //dump(">>> Verifying " + r + "\n"); 1.188 + //dump(" > Expected: " + exp + " / " + exp_ro + " (ro)\n"); 1.189 + //dump(" > Received: " + rec + " / " + rec_ro + " (ro) \n"); 1.190 + 1.191 + // in all cases, we're looking for *at least* the expected number of 1.192 + // reports of each type (there could be more in some edge cases). 1.193 + // If there are not enough, we keep waiting and poll the server again 1.194 + // later. If there are enough, we can successfully finish. 1.195 + 1.196 + if (exp == 0) 1.197 + is(rec, 0, 1.198 + "Expected zero enforced-policy violation " + 1.199 + "reports for " + r + ", got " + rec); 1.200 + else if (lastAttempt) 1.201 + ok(rec >= exp, 1.202 + "Received (" + rec + "/" + exp + ") " + 1.203 + "enforced-policy reports for " + r); 1.204 + else if (rec < exp) 1.205 + return false; // continue waiting for more 1.206 + 1.207 + if(exp_ro == 0) 1.208 + is(rec_ro, 0, 1.209 + "Expected zero report-only-policy violation " + 1.210 + "reports for " + r + ", got " + rec_ro); 1.211 + else if (lastAttempt) 1.212 + ok(rec_ro >= exp_ro, 1.213 + "Received (" + rec_ro + "/" + exp_ro + ") " + 1.214 + "report-only-policy reports for " + r); 1.215 + else if (rec_ro < exp_ro) 1.216 + return false; // continue waiting for more 1.217 + } 1.218 + 1.219 + // if we complete the loop, we've found all of the violation 1.220 + // reports we expect. 1.221 + if (lastAttempt) return true; 1.222 + 1.223 + // Repeat successful tests once more to record successes via ok() 1.224 + return resultPoller.verifyReports(receivedCounts, true); 1.225 + } 1.226 +}; 1.227 + 1.228 +window.testResult = function(testname, result, msg) { 1.229 + // otherwise, make sure the allowed ones are expected and blocked ones are not. 1.230 + if (window.loads[testname].expected) { 1.231 + is(result, 'allowed', ">> " + msg); 1.232 + } else { 1.233 + is(result, 'blocked', ">> " + msg); 1.234 + } 1.235 + window.loads[testname].verified = true; 1.236 +} 1.237 + 1.238 + 1.239 +SimpleTest.waitForExplicitFinish(); 1.240 + 1.241 +SpecialPowers.pushPrefEnv( 1.242 + {'set':[["security.csp.speccompliant", true]]}, 1.243 + function() { 1.244 + // save this for last so that our listeners are registered. 1.245 + // ... this loads the testbed of good and bad requests. 1.246 + document.getElementById('cspframe').src = 'http://mochi.test:8888' + path + 'file_bug836922_npolicies.html'; 1.247 + }); 1.248 + 1.249 +</script> 1.250 +</pre> 1.251 +</body> 1.252 +</html>