Bug 965727 - Implement tests for CSP referrer directive. (r=ckerschb)
authorSid Stamm <sstamm@mozilla.com>
Wed, 17 Dec 2014 14:14:03 -0500
changeset 220218 7c2973cb4fa1a0b0c1044a16c10d046b2538e0ea
parent 220217 6f29a19dc2e84322d3a3c335e3f2e86900c3082a
child 220219 eabee47625c6d680786f498c60d9cf4c1a7ef238
push id10457
push userryanvm@gmail.com
push dateThu, 18 Dec 2014 01:54:25 +0000
treeherderfx-team@0e441ff66c5e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb
bugs965727
milestone37.0a1
Bug 965727 - Implement tests for CSP referrer directive. (r=ckerschb)
dom/base/test/csp/file_csp_referrerdirective.html
dom/base/test/csp/mochitest.ini
dom/base/test/csp/referrerdirective.sjs
dom/base/test/csp/test_CSP_referrerdirective.html
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_csp_referrerdirective.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Subframe test for bug 965727</title>
+
+<script type="text/javascript">
+// we can get the ID out of the querystring.
+var args = document.location.search.substring(1).split('&');
+var id = "unknown";
+for (var i=0; i < args.length; i++) {
+  var arg = unescape(args[i]);
+  if (arg.indexOf('=') > 0 && arg.indexOf('id') == 0) {
+    id = arg.split('=')[1].trim();
+  }
+}
+
+var results = {
+  'id': id,
+  'referrer': document.location.href,
+  'results': {
+    'sameorigin': false,
+    'crossorigin': false,
+    'downgrade': false
+  }
+};
+
+// this is called back by each script load.
+var postResult = function(loadType, referrerLevel, referrer) {
+  results.results[loadType] = referrerLevel;
+
+  // and then check if all three have loaded.
+  for (var id in results.results) {
+    if (!results.results[id]) {
+      return;
+    }
+  }
+  //finished if we don't return early
+  window.parent.postMessage(JSON.stringify(results), "*");
+  console.log(JSON.stringify(results));
+}
+
+</script>
+</head>
+<body>
+Testing ...
+
+<script src="https://example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=sameorigin&"
+        onerror="postResult('sameorigin', 'error');"></script>
+<script src="https://test2.example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=crossorigin&"
+        onerror="postResult('crossorigin', 'error');"></script>
+<script src="http://example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=downgrade&"
+        onerror="postResult('downgrade', 'error');"></script>
+
+</body>
+</html>
--- a/dom/base/test/csp/mochitest.ini
+++ b/dom/base/test/csp/mochitest.ini
@@ -98,16 +98,18 @@ support-files =
   file_leading_wildcard.html
   file_multi_policy_injection_bypass.html
   file_multi_policy_injection_bypass.html^headers^
   file_multi_policy_injection_bypass_2.html
   file_multi_policy_injection_bypass_2.html^headers^
   file_form-action.html
   file_worker_redirect.html
   file_worker_redirect.sjs
+  file_csp_referrerdirective.html
+  referrerdirective.sjs
 
 [test_base-uri.html]
 [test_connect-src.html]
 [test_CSP.html]
 [test_csp_allow_https_schemes.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
@@ -144,9 +146,10 @@ skip-if = buildapp == 'b2g' # intermitte
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_303_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_307_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_subframe_run_js_if_allowed.html]
 [test_leading_wildcard.html]
 [test_multi_policy_injection_bypass.html]
