content/base/test/csp/test_bug836922_npolicies.html

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial