Bug 631040 - parse CSP default-src as equivalent to allow, r=jst,geekboy
authorBrandon Sterne <bsterne@mozilla.com>
Sun, 10 Apr 2011 11:21:02 -0700
changeset 67757 90140c158e789994c6615d7090dc7f7031fac504
parent 67756 8bf059c601c850671836689773ff951387e838de
child 67758 abd037fb8da41a3f652a69a0365a2c18b0f85d31
push id19426
push userbsterne@mozilla.com
push dateSun, 10 Apr 2011 18:20:40 +0000
treeherdermozilla-central@abd037fb8da4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, geekboy
bugs631040
milestone2.2a1pre
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 631040 - parse CSP default-src as equivalent to allow, r=jst,geekboy
content/base/src/CSPUtils.jsm
content/base/src/contentSecurityPolicy.js
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -158,16 +158,17 @@ CSPPolicyURIListener.prototype = {
     if (Components.isSuccessCode(status)) {
       // send the policy we received back to the parent document's CSP
       // for parsing
       this._csp.refinePolicy(this._policy, this._docURI, this._docRequest);
     }
     else {
       // problem fetching policy so fail closed
       this._csp.refinePolicy("allow 'none'", this._docURI, this._docRequest);
+      this._csp.refinePolicy("default-src 'none'", this._docURI, this._docRequest);
     }
     // resume the parent document request
     this._docRequest.resume();
   }
 };
 
 //:::::::::::::::::::::::: CLASSES ::::::::::::::::::::::::::// 
 