+[test_CSP_referrerdirective.html]
 [test_worker_redirect.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/referrerdirective.sjs
@@ -0,0 +1,36 @@
+// Used for bug 965727 to serve up really simple scripts reflecting the
+// referrer sent to load this back to the loader.
+
+
+function handleRequest(request, response) {
+  // skip speculative loads.
+
+  var splits = request.queryString.split('&');
+  var params = {};
+  splits.forEach(function(v) {
+    let parts = v.split('=');
+    params[parts[0]] = unescape(parts[1]);
+  });
+
+  var loadType = params['type'];
+  var referrerLevel = 'error';
+
+  if (request.hasHeader('Referer')) {
+    var referrer = request.getHeader('Referer');
+    if (referrer.indexOf("file_csp_testserver.sjs") > -1) {
+      referrerLevel = "full";
+    } else {
+      referrerLevel = "origin";
+    }
+  } else {
+    referrerLevel = 'none';
+  }
+
+  var theScript = 'window.postResult("' + loadType + '", "' + referrerLevel + '");';
+  response.setHeader('Content-Type', 'application/javascript; charset=utf-8', false);
+  response.setHeader('Cache-Control', 'no-cache', false);
+
+  if (request.method != "OPTIONS") {
+  response.write(theScript);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/test_CSP_referrerdirective.html
@@ -0,0 +1,125 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=965727
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Content Security Policy referrer Directive (Bug 965727)</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/*
+ * This tests various referrer policies and the referrer-sending behavior when
+ * requesting scripts in different ways:
+ *  - cross-origin (https://example.com -> https://test2.example.com)
+ *  - same-origin (https://example.com -> https://example.com)
+ *  - downgrade (https://example.com -> http://example.com)
+ *
+ * Each test creates an iframe that loads scripts for each of the checks.  If
+ * the scripts are blocked, the test fails (they should run).  When loaded,
+ * each script updates a results object in the test page, and then when the
+ * test page has finished loading all the scripts, it postMessages back to this
+ * page.  Once all tests are done, the results are checked.
+ */
+
+var testData = {
+  'default': { 'csp': "script-src * 'unsafe-inline'; referrer default",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'full',
+                          'downgrade': 'none' }},
+
+  'origin':  { 'csp': "script-src * 'unsafe-inline'; referrer origin",
+          'expected': {  'sameorigin': 'origin',
+                        'crossorigin': 'origin',
+                          'downgrade': 'origin' }},
+
+  'origin-when-crossorigin':  { 'csp': "script-src * 'unsafe-inline'; referrer origin-when-crossorigin",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'origin',
+                          'downgrade': 'none' }},
+
+  'unsafe-url':  { 'csp': "script-src * 'unsafe-inline'; referrer unsafe-url",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'full',
+                          'downgrade': 'full' }},
+
+  'none':   { 'csp': "script-src * 'unsafe-inline'; referrer no-referrer",
+          'expected': {  'sameorigin': 'none',
+                        'crossorigin': 'none',
+                          'downgrade': 'none' }},
+  };
+
+
+var referrerDirectiveTests = {
+  // called via postMessage when one of the iframes is done running.
+  onIframeComplete: function(event) {
+    try {
+      var results = JSON.parse(event.data);
+      ok(results.hasOwnProperty('id'), "'id' property required in posted message " + event.data);
+
+      ok(testData.hasOwnProperty(results['id']), "Test " + results['id'] + " must be expected.");
+
+      // check all the various load types' referrers.
+      var expected = testData[results['id']].expected;
+      for (var t in expected) {
+        is(results.results[t], expected[t],
+          " referrer must match expected for " + t + " in " + results['id']);
+      }
+      testData[results['id']]['complete'] = true;
+
+    } catch(e) {
+      // fail -- should always be JSON
+      ok(false, "failed to parse posted message + " + event.data);
+      // have to end as well since not all messages were valid.
+      SimpleTest.finish();
+    }
+
+    referrerDirectiveTests.checkForCompletion();
+  },
+
+  // checks to see if all the parallel tests are done and validates results.
+  checkForCompletion: function() {
+    for (var id in testData) {
+      if (!testData[id].hasOwnProperty('complete')) {
+        return;
+      }
+    }
+    SimpleTest.finish();
+  }
+};
+
+SimpleTest.waitForExplicitFinish();
+// have to disable mixed content blocking to test https->http referrers.
+SpecialPowers.pushPrefEnv({
+    'set': [['security.mixed_content.block_active_content',   false],
+            ['security.mixed_content.block_display_content',  false]]
+    },
+    function() {
+      // each of the iframes we create will call us back when its contents are loaded.
+      window.addEventListener("message", referrerDirectiveTests.onIframeComplete.bind(window), false);
+
+      // one iframe created for each test case
+      for (var id in testData) {
+        var elt = document.createElement("iframe");
+        elt.src = "https://example.com/tests/dom/base/test/csp/file_csp_testserver.sjs?" +
+                  "id=" + id +
+                  "&csp=" + escape(testData[id]['csp']) +
+                  "&file=tests/dom/base/test/csp/file_csp_referrerdirective.html";
+        document.getElementById("content").appendChild(elt);
+      }
+    });
+</script>
+</pre>
+</body>
+</html>