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