content/base/test/csp/test_bug836922_npolicies.html

changeset 0
6474c204b198
     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>

mercurial