Bug 746978 - sync CSP directive parsing and directive names with w3c spec - Part 1 (r=sstamm)
authorIan Melven <imelven@mozilla.com>
Wed, 09 Jan 2013 10:57:04 -0800
changeset 118278 a16f8c77ab42dc4577f16f5f004d193587e23679
parent 118277 cb6907c8de986d5518266f2063b4d4db087903f6
child 118279 e6a5547ae073e7962705bcd1fcac1587fbca184d
push id24159
push useremorley@mozilla.com
push dateThu, 10 Jan 2013 08:59:47 +0000
treeherdermozilla-central@a2c46e2c7df1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstamm
bugs746978
milestone21.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 746978 - sync CSP directive parsing and directive names with w3c spec - Part 1 (r=sstamm)
content/base/src/CSPUtils.jsm
content/base/src/contentSecurityPolicy.js
content/base/test/test_CSP.html
content/base/test/unit/test_csputils.js
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -138,63 +138,92 @@ CSPPolicyURIListener.prototype = {
     this._policy += this._wrapper.read(count);
   },
 
   onStopRequest:
   function(request, context, status) {
     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);
+      this._csp.refinePolicy(this._policy, this._docURI,
+                             this._csp._specCompliant);
     }
     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);
+      this._csp.refinePolicy("default-src 'none'", this._docURI,
+                             this._csp._specCompliant);
     }
     // resume the parent document request
     this._docRequest.resume();
   }
 };
 
 //:::::::::::::::::::::::: CLASSES :::::::::::::::::::::::::://
 
 /**
  * Class that represents a parsed policy structure.
+ *
+ * @param aSpecCompliant: true: this policy is a CSP 1.0 spec
+ *                   compliant policy and should be parsed as such.
+ *                   false or undefined: this is a policy using
+ *                   our original implementation's CSP syntax.
  */
-this.CSPRep = function CSPRep() {
+this.CSPRep = function CSPRep(aSpecCompliant) {
   // this gets set to true when the policy is done parsing, or when a
   // URI-borne policy has finished loading.
   this._isInitialized = false;
 
   this._allowEval = false;
   this._allowInlineScripts = false;
 
   // don't auto-populate _directives, so it is easier to find bugs
   this._directives = {};
+
+  // Is this a 1.0 spec compliant CSPRep ?
+  // Default to false if not specified.
+  this._specCompliant = (aSpecCompliant !== undefined) ? aSpecCompliant : false;
 }
 
-CSPRep.SRC_DIRECTIVES = {
+// Source directives for our original CSP implementation.
+// These can be removed when the original implementation is deprecated.
+CSPRep.SRC_DIRECTIVES_OLD = {
   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",
   XHR_SRC:          "xhr-src"
 };
 
+// Source directives for our CSP 1.0 spec compliant implementation.
+CSPRep.SRC_DIRECTIVES_NEW = {
+  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",
+  CONNECT_SRC:      "connect-src"
+};
+
 CSPRep.URI_DIRECTIVES = {
   REPORT_URI:       "report-uri", /* list of URIs */
   POLICY_URI:       "policy-uri"  /* single URI */
 };
 
+// These directives no longer exist in CSP 1.0 and
+// later and will eventually be removed when we no longer
+// support our original implementation's syntax.
 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
@@ -204,17 +233,17 @@ CSPRep.ALLOW_DIRECTIVE   = "allow";
   *        request for the parent document which may need to be suspended
   *        while the policy-uri is asynchronously fetched
   * @param csp (optional)
   *        the CSP object to update once the policy has been fetched
   * @returns
   *        an instance of CSPRep
   */
 CSPRep.fromString = function(aStr, self, docRequest, csp) {
-  var SD = CSPRep.SRC_DIRECTIVES;
+  var SD = CSPRep.SRC_DIRECTIVES_OLD;
   var UD = CSPRep.URI_DIRECTIVES;
   var aCSPR = new CSPRep();
   aCSPR._originalText = aStr;
   aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
 
   var selfUri = null;
   if (self instanceof Ci.nsIURI)
     selfUri = self.clone();
@@ -424,16 +453,17 @@ CSPRep.fromString = function(aStr, self,
     }
 
     // UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
     cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective",
                                              [dirname]));
 
   } // end directive: loop
 
