Bug 885433 - CSP should not block inline scripts or eval unless script-src or default-src are included (r=imelven)
authorGarrett Robinson <grobinson@mozilla.com>
Mon, 01 Jul 2013 17:46:21 -0700
changeset 137089 108624302b7d54710174d3130ec90c21d968d9bf
parent 137088 fb16679405423011995d59ad0f4064a9d87ec094
child 137090 4064376846225ce914692b102fe8a4a2367820c5
push id24909
push userkhuey@mozilla.com
push dateTue, 02 Jul 2013 16:45:38 +0000
treeherdermozilla-central@23ce4eab8fb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersimelven
bugs885433
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 885433 - CSP should not block inline scripts or eval unless script-src or default-src are included (r=imelven)
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>