@@ -182,17 +183,17 @@ function CSPRep() {
   this._allowEval = false;
   this._allowInlineScripts = false;
 
   // don't auto-populate _directives, so it is easier to find bugs
   this._directives = {};
 }
 
 CSPRep.SRC_DIRECTIVES = {
-  ALLOW:            "allow",
+  DEFAULT_SRC:      "default-src",
   SCRIPT_SRC:       "script-src",
   STYLE_SRC:        "style-src",
   MEDIA_SRC:        "media-src",
   IMG_SRC:          "img-src",
   OBJECT_SRC:       "object-src",
   FRAME_SRC:        "frame-src",
   FRAME_ANCESTORS:  "frame-ancestors",
   FONT_SRC:         "font-src",
@@ -200,16 +201,17 @@ CSPRep.SRC_DIRECTIVES = {
 };
 
 CSPRep.URI_DIRECTIVES = {
   REPORT_URI:       "report-uri", /* list of URIs */
   POLICY_URI:       "policy-uri"  /* single URI */
 };
 
 CSPRep.OPTIONS_DIRECTIVE = "options";
+CSPRep.ALLOW_DIRECTIVE   = "allow";
 
 /**
   * Factory to create a new CSPRep, parsed from a string.
   *
   * @param aStr
   *        string rep of a CSP
   * @param self (optional)
   *        string or CSPSource representing the "self" source
@@ -253,16 +255,27 @@ CSPRep.fromString = function(aStr, self,
         else if (opt === "eval-script")
           aCSPR._allowEval = true;
         else
           CSPWarning("don't understand option '" + opt + "'.  Ignoring it.");
       }
       continue directive;
     }
 
+    // ALLOW DIRECTIVE //////////////////////////////////////////////////
+    // parse "allow" as equivalent to "default-src", at least until the spec
+    // stabilizes, at which time we can stop parsing "allow"
+    if (dirname === CSPRep.ALLOW_DIRECTIVE) {
+      var dv = CSPSourceList.fromString(dirvalue, self, true);
+      if (dv) {
+        aCSPR._directives[SD.DEFAULT_SRC] = dv;
+        continue directive;
+      }
+    }
+
     // SOURCE DIRECTIVES ////////////////////////////////////////////////
     for each(var sdi in SD) {
       if (dirname === sdi) {
         // process dirs, and enforce that 'self' is defined.
         var dv = CSPSourceList.fromString(dirvalue, self, true);
         if (dv) {
           aCSPR._directives[sdi] = dv;
           continue directive;
@@ -333,80 +346,80 @@ CSPRep.fromString = function(aStr, self,
       continue directive;
     }
 
     // POLICY URI //////////////////////////////////////////////////////////
     if (dirname === UD.POLICY_URI) {
       // POLICY_URI can only be alone
       if (aCSPR._directives.length > 0 || dirs.length > 1) {
         CSPError("policy-uri directive can only appear alone");
-        return CSPRep.fromString("allow 'none'");
+        return CSPRep.fromString("default-src 'none'");
       }
       // if we were called without a reference to the parent document request
       // we won't be able to suspend it while we fetch the policy -> fail closed
       if (!docRequest || !csp) {
         CSPError("The policy-uri cannot be fetched without a parent request and a CSP.");
-        return CSPRep.fromString("allow 'none'");
+        return CSPRep.fromString("default-src 'none'");
       }
 
       var uri = '';
       try {
         uri = gIoService.newURI(dirvalue, null, selfUri);
       } catch(e) {
         CSPError("could not parse URI in policy URI: " + dirvalue);
-        return CSPRep.fromString("allow 'none'");
+        return CSPRep.fromString("default-src 'none'");
       }
 
       // Verify that policy URI comes from the same origin
       if (selfUri) {
         if (selfUri.host !== uri.host){
           CSPError("can't fetch policy uri from non-matching hostname: " + uri.host);
-          return CSPRep.fromString("allow 'none'");
+          return CSPRep.fromString("default-src 'none'");
         }
         if (selfUri.port !== uri.port){
           CSPError("can't fetch policy uri from non-matching port: " + uri.port);
-          return CSPRep.fromString("allow 'none'");
+          return CSPRep.fromString("default-src 'none'");
         }
         if (selfUri.scheme !== uri.scheme){
           CSPError("can't fetch policy uri from non-matching scheme: " + uri.scheme);
-          return CSPRep.fromString("allow 'none'");
+          return CSPRep.fromString("default-src 'none'");
         }
       }
 
       // suspend the parent document request while we fetch the policy-uri
       try {
         docRequest.suspend();
         var chan = gIoService.newChannel(uri.asciiSpec, null, null);
         // make request anonymous (no cookies, etc.) so the request for the
         // policy-uri can't be abused for CSRF
         chan.loadFlags |= Components.interfaces.nsIChannel.LOAD_ANONYMOUS;
         chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
       }
       catch (e) {
         // resume the document request and apply most restrictive policy
         docRequest.resume();
         CSPError("Error fetching policy-uri: " + e);
-        return CSPRep.fromString("allow 'none'");
+        return CSPRep.fromString("default-src 'none'");
       }
 
       // return a fully-open policy to be intersected with the contents of the
       // policy-uri when it returns
-      return CSPRep.fromString("allow *");
+      return CSPRep.fromString("default-src *");
     }
 
     // UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
     CSPWarning("Couldn't process unknown directive '" + dirname + "'");
 
   } // end directive: loop
 
-  // if makeExplicit fails for any reason, default to allow 'none'.  This
-  // includes the case where "allow" is not present.
+  // if makeExplicit fails for any reason, default to default-src 'none'.  This
+  // includes the case where "default-src" is not present.
   if (aCSPR.makeExplicit())
     return aCSPR;
-  return CSPRep.fromString("allow 'none'", self);
+  return CSPRep.fromString("default-src 'none'", self);
 };
 
 CSPRep.prototype = {
   /**
    * Returns a space-separated list of all report uris defined, or 'none' if there are none.
    */
   getReportURIs:
   function() {
@@ -529,32 +542,32 @@ CSPRep.prototype = {
    * Copies default source list to each unspecified directive.
    * @returns
    *      true  if the makeExplicit succeeds
    *      false if it fails (for some weird reason)
    */
   makeExplicit:
   function cspsd_makeExplicit() {
     var SD = CSPRep.SRC_DIRECTIVES;
-    var allowDir = this._directives[SD.ALLOW];
-    if (!allowDir) {
-      CSPWarning("'allow' directive required but not present.  Reverting to \"allow 'none'\"");
+    var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
+    if (!defaultSrcDir) {
+      CSPWarning("'allow' or 'default-src' directive required but not present.  Reverting to \"default-src 'none'\"");
       return false;
     }
 
     for (var dir in SD) {
       var dirv = SD[dir];
-      if (dirv === SD.ALLOW) continue;
+      if (dirv === SD.DEFAULT_SRC) continue;
       if (!this._directives[dirv]) {
         // implicit directive, make explicit.
         // All but frame-ancestors directive inherit from 'allow' (bug 555068)
         if (dirv === SD.FRAME_ANCESTORS)
           this._directives[dirv] = CSPSourceList.fromString("*");
         else
-          this._directives[dirv] = allowDir.clone();
+          this._directives[dirv] = defaultSrcDir.clone();
         this._directives[dirv]._isImplicit = true;
       }
     }
     this._isInitialized = true;
     return true;
   },
 
   /**
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -57,40 +57,40 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/CSPUtils.jsm");
 
 /* ::::: Policy Parsing & Data structures :::::: */
 
 function ContentSecurityPolicy() {
   CSPdebug("CSP CREATED");
   this._isInitialized = false;
   this._reportOnlyMode = false;
-  this._policy = CSPRep.fromString("allow *");
+  this._policy = CSPRep.fromString("default-src *");
 
   // default options "wide open" since this policy will be intersected soon
   this._policy._allowInlineScripts = true;
   this._policy._allowEval = true;
 
   this._requestHeaders = []; 
   this._request = "";
   this._docRequest = null;
-  CSPdebug("CSP POLICY INITED TO 'allow *'");
+  CSPdebug("CSP POLICY INITED TO 'default-src *'");
 }
 
 /*
  * Set up mappings from nsIContentPolicy content types to CSP directives.
  */
 {
   let cp = Ci.nsIContentPolicy;
   let csp = ContentSecurityPolicy;
   let cspr_sd = CSPRep.SRC_DIRECTIVES;
 
   csp._MAPPINGS=[];
 
   /* default, catch-all case */
-  csp._MAPPINGS[cp.TYPE_OTHER]             =  cspr_sd.ALLOW;
+  csp._MAPPINGS[cp.TYPE_OTHER]             =  cspr_sd.DEFAULT_SRC;
 
   /* self */
   csp._MAPPINGS[cp.TYPE_DOCUMENT]          =  null;
 
   /* shouldn't see this one */
   csp._MAPPINGS[cp.TYPE_REFRESH]           =  null;
 
   /* categorized content types */
@@ -101,19 +101,19 @@ function ContentSecurityPolicy() {
   csp._MAPPINGS[cp.TYPE_OBJECT_SUBREQUEST] = cspr_sd.OBJECT_SRC;
   csp._MAPPINGS[cp.TYPE_SUBDOCUMENT]       = cspr_sd.FRAME_SRC;
   csp._MAPPINGS[cp.TYPE_MEDIA]             = cspr_sd.MEDIA_SRC;
   csp._MAPPINGS[cp.TYPE_FONT]              = cspr_sd.FONT_SRC;
   csp._MAPPINGS[cp.TYPE_XMLHTTPREQUEST]    = cspr_sd.XHR_SRC;
 
 
   /* These must go through the catch-all */
-  csp._MAPPINGS[cp.TYPE_XBL]               = cspr_sd.ALLOW;
-  csp._MAPPINGS[cp.TYPE_PING]              = cspr_sd.ALLOW;
-  csp._MAPPINGS[cp.TYPE_DTD]               = cspr_sd.ALLOW;
+  csp._MAPPINGS[cp.TYPE_XBL]               = cspr_sd.DEFAULT_SRC;
+  csp._MAPPINGS[cp.TYPE_PING]              = cspr_sd.DEFAULT_SRC;
+  csp._MAPPINGS[cp.TYPE_DTD]               = cspr_sd.DEFAULT_SRC;
 }
 
 ContentSecurityPolicy.prototype = {
   classID:          Components.ID("{AB36A2BF-CB32-4AA6-AB41-6B4E4444A221}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentSecurityPolicy]),
 
   get isInitialized() {
     return this._isInitialized;
@@ -381,17 +381,17 @@ ContentSecurityPolicy.prototype = {
     // scan the discovered ancestors
     let cspContext = CSPRep.SRC_DIRECTIVES.FRAME_ANCESTORS;
     for (let i in ancestors) {
       let ancestor = ancestors[i].prePath;
       if (!this._policy.permits(ancestor, cspContext)) {
         // report the frame-ancestor violation
         let directive = this._policy._directives[cspContext];
         let violatedPolicy = (directive._isImplicit
-                                ? 'allow' : 'frame-ancestors ')
+                                ? 'default-src' : 'frame-ancestors ')
                                 + directive.toString();
 
         this._asyncReportViolation(ancestors[i], violatedPolicy);
 
         // need to lie if we are testing in report-only mode
         return this._reportOnlyMode;
       }
     }
@@ -436,17 +436,17 @@ ContentSecurityPolicy.prototype = {
     // frame-ancestors is taken care of early on (as this document is loaded)
 
     // If the result is *NOT* ACCEPT, then send report
     if (res != Ci.nsIContentPolicy.ACCEPT) { 
       CSPdebug("blocking request for " + aContentLocation.asciiSpec);
       try {
         let directive = this._policy._directives[cspContext];
         let violatedPolicy = (directive._isImplicit
-                                ? 'allow' : cspContext)
+                                ? 'default-src' : cspContext)
                                 + ' ' + directive.toString();
         this._asyncReportViolation(aContentLocation, violatedPolicy);
       } catch(e) {
         CSPdebug('---------------- ERROR: ' + e);
       }
     }
 
     return (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);