Bug 888172 - CSP 1.0 does not process 'unsafe-inline' or 'unsafe-eval' for default-src. r=imelven
authorGarrett Robinson <grobinson@mozilla.com>
Wed, 24 Jul 2013 17:48:30 -0400
changeset 139877 ff3b2131de1289d1609dc69f14a569c839205815
parent 139876 bf3c83a7aed05e9ca91802e16a0045a848535372
child 139878 0f7620a5047a753f2f8b200d8a0e9979d215387d
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersimelven
bugs888172
milestone25.0a1
Bug 888172 - CSP 1.0 does not process 'unsafe-inline' or 'unsafe-eval' for default-src. r=imelven
content/base/src/CSPUtils.jsm
content/base/test/Makefile.in
content/base/test/file_CSP_bug888172.html
content/base/test/file_CSP_bug888172.sjs
content/base/test/test_CSP_bug888172.html
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -533,27 +533,33 @@ CSPRep.fromStringSpecCompliant = functio
   aCSPR._allowEval = true;
   aCSPR._allowInlineScripts = true;
   aCSPR._allowInlineStyles = true;
 
   // In CSP 1.0, you need to opt-in to blocking inline scripts and eval by
   // specifying either default-src or script-src, and to blocking inline
   // styles by specifying either default-src or style-src.
   if ("default-src" in dirs) {
-    aCSPR._allowInlineScripts = false;
-    aCSPR._allowInlineStyles = false;
-    aCSPR._allowEval = false;
-  } else {
-    if ("script-src" in dirs) {
+    // Parse the source list (look ahead) so we can set the defaults properly,
+    // honoring the 'unsafe-inline' and 'unsafe-eval' keywords
+    var defaultSrcValue = CSPSourceList.fromString(dirs["default-src"], null, self);
+    if (!defaultSrcValue._allowUnsafeInline) {
       aCSPR._allowInlineScripts = false;
+      aCSPR._allowInlineStyles = false;
+    }
+    if (!defaultSrcValue._allowUnsafeEval) {
       aCSPR._allowEval = false;
     }
-    if ("style-src" in dirs) {
-      aCSPR._allowInlineStyles = false;
-    }
+  }
+  if ("script-src" in dirs) {
+    aCSPR._allowInlineScripts = false;
+    aCSPR._allowEval = false;
+  }
+  if ("style-src" in dirs) {
+    aCSPR._allowInlineStyles = false;
   }
 
   directive:
   for (var dirname in dirs) {
     var dirvalue = dirs[dirname];
 
     if (aCSPR._directives.hasOwnProperty(dirname)) {
       // Check for (most) duplicate directives
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -628,16 +628,19 @@ MOCHITEST_FILES_C= \
 		file_CSP_bug885433_allows.html \
 		file_CSP_bug885433_allows.html^headers^ \
 		file_CSP_bug885433_blocks.html \
 		file_CSP_bug885433_blocks.html^headers^ \
 		test_bug890580.html \
 		test_declare_stylesheet_obsolete.html \
 		variable_style_sheet.sjs \
 		test_processing_instruction_update_stylesheet.xhtml \
+		test_CSP_bug888172.html \
+		file_CSP_bug888172.html \
+		file_CSP_bug888172.sjs \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939)
 ifneq ($(OS_ARCH),WINNT)
 ifndef MOZ_ANDROID_OMTC
 MOCHITEST_FILES_B += \
 		test_messagemanager_assertpermission.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_bug888172.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+  <body>
+    <ol>
+      <li id="unsafe-inline-script">Inline script (green if allowed, black if blocked)</li>
+      <li id="unsafe-eval-script">Eval script (green if allowed, black if blocked)</li>
+      <li id="unsafe-inline-style">Inline style (green if allowed, black if blocked)</li>
+    </ol>
+
+    <script>
+      // Use inline script to set a style attribute
+    document.getElementById("unsafe-inline-script").style.color = "green";
+
+    // Use eval to set a style attribute
+    // try/catch is used because CSP causes eval to throw an exception when it
+    // is blocked, which would derail the rest of the tests  in this file.
+    try {
+      eval('document.getElementById("unsafe-eval-script").style.color = "green";');
+    } catch (e) {}
+    </script>
+
+    <style>
+      li#unsafe-inline-style {
+        color: green;
+      }
+    </style>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_bug888172.sjs
@@ -0,0 +1,43 @@
+// SJS file for CSP mochitests
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function loadHTMLFromFile(path) {
+  // Load the HTML to return in the response from file.
+  // Since it's relative to the cwd of the test runner, we start there and
+  // append to get to the actual path of the file.
+  var testHTMLFile =
+    Components.classes["@mozilla.org/file/directory_service;1"].
+    getService(Components.interfaces.nsIProperties).
+    get("CurWorkD", Components.interfaces.nsILocalFile);
+  var dirs = path.split("/");
+  for (var i = 0; i < dirs.length; i++) {
+    testHTMLFile.append(dirs[i]);
+  }
+  var testHTMLFileStream =
+    Components.classes["@mozilla.org/network/file-input-stream;1"].
+    createInstance(Components.interfaces.nsIFileInputStream);
+  testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+  var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
+  return testHTML;
+}
+
+function handleRequest(request, response)
+{
+  var query = {};
+  request.queryString.split('&').forEach(function (val) {
+    var [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+
+  // avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  // Deliver the CSP policy encoded in the URI
+  if (query['csp'])
+    response.setHeader("Content-Security-Policy", unescape(query['csp']), false);
+
+  // Send HTML to test allowed/blocked behaviors
+  response.setHeader("Content-Type", "text/html", false);
+  response.write(loadHTMLFromFile("tests/content/base/test/file_CSP_bug888172.html"));
+}
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_bug888172.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 888172 - CSP 1.0 does not process 'unsafe-inline' or 'unsafe-eval' for default-src</title>
+  <script type="text/javascript" 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:100%;" id='testframe1'></iframe>
+<iframe style="width:100%;" id='testframe2'></iframe>
+<iframe style="width:100%;" id='testframe3'></iframe>
+<script class="testbody" type="text/javascript">
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+// utilities for check functions
+// black means the style wasn't applied, applied styles are green
+var green = 'rgb(0, 128, 0)';
+var black = 'rgb(0, 0, 0)';
+
+function getElementColorById(doc, id) {
+  return window.getComputedStyle(doc.contentDocument.getElementById(id)).color;
+}
+
+// We test both script and style execution by observing changes in computed styles
+function checkDefaultSrcOnly() {
+  var testframe = document.getElementById('testframe1');
+
+  ok(getElementColorById(testframe, 'unsafe-inline-script') === green, "Inline script should be allowed");
+  ok(getElementColorById(testframe, 'unsafe-eval-script')  === green, "Eval should be allowed");
+  ok(getElementColorById(testframe, 'unsafe-inline-style') === green, "Inline style should be allowed");
+}
+
+function checkDefaultSrcWithScriptSrc() {
+  var testframe = document.getElementById('testframe2');
+
+  ok(getElementColorById(testframe, 'unsafe-inline-script') === black, "Inline script should be blocked");
+  ok(getElementColorById(testframe, 'unsafe-eval-script')  === black, "Eval should be blocked");
+  ok(getElementColorById(testframe, 'unsafe-inline-style') === green, "Inline style should be allowed");
+}
+
+function checkDefaultSrcWithStyleSrc() {
+  var testframe = document.getElementById('testframe3');
+
+  ok(getElementColorById(testframe, 'unsafe-inline-script') === green, "Inline script should be allowed");
+  ok(getElementColorById(testframe, 'unsafe-eval-script')  === green, "Eval should be allowed");
+  ok(getElementColorById(testframe, 'unsafe-inline-style') === black, "Inline style should be blocked");
+
+  // last test calls finish
+  SimpleTest.finish();
+}
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+  function () {
+    document.getElementById('testframe1').src = 'file_CSP_bug888172.sjs?csp=' +
+      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'");
+    document.getElementById('testframe1').addEventListener('load', checkDefaultSrcOnly, false);
+
+    document.getElementById('testframe2').src = 'file_CSP_bug888172.sjs?csp=' +
+      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'self'");
+    document.getElementById('testframe2').addEventListener('load', checkDefaultSrcWithScriptSrc, false);
+
+    document.getElementById('testframe3').src = 'file_CSP_bug888172.sjs?csp=' +
+      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self'");
+    document.getElementById('testframe3').addEventListener('load', checkDefaultSrcWithStyleSrc, false);
+  }
+);
+</script>
+</pre>
+</body>
+</html>