Bug 587377 - Display CSP warning in the web console if a hostname is a quoteless CSP keyword match. r=sstamm
authorYeuk Hon Wong <yeukhon@acm.org>
Fri, 17 Jan 2014 11:10:29 -0500
changeset 163972 1f71357d7de13f2805261d5faba73d5bcd87452b
parent 163971 648a54eeed1924f0646c4bab288f18a7928ce97f
child 163973 0496d73bf88ec60976a9688293289fc3d7dbcf01
child 164007 316466d0b962101be7df72fdaee8133dc5f36c4d
push id38601
push userryanvm@gmail.com
push dateFri, 17 Jan 2014 16:10:57 +0000
treeherdermozilla-inbound@0496d73bf88e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstamm
bugs587377
milestone29.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 587377 - Display CSP warning in the web console if a hostname is a quoteless CSP keyword match. r=sstamm
content/base/src/CSPUtils.jsm
content/base/test/csp/file_self_none_as_hostname_confusion.html
content/base/test/csp/file_self_none_as_hostname_confusion.html^headers^
content/base/test/csp/mochitest.ini
content/base/test/csp/test_self_none_as_hostname_confusion.html
dom/locales/en-US/chrome/security/csp.properties
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -81,16 +81,18 @@ const R_HASHSRC    = new RegExp ("^'" + 
 
 // source-exp      = scheme-source / host-source / keyword-source
 const R_SOURCEEXP  = new RegExp (R_SCHEMESRC.source + "|" +
                                    R_HOSTSRC.source + "|" +
                                 R_KEYWORDSRC.source + "|" +
                                   R_NONCESRC.source + "|" +
                                    R_HASHSRC.source,  'i');
 
+const R_QUOTELESS_KEYWORDS = new RegExp ("^(self|unsafe-inline|unsafe-eval|" +
+                                         "inline-script|eval-script|none)$", 'i');
 
 this.CSPPrefObserver = {
   get debugEnabled () {
     if (!this._branch)
       this._initialize();
     return this._debugEnabled;
   },
 
@@ -1393,16 +1395,22 @@ CSPSource.fromString = function(aStr, aC
         return null;
       }
       sObj._port = defPort;
     }
     else {
       // strip the ':' from the port
       sObj._port = portMatch[0].substr(1);
     }
+    // A CSP keyword without quotes is a valid hostname, but this can also be a mistake.
+    // Raise a CSP warning in the web console to developer to check his/her intent.
+    if (R_QUOTELESS_KEYWORDS.test(aStr)) {
+      cspWarn(aCSPRep, CSPLocalizer.getFormatStr("hostNameMightBeKeyword",
+                                                 [aStr, aStr.toLowerCase()]));
+    }
     return sObj;
   }
 
   // check for a nonce-source match
   if (R_NONCESRC.test(aStr))
     return CSPNonceSource.fromString(aStr, aCSPRep);
 
   // check for a hash-source match
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_self_none_as_hostname_confusion.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf8">
+    <title>Bug 587377 - CSP keywords "'self'" and "'none'" are easy to confuse with host names "self" and "none"</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+       - http://creativecommons.org/publicdomain/zero/1.0/ -->
+  </head>
+  <body>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_self_none_as_hostname_confusion.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self' SELF;
--- a/content/base/test/csp/mochitest.ini
+++ b/content/base/test/csp/mochitest.ini
@@ -101,16 +101,18 @@ support-files =
   file_nonce_source.html^headers^
   file_CSP_bug941404.html
   file_CSP_bug941404_xhr.html
   file_CSP_bug941404_xhr.html^headers^
   file_hash_source.html
   file_hash_source.html^headers^
   file_dual_headers_warning.html
   file_dual_headers_warning.html^headers^
+  file_self_none_as_hostname_confusion.html
+  file_self_none_as_hostname_confusion.html^headers^
 
 [test_CSP.html]
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
 [test_CSP_bug885433.html]
 [test_CSP_bug888172.html]
 [test_CSP_bug916446.html]
 [test_CSP_evalscript.html]
@@ -124,8 +126,9 @@ support-files =
 [test_csp_redirects.html]
 [test_CSP_bug910139.html]
 [test_CSP_bug909029.html]
 [test_policyuri_regression_from_multipolicy.html]
 [test_nonce_source.html]
 [test_CSP_bug941404.html]
 [test_hash_source.html]
 [test_dual_headers_warning.html]
+[test_self_none_as_hostname_confusion.html]
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/test_self_none_as_hostname_confusion.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=587377
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 587377</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=587377">Mozilla Bug 587377</a>
+<p id="display"></p>
+
+<iframe id="cspframe"></iframe>
+
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+// Load locale string during mochitest
+var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"]
+                          .getService(SpecialPowers.Ci.nsIStringBundleService);
+var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties");
+var confusionMsg = localizer.formatStringFromName("hostNameMightBeKeyword", ["SELF", "self"], 2);
+
+function cleanup() {
+  SpecialPowers.postConsoleSentinel();
+  SimpleTest.finish();
+};
+
+// To prevent the test from asserting twice and calling SimpleTest.finish() twice,
+// startTest will be marked false as soon as the confusionMsg is detected.
+startTest = false;
+SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
+  if (startTest) {
+    if (aMsg.message.indexOf(confusionMsg) > -1) {
+      startTest = false;
+      ok(true, "CSP header with a hostname similar to keyword should be warned");
+      SimpleTest.executeSoon(cleanup);
+    } else {
+      // don't see the warning yet? wait.
+      return;
+    }
+  }
+});
+
+// set up and start testing
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {'set': [["security.csp.speccompliant", true]]},
+  function() {
+    document.getElementById('cspframe').src = 'file_self_none_as_hostname_confusion.html';
+    startTest = true;
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -51,16 +51,19 @@ allowDirectiveIsDeprecated = allow direc
 # inline script refers to JavaScript code that is embedded into the HTML document.
 inlineScriptBlocked = An attempt to execute inline scripts has been blocked
 # LOCALIZATION NOTE (inlineStyleBlocked):
 # inline style refers to CSS code that is embedded into the HTML document.
 inlineStyleBlocked = An attempt to apply inline style sheets has been blocked
 # LOCALIZATION NOTE (scriptFromStringBlocked):
 # eval is a name and should not be localized.
 scriptFromStringBlocked = An attempt to call JavaScript from a string (by calling a function like eval) has been blocked
+# LOCALIZATION NOTE (hostNameMightBeKeyword):
+# %1$S is the hostname in question and %2$S is the keyword
+hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
 
 # CSP Errors:
 policyURINotAlone = policy-uri directive can only appear alone
 noParentRequest = The policy-uri cannot be fetched without a parent request and a CSP.
 # LOCALIZATION NOTE (policyURIParseError):
 # %1$S is the URI that could not be parsed
 policyURIParseError = could not parse URI in policy URI: %1$S
 # LOCALIZATION NOTE (nonMatchingHost):