Bug 885433 - CSP should not block inline scripts or eval unless script-src or default-src are included (r=imelven a=bajaj)
authorGarrett Robinson <grobinson@mozilla.com>
Wed, 03 Jul 2013 13:11:27 -0700
changeset 143449 589121b50d01dd13f1bda0e4f2837c30333c672f
parent 143448 bf18c9cdbac1b3c3b8b04870afd219ad9691204b
child 143450 bfc39992335fbdc804dde531efb9591c746a159b
push id3941
push userimelven@mozilla.com
push dateWed, 03 Jul 2013 20:25:44 +0000
treeherdermozilla-aurora@589121b50d01 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersimelven, bajaj
bugs885433
milestone24.0a2
Bug 885433 - CSP should not block inline scripts or eval unless script-src or default-src are included (r=imelven a=bajaj)
content/base/src/CSPUtils.jsm
content/base/test/Makefile.in
content/base/test/file_CSP_bug885433_allows.html
content/base/test/file_CSP_bug885433_allows.html^headers^
content/base/test/file_CSP_bug885433_blocks.html
content/base/test/file_CSP_bug885433_blocks.html^headers^
content/base/test/test_CSP_bug885433.html
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -512,25 +512,53 @@ CSPRep.fromStringSpecCompliant = functio
     // clean userpass out of the URI (not used for CSP origin checking, but
     // shows up in prePath).
     try {
       // GetUserPass throws for some protocols without userPass
       selfUri.userPass = '';
     } catch (ex) {}
   }
 
-  var dirs = aStr.split(";");
-
-  directive:
-  for each(var dir in dirs) {
+  var dirs_list = aStr.split(";");
+  var dirs = {};
+  for each(var dir in dirs_list) {
     dir = dir.trim();
     if (dir.length < 1) continue;
 
     var dirname = dir.split(/\s+/)[0];
     var dirvalue = dir.substring(dirname.length).trim();
+    dirs[dirname] = dirvalue;
+  }
+
+  // Spec compliant policies have different default behavior for inline
+  // scripts, styles, and eval. Bug 885433
+  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) {
+      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
       cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
                                                 [dirname]));
       CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
       continue directive;
     }
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -626,16 +626,21 @@ MOCHITEST_FILES_C= \
 		badMessageEvent2.eventsource \
 		badMessageEvent2.eventsource^headers^ \
 		test_object.html \
 		test_bug869006.html \
 		test_bug868999.html \
 		test_bug869000.html \
 		test_bug869002.html \
 		test_bug876282.html \
+		test_CSP_bug885433.html \
+		file_CSP_bug885433_allows.html \
+		file_CSP_bug885433_allows.html^headers^ \
+		file_CSP_bug885433_blocks.html \
+		file_CSP_bug885433_blocks.html^headers^ \
 		$(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_bug885433_allows.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<!--
+The Content-Security-Policy header for this file is:
+
+  Content-Security-Policy: img-src 'self';
+
+It does not include any of the default-src, script-src, or style-src
+directives. It should allow the use of unsafe-inline and unsafe-eval on
+scripts, and unsafe-inline on styles, because no directives related to scripts
+or styles are specified.
+-->
+<html>
+<body>
+  <ol>
+    <li id="unsafe-inline-script-allowed">Inline script allowed (this text should be green)</li>
+    <li id="unsafe-eval-script-allowed">Eval script allowed (this text should be green)</li>
+    <li id="unsafe-inline-style-allowed">Inline style allowed (this text should be green)</li>
+  </ol>
+
+  <script>
+    // Use inline script to set a style attribute
+    document.getElementById("unsafe-inline-script-allowed").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-allowed").style.color = "green";');
+    } catch (e) {}
+  </script>
+
+  <style>
+    li#unsafe-inline-style-allowed {
+      color: green;
+    }
+  </style>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_bug885433_allows.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: img-src 'self';
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_bug885433_blocks.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<!--
+The Content-Security-Policy header for this file is:
+
+  Content-Security-Policy: default-src 'self';
+
+The Content-Security-Policy header for this file includes the default-src
+directive, which triggers the default behavior of blocking unsafe-inline and
+unsafe-eval on scripts, and unsafe-inline on styles.
+-->
+<html>
+<body>
+  <ol>
+    <li id="unsafe-inline-script-blocked">Inline script blocked (this text should be black)</li>
+    <li id="unsafe-eval-script-blocked">Eval script blocked (this text should be black)</li>
+    <li id="unsafe-inline-style-blocked">Inline style blocked (this text should be black)</li>
+  </ol>
+
+  <script>
+    // Use inline script to set a style attribute
+    document.getElementById("unsafe-inline-script-blocked").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-blocked").style.color = "green";');
+    } catch (e) {}
+  </script>
+
+  <style>
+    li#unsafe-inline-style-blocked {
+      color: green;
+    }
+  </style>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_bug885433_blocks.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self';
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_bug885433.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Content Security Policy inline stylesheets stuff</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='cspframe'></iframe>
+<iframe style="width:100%;" id='cspframe2'></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)';
+
+// We test both script and style execution by observing changes in computed styles
+function checkAllowed () {
+  var cspframe = document.getElementById('cspframe');
+  var color;
+
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-script-allowed')).color;
+  ok(color === green, "Inline script should be allowed");
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-eval-script-allowed')).color;
+  ok(color === green, "Eval should be allowed");
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-style-allowed')).color;
+  ok(color === green, "Inline style should be allowed");
+}
+
+function checkBlocked () {
+  var cspframe = document.getElementById('cspframe2');
+  var color;
+
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-script-blocked')).color;
+  ok(color === black, "Inline script should be blocked");
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-eval-script-blocked')).color;
+  ok(color === black, "Eval should be blocked");
+  color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-style-blocked')).color;
+  ok(color === black, "Inline style should be blocked");
+
+  SimpleTest.finish();
+}
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+  function () {
+    document.getElementById('cspframe').src = 'file_CSP_bug885433_allows.html';
+    document.getElementById('cspframe').addEventListener('load', checkAllowed, false);
+    document.getElementById('cspframe2').src = 'file_CSP_bug885433_blocks.html';
+    document.getElementById('cspframe2').addEventListener('load', checkBlocked, false);
+  }
+);
+</script>
+</pre>
+</body>
+</html>