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