+  // TODO : clean this up using patch in bug 780978
   // 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("default-src 'none'", self);
 };
 
 /**
@@ -447,18 +477,197 @@ CSPRep.fromString = function(aStr, self,
   * @param docRequest (optional)
   *        request for the parent document which may need to be suspended
   *        while the policy-uri is asynchronously fetched
   * @param csp (optional)
   *        the CSP object to update once the policy has been fetched
   * @returns
   *        an instance of CSPRep
   */
+// When we deprecate our original CSP implementation, we rename this to
+// CSPRep.fromString and remove the existing CSPRep.fromString above.
 CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp) {
-  // bug #746878 goes here
+  var SD = CSPRep.SRC_DIRECTIVES_NEW;
+  var UD = CSPRep.URI_DIRECTIVES;
+  var aCSPR = new CSPRep(true);
+  aCSPR._originalText = aStr;
+  aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
+
+  var selfUri = null;
+  if (self instanceof Ci.nsIURI)
+    selfUri = self.clone();
+
+  var dirs = aStr.split(";");
+
+  directive:
+  for each(var dir in dirs) {
+    dir = dir.trim();
+    if (dir.length < 1) continue;
+
+    var dirname = dir.split(/\s+/)[0];
+    var dirvalue = dir.substring(dirname.length).trim();
+
+    if (aCSPR._directives.hasOwnProperty(dirname)) {
+      // Check for (most) duplicate directives
+      cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
+                                                [dirname]));
+      CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
+      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, aCSPR, self, true);
+        if (dv) {
+          aCSPR._directives[sdi] = dv;
+          continue directive;
+        }
+      }
+    }
+
+    // REPORT URI ///////////////////////////////////////////////////////
+    if (dirname === UD.REPORT_URI) {
+      // might be space-separated list of URIs
+      var uriStrings = dirvalue.split(/\s+/);
+      var okUriStrings = [];
+
+      for (let i in uriStrings) {
+        var uri = null;
+        try {
+          // Relative URIs are okay, but to ensure we send the reports to the
+          // right spot, the relative URIs are expanded here during parsing.
+          // The resulting CSPRep instance will have only absolute URIs.
+          uri = gIoService.newURI(uriStrings[i],null,selfUri);
+
+          // if there's no host, don't do the ETLD+ check.  This will throw
+          // NS_ERROR_FAILURE if the URI doesn't have a host, causing a parse
+          // failure.
+          uri.host;
+
+          // Verify that each report URI is in the same etld + 1 and that the
+          // scheme and port match "self" if "self" is defined, and just that
+          // it's valid otherwise.
+          if (self) {
+            if (gETLDService.getBaseDomain(uri) !==
+                gETLDService.getBaseDomain(selfUri)) {
+              cspWarn(aCSPR, 
+                      CSPLocalizer.getFormatStr("notETLDPlus1",
+                                            [gETLDService.getBaseDomain(uri)]));
+              continue;
+            }
+            if (!uri.schemeIs(selfUri.scheme)) {
+              cspWarn(aCSPR, 
+                      CSPLocalizer.getFormatStr("notSameScheme",
+                                                [uri.asciiSpec]));
+              continue;
+            }
+            if (uri.port && uri.port !== selfUri.port) {
+              cspWarn(aCSPR, 
+                      CSPLocalizer.getFormatStr("notSamePort",
+                                                [uri.asciiSpec]));
+              continue;
+            }
+          }
+        } catch(e) {
+          switch (e.result) {
+            case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
+            case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
+              if (uri.host !== selfUri.host) {
+                cspWarn(aCSPR, CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
+                                                         [selfUri.host, uri.host]));
+                continue;
+              }
+              break;
+
+            default:
+              cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotParseReportURI", 
+                                                       [uriStrings[i]]));
+              continue;
+          }
+        }
+        // all verification passed: same ETLD+1, scheme, and port.
+       okUriStrings.push(uri.asciiSpec);
+      }
+      aCSPR._directives[UD.REPORT_URI] = okUriStrings.join(' ');
+      continue directive;
+    }
+
+    // POLICY URI //////////////////////////////////////////////////////////
+    if (dirname === UD.POLICY_URI) {
+      // POLICY_URI can only be alone
+      if (aCSPR._directives.length > 0 || dirs.length > 1) {
+        cspError(aCSPR, CSPLocalizer.getStr("policyURINotAlone"));
+        return CSPRep.fromStringSpecCompliant("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(aCSPR, CSPLocalizer.getStr("noParentRequest"));
+        return CSPRep.fromStringSpecCompliant("default-src 'none'");
+      }
+
+      var uri = '';
+      try {
+        uri = gIoService.newURI(dirvalue, null, selfUri);
+      } catch(e) {
+        cspError(aCSPR, CSPLocalizer.getFormatStr("policyURIParseError", [dirvalue]));
+        return CSPRep.fromStringSpecCompliant("default-src 'none'");
+      }
+
+      // Verify that policy URI comes from the same origin
+      if (selfUri) {
+        if (selfUri.host !== uri.host){
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingHost", [uri.host]));
+          return CSPRep.fromStringSpecCompliant("default-src 'none'");
+        }
+        if (selfUri.port !== uri.port){
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingPort", [uri.port.toString()]));
+          return CSPRep.fromStringSpecCompliant("default-src 'none'");
+        }
+        if (selfUri.scheme !== uri.scheme){
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingScheme", [uri.scheme]));
+          return CSPRep.fromStringSpecCompliant("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.loadGroup = docRequest.loadGroup;
+        chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
+      }
+      catch (e) {
+        // resume the document request and apply most restrictive policy
+        docRequest.resume();
+        cspError(aCSPR, CSPLocalizer.getFormatStr("errorFetchingPolicy", [e.toString()]));
+        return CSPRep.fromStringSpecCompliant("default-src 'none'");
+      }
+
+      // return a fully-open policy to be intersected with the contents of the
+      // policy-uri when it returns
+      return CSPRep.fromStringSpecCompliant("default-src *");
+    }
+
+    // UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
+    cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective", [dirname]));
+
+  } // end directive: loop
+
+  // TODO : clean this up using patch in bug 780978
+  // 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.fromStringSpecCompliant("default-src 'none'", self);
 };
 
 CSPRep.prototype = {
   /**
    * Returns a space-separated list of all report uris defined, or 'none' if there are none.
    */
   getReportURIs:
   function() {
@@ -516,38 +725,44 @@ CSPRep.prototype = {
 
     // GLOBALLY ALLOW "about:" SCHEME
     if (aURI instanceof String && aURI.substring(0,6) === "about:")
       return true;
     if (aURI instanceof Ci.nsIURI && aURI.scheme === "about")
       return true;
 
     // make sure the context is valid
-    for (var i in CSPRep.SRC_DIRECTIVES) {
-      if (CSPRep.SRC_DIRECTIVES[i] === aContext) {
+    let DIRS = this._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW : CSPRep.SRC_DIRECTIVES_OLD;
+
+    for (var i in DIRS) {
+      if (DIRS[i] === aContext) {
         return this._directives[aContext].permits(aURI);
       }
     }
+
     return false;
   },
 
   /**
    * Intersects with another CSPRep, deciding the subset policy
    * that should be enforced, and returning a new instance.
    * @param aCSPRep
    *        a CSPRep instance to use as "other" CSP
    * @returns
    *        a new CSPRep instance of the intersection
    */
   intersectWith:
   function cspsd_intersectWith(aCSPRep) {
     var newRep = new CSPRep();
 
-    for (var dir in CSPRep.SRC_DIRECTIVES) {
-      var dirv = CSPRep.SRC_DIRECTIVES[dir];
+    let DIRS = aCSPRep._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW :
+                                        CSPRep.SRC_DIRECTIVES_OLD;
+
+    for (var dir in DIRS) {
+      var dirv = DIRS[dir];
       if (this._directives.hasOwnProperty(dirv))
         newRep._directives[dirv] = this._directives[dirv].intersectWith(aCSPRep._directives[dirv]);
       else
         newRep._directives[dirv] = aCSPRep._directives[dirv];
     }
 
     // REPORT_URI
     var reportURIDir = CSPRep.URI_DIRECTIVES.REPORT_URI;
@@ -564,34 +779,40 @@ CSPRep.prototype = {
       newRep._directives[reportURIDir] = aCSPRep._directives[reportURIDir].concat();
     }
 
     newRep._allowEval =          this.allowsEvalInScripts
                            && aCSPRep.allowsEvalInScripts;
 
     newRep._allowInlineScripts = this.allowsInlineScripts
                            && aCSPRep.allowsInlineScripts;
- 
+
     newRep._innerWindowID = this._innerWindowID ?
                               this._innerWindowID : aCSPRep._innerWindowID;
 
     return newRep;
   },
 
   /**
    * 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;
+    let SD = this._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW : CSPRep.SRC_DIRECTIVES_OLD;
+
     var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
-    if (!defaultSrcDir) {
+
+    // It's ok for a 1.0 spec compliant policy to not have a default source,
+    // in this case it should use default-src *
+    // However, our original CSP implementation required a default src
+    // or an allow directive.
+    if (!defaultSrcDir && !this._specCompliant) {
       this.warn(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
       return false;
     }
 
     for (var dir in SD) {
       var dirv = SD[dir];
       if (dirv === SD.DEFAULT_SRC) continue;
       if (!this._directives[dirv]) {
@@ -599,16 +820,17 @@ CSPRep.prototype = {
         // All but frame-ancestors directive inherit from 'allow' (bug 555068)
         if (dirv === SD.FRAME_ANCESTORS)
           this._directives[dirv] = CSPSourceList.fromString("*",this);
         else
           this._directives[dirv] = defaultSrcDir.clone();
         this._directives[dirv]._isImplicit = true;
       }
     }
+
     this._isInitialized = true;
     return true;
   },
 
   /**
    * Returns true if "eval" is enabled through the "eval" keyword.
    */
   get allowsEvalInScripts () {
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -15,26 +15,32 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 const CSP_VIOLATION_TOPIC = "csp-on-violate-policy";
 
+// Needed to support CSP 1.0 spec and our original CSP implementation - should
+// be removed when our original implementation is deprecated.
+const CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT = "csp_type_xmlhttprequest_spec_compliant";
+const CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT = "csp_type_websocket_spec_compliant";
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 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("default-src *");
 
   // default options "wide open" since this policy will be intersected soon
   this._policy._allowInlineScripts = true;
   this._policy._allowEval = true;
 
   this._request = "";
   this._requestOrigin = "";
@@ -47,48 +53,68 @@ function ContentSecurityPolicy() {
 }
 
 /*
  * Set up mappings from nsIContentPolicy content types to CSP directives.
  */
 {
   let cp = Ci.nsIContentPolicy;
   let csp = ContentSecurityPolicy;
-  let cspr_sd = CSPRep.SRC_DIRECTIVES;
+  let cspr_sd_old = CSPRep.SRC_DIRECTIVES_OLD;
+  let cspr_sd_new = CSPRep.SRC_DIRECTIVES_NEW;
 
   csp._MAPPINGS=[];
 
   /* default, catch-all case */
-  csp._MAPPINGS[cp.TYPE_OTHER]             =  cspr_sd.DEFAULT_SRC;
+  // This is the same in old and new CSP so use the new mapping.
+  csp._MAPPINGS[cp.TYPE_OTHER]             =  cspr_sd_new.DEFAULT_SRC;
 
   /* self */
   csp._MAPPINGS[cp.TYPE_DOCUMENT]          =  null;
 
   /* shouldn't see this one */
   csp._MAPPINGS[cp.TYPE_REFRESH]           =  null;
 
   /* categorized content types */
-  csp._MAPPINGS[cp.TYPE_SCRIPT]            = cspr_sd.SCRIPT_SRC;
-  csp._MAPPINGS[cp.TYPE_IMAGE]             = cspr_sd.IMG_SRC;
-  csp._MAPPINGS[cp.TYPE_STYLESHEET]        = cspr_sd.STYLE_SRC;
-  csp._MAPPINGS[cp.TYPE_OBJECT]            = cspr_sd.OBJECT_SRC;
-  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;
-  csp._MAPPINGS[cp.TYPE_WEBSOCKET]         = cspr_sd.XHR_SRC;
+  // These are the same in old and new CSP's so just use the new mappings.
+  csp._MAPPINGS[cp.TYPE_SCRIPT]            = cspr_sd_new.SCRIPT_SRC;
+  csp._MAPPINGS[cp.TYPE_IMAGE]             = cspr_sd_new.IMG_SRC;
+  csp._MAPPINGS[cp.TYPE_STYLESHEET]        = cspr_sd_new.STYLE_SRC;
+  csp._MAPPINGS[cp.TYPE_OBJECT]            = cspr_sd_new.OBJECT_SRC;
+  csp._MAPPINGS[cp.TYPE_OBJECT_SUBREQUEST] = cspr_sd_new.OBJECT_SRC;
+  csp._MAPPINGS[cp.TYPE_SUBDOCUMENT]       = cspr_sd_new.FRAME_SRC;
+  csp._MAPPINGS[cp.TYPE_MEDIA]             = cspr_sd_new.MEDIA_SRC;
+  csp._MAPPINGS[cp.TYPE_FONT]              = cspr_sd_new.FONT_SRC;
+
+  /* Our original CSP implementation's mappings for XHR and websocket
+   * These should be changed to be = cspr_sd.CONNECT_SRC when we remove
+   * the original implementation - NOTE: order in this array is important !!!
+   */
+  csp._MAPPINGS[cp.TYPE_XMLHTTPREQUEST]    = cspr_sd_old.XHR_SRC;
+  csp._MAPPINGS[cp.TYPE_WEBSOCKET]         = cspr_sd_old.XHR_SRC;
 
   /* CSP cannot block CSP reports */
   csp._MAPPINGS[cp.TYPE_CSP_REPORT]        = null;
 
   /* These must go through the catch-all */
-  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;
+  csp._MAPPINGS[cp.TYPE_XBL]               = cspr_sd_new.DEFAULT_SRC;
+  csp._MAPPINGS[cp.TYPE_PING]              = cspr_sd_new.DEFAULT_SRC;
+  csp._MAPPINGS[cp.TYPE_DTD]               = cspr_sd_new.DEFAULT_SRC;
+
+  /* CSP 1.0 spec compliant mappings for XHR and websocket */
+  // The directive name for XHR, websocket, and EventSource is different
+  // in the 1.0 spec than in our original implementation, these mappings
+  // address this. These won't be needed when we deprecate our original
+  // implementation.
+  csp._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT]    = cspr_sd_new.CONNECT_SRC;
+  csp._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT]         = cspr_sd_new.CONNECT_SRC;
+  // TODO : EventSource will be here and also will use connect-src
+  // after we fix Bug 802872 - CSP should restrict EventSource using the connect-src
+  // directive. For background see Bug 667490 - EventSource should use the same
+  // nsIContentPolicy type as XHR (which is fixed)
 }
 
 ContentSecurityPolicy.prototype = {
   classID:          Components.ID("{d1680bb4-1ac0-4772-9437-1188375e44f2}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentSecurityPolicy]),
 
   get isInitialized() {
     return this._isInitialized;
@@ -233,16 +259,19 @@ ContentSecurityPolicy.prototype = {
                                     this);
     }
 
     // (2) Intersect the currently installed CSPRep object with the new one
     var intersect = this._policy.intersectWith(newpolicy);
 
     // (3) Save the result
     this._policy = intersect;
+
+    this._policy._specCompliant = !!aSpecCompliant;
+
     this._isInitialized = true;
     this._cache = {};
   },
 
   /**
    * Generates and sends a violation report to the specified report URIs.
    */
   sendReports:
@@ -405,17 +434,19 @@ ContentSecurityPolicy.prototype = {
         }
         let ancestor = it.currentURI;
         CSPdebug(" found frame ancestor " + ancestor.asciiSpec);
         ancestors.push(ancestor);
       }
     }
 
     // scan the discovered ancestors
-    let cspContext = CSPRep.SRC_DIRECTIVES.FRAME_ANCESTORS;
+    // frame-ancestors is the same in both old and new source directives,
+    // so don't need to differentiate here.
+    let cspContext = CSPRep.SRC_DIRECTIVES_NEW.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
                                 ? 'default-src' : 'frame-ancestors ')
                                 + directive.toString();
@@ -449,16 +480,37 @@ ContentSecurityPolicy.prototype = {
 #ifndef MOZ_B2G
     // Try to remove as much as possible from the hot path on b2g.
     CSPdebug("shouldLoad location = " + aContentLocation.asciiSpec);
     CSPdebug("shouldLoad content type = " + aContentType);
 #endif
     // interpret the context, and then pass off to the decision structure
     var cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
 
+    // The mapping for XHR and websockets is different between our original
+    // implementation and the 1.0 spec, we handle this here.
+    var cspContext;
+
+    let cp = Ci.nsIContentPolicy;
+
+#ifndef MOZ_B2G
+    CSPdebug("policy is " + (this._policy._specCompliant ?
+                             "1.0 compliant" : "pre-1.0"));
+#endif
+
+    if (aContentType == cp.TYPE_XMLHTTPREQUEST && this._policy._specCompliant) {
+      cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT];
+    } else if (aContentType == cp.TYPE_WEBSOCKET && this._policy._specCompliant) {
+      cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT];
+    } else {
+      cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
+    }
+
+    CSPdebug("shouldLoad cspContext = " + cspContext);
+
     // if the mapping is null, there's no policy, let it through.
     if (!cspContext) {
       return Ci.nsIContentPolicy.ACCEPT;
     }
 
     // otherwise, honor the translation
     // var source = aContentLocation.scheme + "://" + aContentLocation.hostPort;
     var res = this._policy.permits(aContentLocation, cspContext)
--- a/content/base/test/test_CSP.html
+++ b/content/base/test/test_CSP.html
@@ -111,17 +111,20 @@ window.testResult = function(testname, r
 
   // ... otherwise, finish
   window.examiner.remove();
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 
-// save this for last so that our listeners are registered.
-// ... this loads the testbed of good and bad requests.
-
-document.getElementById('cspframe').src = 'file_CSP_main.html';
-
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+    function() {
+      // save this for last so that our listeners are registered.
+      // ... this loads the testbed of good and bad requests.
+      document.getElementById('cspframe').src = 'file_CSP_main.html';
+      document.getElementById('cspframe2').src = 'file_CSP_main_spec_compliant.html';
+    });
 </script>
 </pre>
 </body>
 </html>
--- a/content/base/test/unit/test_csputils.js
+++ b/content/base/test/unit/test_csputils.js
@@ -346,17 +346,17 @@ test(
 test(
     function test_CSPRep_fromString() {
 
       // check default init
       //ASSERT(!(new CSPRep())._isInitialized, "Uninitialized rep thinks it is.")
 
       var cspr;
       var cspr_allowval;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
 
       // check default policy "allow *"
       cspr = CSPRep.fromString("allow *", URI("http://self.com:80"));
       // "DEFAULT_SRC directive is missing when specified in fromString"
       do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
 
       // ... and check that the other directives were auto-filled with the
       // DEFAULT_SRC one.
@@ -368,17 +368,17 @@ test(
         do_check_eq(cspr._directives[SD[d]].toString(), cspr_allowval.toString());
       }
     });
 
 
 test(
     function test_CSPRep_defaultSrc() {
       var cspr, cspr_default_val, cspr_allow;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
 
       // apply policy of "default-src *" (e.g. "allow *")
       cspr = CSPRep.fromString("default-src *", URI("http://self.com:80"));
       // "DEFAULT_SRC directive is missing when specified in fromString"
       do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
 
       // check that the other directives were auto-filled with the
       // DEFAULT_SRC one.
@@ -400,17 +400,17 @@ test(
       }
     });
 
 
 test(
     function test_CSPRep_fromString_oneDir() {
 
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.IMG_SRC, SD.FRAME_SRC];
 
       // check one-directive policies
       cspr = CSPRep.fromString("allow bar.com; script-src https://foo.com",
                                URI("http://self.com"));
 
       for(var x in DEFAULTS) {
         //DEFAULTS[x] + " does not use default rule."
@@ -426,17 +426,17 @@ test(
       do_check_false(cspr.permits("http://bar.com:22", SD.SCRIPT_SRC));
       //"script-src false negative in policy.
       do_check_true(cspr.permits("https://foo.com:443", SD.SCRIPT_SRC));
     });
 
 test(
     function test_CSPRep_fromString_twodir() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.FRAME_SRC];
 
       // check two-directive policies
       var polstr = "allow allow.com; "
                   + "script-src https://foo.com; "
                   + "img-src bar.com:*";
       cspr = CSPRep.fromString(polstr, URI("http://self.com"));
 
@@ -460,17 +460,17 @@ test(
       //"script-src does not use default rule.
       do_check_true(cspr.permits("https://foo.com:443", SD.SCRIPT_SRC));
       //"script-src does not use default rule.
       do_check_false(cspr.permits("http://bar.com:400", SD.SCRIPT_SRC));
     });
 
 test(function test_CSPRep_fromString_withself() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var self = "https://self.com:34";
 
       // check one-directive policies
       cspr = CSPRep.fromString("allow 'self'; script-src 'self' https://*:*",
                               URI(self));
       //"img-src does not enforce default rule, 'self'.
       do_check_false(cspr.permits("https://foo.com:400", SD.IMG_SRC));
       //"img-src does not allow self
@@ -482,17 +482,17 @@ test(function test_CSPRep_fromString_wit
       //"script-src is too strict on host/port
       do_check_true(cspr.permits("https://evil.com:100", SD.SCRIPT_SRC));
      });
 
 //////////////// TEST FRAME ANCESTOR DEFAULTS /////////////////
 // (see bug 555068)
 test(function test_FrameAncestor_defaults() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var self = "http://self.com:34";
 
       cspr = CSPRep.fromString("allow 'none'", URI(self));
 
       //"frame-ancestors should default to * not 'allow' value"
       do_check_true(cspr.permits("https://foo.com:400", SD.FRAME_ANCESTORS));
       do_check_true(cspr.permits("http://self.com:34", SD.FRAME_ANCESTORS));
       do_check_true(cspr.permits("https://self.com:34", SD.FRAME_ANCESTORS));
@@ -506,17 +506,17 @@ test(function test_FrameAncestor_default
       do_check_false(cspr.permits("https://foo.com:400", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("https://self.com:34", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("http://self.com", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("http://subd.self.com:34", SD.FRAME_ANCESTORS));
      });
 
 test(function test_FrameAncestor_TLD_defaultPorts() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var self = "http://self"; //TLD only, no .com or anything.
 
       cspr = CSPRep.fromString("allow 'self'; frame-ancestors 'self' http://foo:80 bar:80 http://three", URI(self));
 
       //"frame-ancestors should default to * not 'allow' value"
       do_check_true(cspr.permits("http://self", SD.FRAME_ANCESTORS));
       do_check_true(cspr.permits("http://self:80", SD.FRAME_ANCESTORS));
       do_check_true(cspr.permits("http://foo", SD.FRAME_ANCESTORS));
@@ -528,17 +528,17 @@ test(function test_FrameAncestor_TLD_def
       do_check_false(cspr.permits("https://self:34", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("https://bar", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("http://three:81", SD.FRAME_ANCESTORS));
       do_check_false(cspr.permits("https://three:81", SD.FRAME_ANCESTORS));
      });
 
 test(function test_CSP_ReportURI_parsing() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var self = "http://self.com:34";
       var parsedURIs = [];
 
       var uri_valid_absolute = self + "/report.py";
       var uri_invalid_host_absolute = "http://foo.org:34/report.py";
       var uri_valid_relative = "/report.py";
       var uri_valid_relative_expanded = self + uri_valid_relative;
       var uri_valid_relative2 = "foo/bar/report.py";
@@ -588,17 +588,17 @@ test(function test_CSP_ReportURI_parsing
       do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
       do_check_in_array(parsedURIs, uri_valid_absolute);
       do_check_eq(parsedURIs.length, 2);
     });
 
 test(
      function test_bug634778_duplicateDirective_Detection() {
       var cspr;
-      var SD = CSPRep.SRC_DIRECTIVES;
+      var SD = CSPRep.SRC_DIRECTIVES_OLD;
       var self = "http://self.com:34";
       var firstDomain = "http://first.com";
       var secondDomain = "http://second.com";
       var thirdDomain = "http://third.com";
 
       // check for duplicate "default-src" directives
       cspr = CSPRep.fromString("default-src " + self + "; default-src " +
                               firstDomain, URI(self));