From edab85ef3367d5e497c8c570696a131fcaddac16 Mon Sep 17 00:00:00 2001 From: Sid Stamm Date: Wed, 30 Jul 2014 14:37:22 -0700 Subject: [PATCH] Bug 994782 - Remove the old Content Security Policy parser, CSPUtils.jsm and tie-in logic. (r=ckerschb,jst) --- b2g/components/ProcessGlobal.js | 3 - b2g/installer/package-manifest.in | 2 - browser/installer/package-manifest.in | 2 - content/base/public/nsIContentPolicy.idl | 2 +- content/base/src/CSPUtils.jsm | 1737 ----------------- content/base/src/contentSecurityPolicy.js | 1010 ---------- .../base/src/contentSecurityPolicy.manifest | 2 - content/base/src/moz.build | 9 - content/base/src/nsCSPService.cpp | 7 +- content/base/src/nsCSPService.h | 1 - content/base/src/nsDocument.cpp | 12 +- .../base/test/unit/test_csp_ignores_path.js | 159 -- content/base/test/unit/test_csputils.js | 879 --------- content/base/test/unit/xpcshell.ini | 2 - dom/ipc/preload.js | 3 +- mobile/android/installer/package-manifest.in | 2 - modules/libpref/src/init/all.js | 1 - 17 files changed, 6 insertions(+), 3827 deletions(-) delete mode 100644 content/base/src/CSPUtils.jsm delete mode 100644 content/base/src/contentSecurityPolicy.js delete mode 100644 content/base/src/contentSecurityPolicy.manifest delete mode 100644 content/base/test/unit/test_csp_ignores_path.js delete mode 100644 content/base/test/unit/test_csputils.js diff --git a/b2g/components/ProcessGlobal.js b/b2g/components/ProcessGlobal.js index 6cb3a550b07..f7e63b65c45 100644 --- a/b2g/components/ProcessGlobal.js +++ b/b2g/components/ProcessGlobal.js @@ -22,9 +22,6 @@ const Cu = Components.utils; Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -// Preloading the CSP jsm in this process early on. -Cu.import("resource://gre/modules/CSPUtils.jsm"); - function debug(msg) { log(msg); } diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 8b178ca05b8..c7b6085f22b 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -490,8 +490,6 @@ @BINPATH@/components/formautofill.manifest @BINPATH@/components/FormAutofillContentService.js @BINPATH@/components/FormAutofillStartup.js -@BINPATH@/components/contentSecurityPolicy.manifest -@BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js @BINPATH@/components/messageWakeupService.js diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 85de33f6c86..c51cd237719 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -452,8 +452,6 @@ @BINPATH@/components/formautofill.manifest @BINPATH@/components/FormAutofillContentService.js @BINPATH@/components/FormAutofillStartup.js -@BINPATH@/components/contentSecurityPolicy.manifest -@BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js @BINPATH@/browser/components/BrowserProfileMigrators.manifest diff --git a/content/base/public/nsIContentPolicy.idl b/content/base/public/nsIContentPolicy.idl index 68d6c188164..f57e8e8fa03 100644 --- a/content/base/public/nsIContentPolicy.idl +++ b/content/base/public/nsIContentPolicy.idl @@ -151,7 +151,7 @@ interface nsIContentPolicy : nsISupports const nsContentPolicyType TYPE_BEACON = 19; /* When adding new content types, please update nsContentBlocker, - * NS_CP_ContentTypeName, contentSecurityPolicy.js, all nsIContentPolicy + * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy * implementations, and other things that are not listed here that are * related to nsIContentPolicy. */ diff --git a/content/base/src/CSPUtils.jsm b/content/base/src/CSPUtils.jsm deleted file mode 100644 index 6e94b4b91f3..00000000000 --- a/content/base/src/CSPUtils.jsm +++ /dev/null @@ -1,1737 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Content Security Policy Utilities - * - * Overview - * This contains a set of classes and utilities for CSP. It is in this - * separate file for testing purposes. - */ - -const Cu = Components.utils; -const Ci = Components.interfaces; - -const WARN_FLAG = Ci.nsIScriptError.warningFlag; -const ERROR_FLAG = Ci.nsIScriptError.ERROR_FLAG; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -// Module stuff -this.EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", "CSPHost", - "CSPdebug", "CSPViolationReportListener", "CSPLocalizer", - "CSPPrefObserver"]; - -var STRINGS_URI = "chrome://global/locale/security/csp.properties"; - -// these are not exported -var gIoService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - -var gETLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"] - .getService(Ci.nsIEffectiveTLDService); - -// These regexps represent the concrete syntax on the w3 spec as of 7-5-2012 -// scheme = -const R_SCHEME = new RegExp ("([a-zA-Z0-9\\-]+)", 'i'); -const R_GETSCHEME = new RegExp ("^" + R_SCHEME.source + "(?=\\:)", 'i'); - -// scheme-source = scheme ":" -const R_SCHEMESRC = new RegExp ("^" + R_SCHEME.source + "\\:$", 'i'); - -// host-char = ALPHA / DIGIT / "-" -// For the app: protocol, we need to add {} to the valid character set -const HOSTCHAR = "{}a-zA-Z0-9\\-"; -const R_HOSTCHAR = new RegExp ("[" + HOSTCHAR + "]", 'i'); - -// Complementary character set of HOSTCHAR (characters that can't appear) -const R_COMP_HCHAR = new RegExp ("[^" + HOSTCHAR + "]", "i"); - -// Invalid character set for host strings (which can include dots and star) -const R_INV_HCHAR = new RegExp ("[^" + HOSTCHAR + "\\.\\*]", 'i'); - - -// host = "*" / [ "*." ] 1*host-char *( "." 1*host-char ) -const R_HOST = new RegExp ("\\*|(((\\*\\.)?" + R_HOSTCHAR.source + - "+)" + "(\\." + R_HOSTCHAR.source + "+)*)", 'i'); - -// port = ":" ( 1*DIGIT / "*" ) -const R_PORT = new RegExp ("(\\:([0-9]+|\\*))", 'i'); - -// host-source = [ scheme "://" ] host [ port path file ] -const R_HOSTSRC = new RegExp ("^((" + R_SCHEME.source + "\\:\\/\\/)?(" - + R_HOST.source + ")" - + R_PORT.source + "?)$", 'i'); - -function STRIP_INPUTDELIM(re) { - return re.replace(/(^\^)|(\$$)/g, ""); -} - -// ext-host-source = host-source "/" *( ) -// ; ext-host-source is reserved for future use. -const R_VCHAR_EXCEPT = new RegExp("[!-+--:<-~]"); // ranges exclude , and ; -const R_EXTHOSTSRC = new RegExp ("^" + STRIP_INPUTDELIM(R_HOSTSRC.source) - + "\\/" - + R_VCHAR_EXCEPT.source + "*$", 'i'); - -// keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'" -const R_KEYWORDSRC = new RegExp ("^('self'|'unsafe-inline'|'unsafe-eval')$", 'i'); - -const R_BASE64 = new RegExp ("([a-zA-Z0-9+/]+={0,2})"); - -// nonce-source = "'nonce-" nonce-value "'" -// nonce-value = 1*( ALPHA / DIGIT / "+" / "/" ) -const R_NONCESRC = new RegExp ("^'nonce-" + R_BASE64.source + "'$"); - -// hash-source = "'" hash-algo "-" hash-value "'" -// hash-algo = "sha256" / "sha384" / "sha512" -// hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" ) -// Each algo must be a valid argument to nsICryptoHash.init -const R_HASH_ALGOS = new RegExp ("(sha256|sha384|sha512)"); -const R_HASHSRC = new RegExp ("^'" + R_HASH_ALGOS.source + "-" + R_BASE64.source + "'$"); - -// source-exp = scheme-source / host-source / keyword-source -const R_SOURCEEXP = new RegExp (R_SCHEMESRC.source + "|" + - R_HOSTSRC.source + "|" + - R_EXTHOSTSRC.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; - }, - - get experimentalEnabled () { - if (!this._branch) - this._initialize(); - return this._experimentalEnabled; - }, - - _initialize: function() { - var prefSvc = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefService); - this._branch = prefSvc.getBranch("security.csp."); - this._branch.addObserver("", this, false); - this._debugEnabled = this._branch.getBoolPref("debug"); - this._experimentalEnabled = this._branch.getBoolPref("experimentalEnabled"); - }, - - unregister: function() { - if (!this._branch) return; - this._branch.removeObserver("", this); - }, - - observe: function(aSubject, aTopic, aData) { - if (aTopic != "nsPref:changed") return; - if (aData === "debug") - this._debugEnabled = this._branch.getBoolPref("debug"); - if (aData === "experimentalEnabled") - this._experimentalEnabled = this._branch.getBoolPref("experimentalEnabled"); - }, -}; - -this.CSPdebug = function CSPdebug(aMsg) { - if (!CSPPrefObserver.debugEnabled) return; - - aMsg = 'CSP debug: ' + aMsg + "\n"; - Components.classes["@mozilla.org/consoleservice;1"] - .getService(Ci.nsIConsoleService) - .logStringMessage(aMsg); -} - -// Callback to resume a request once the policy-uri has been fetched -function CSPPolicyURIListener(policyURI, docRequest, csp, reportOnly) { - this._policyURI = policyURI; // location of remote policy - this._docRequest = docRequest; // the parent document request - this._csp = csp; // parent document's CSP - this._policy = ""; // contents fetched from policyURI - this._wrapper = null; // nsIScriptableInputStream - this._docURI = docRequest.QueryInterface(Ci.nsIChannel) - .URI; // parent document URI (to be used as 'self') - this._reportOnly = reportOnly; -} - -CSPPolicyURIListener.prototype = { - - QueryInterface: function(iid) { - if (iid.equals(Ci.nsIStreamListener) || - iid.equals(Ci.nsIRequestObserver) || - iid.equals(Ci.nsISupports)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - onStartRequest: - function(request, context) {}, - - onDataAvailable: - function(request, context, inputStream, offset, count) { - if (this._wrapper == null) { - this._wrapper = Components.classes["@mozilla.org/scriptableinputstream;1"] - .createInstance(Ci.nsIScriptableInputStream); - this._wrapper.init(inputStream); - } - // store the remote policy as it becomes available - 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.appendPolicy(this._policy, this._docURI, this._reportOnly); - } - else { - // problem fetching policy so fail closed by appending a "block it all" - // policy. Also toss an error into the console so developers can see why - // this policy is used. - this._csp.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorFetchingPolicy", - [status])); - this._csp.appendPolicy("default-src 'none'", this._docURI, this._reportOnly); - } - // resume the parent document request - this._docRequest.resume(); - } -}; - -//:::::::::::::::::::::::: CLASSES ::::::::::::::::::::::::::// - -/** - * Class that represents a parsed policy structure. - */ -this.CSPRep = function CSPRep() { - // 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; - this._allowInlineStyles = false; - this._reportOnlyMode = false; - - // don't auto-populate _directives, so it is easier to find bugs - this._directives = {}; -} - -// Source directives for our CSP 1.0 implementation. -CSPRep.SRC_DIRECTIVES = { - 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 */ -}; - -/** - * Factory to create a new CSPRep, parsed from a string, compliant - * with the CSP 1.0 spec. - * - * @param aStr - * string rep of a CSP - * @param self (optional) - * URI representing the "self" source - * @param reportOnly (optional) - * whether or not this CSP is report-only (defaults to false) - * @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 - * @param enforceSelfChecks (optional) - * if present, and "true", will check to be sure "self" has the - * appropriate values to inherit when they are omitted from the source. - * @returns - * an instance of CSPRep - */ -CSPRep.fromString = function(aStr, self, reportOnly, docRequest, csp, - enforceSelfChecks) { - var SD = CSPRep.SRC_DIRECTIVES; - var UD = CSPRep.URI_DIRECTIVES; - var aCSPR = new CSPRep(); - aCSPR._originalText = aStr; - aCSPR._innerWindowID = innerWindowFromRequest(docRequest); - if (typeof reportOnly === 'undefined') reportOnly = false; - aCSPR._reportOnlyMode = reportOnly; - - var selfUri = null; - if (self instanceof Ci.nsIURI) { - selfUri = self.cloneIgnoringRef(); - // 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_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].toLowerCase(); - var dirvalue = dir.substring(dirname.length).trim(); - // skip duplicates - if (!dirs.hasOwnProperty(dirname)) { - 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) { - // Parse the source list (look ahead) so we can set the defaults properly, - // honoring the 'unsafe-inline' and 'unsafe-eval' keywords - var defaultSrcValue = CSPSourceList.fromString(dirs["default-src"], null, self); - if (!defaultSrcValue._allowUnsafeInline) { - aCSPR._allowInlineScripts = false; - aCSPR._allowInlineStyles = false; - } - if (!defaultSrcValue._allowUnsafeEval) { - aCSPR._allowEval = false; - } - } - 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; - } - - // 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, - enforceSelfChecks); - if (dv) { - // Check for unsafe-inline in style-src - if (sdi === "style-src" && dv._allowUnsafeInline) { - aCSPR._allowInlineStyles = true; - } else if (sdi === "script-src") { - // Check for unsafe-inline and unsafe-eval in script-src - if (dv._allowUnsafeInline) { - aCSPR._allowInlineScripts = true; - } - if (dv._allowUnsafeEval) { - aCSPR._allowEval = true; - } - } - - 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, this will throw NS_ERROR_FAILURE, causing a - // parse failure. - uri.host; - - // warn about, but do not prohibit non-http and non-https schemes for - // reporting URIs. The spec allows unrestricted URIs resolved - // relative to "self", but we should let devs know if the scheme is - // abnormal and may fail a POST. - if (!uri.schemeIs("http") && !uri.schemeIs("https")) { - cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp2", - [uri.asciiSpec])); - } - } 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. - 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.fromString("default-src 'none'", null, reportOnly); - } - // 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.fromString("default-src 'none'", null, reportOnly); - } - - var uri = ''; - try { - uri = gIoService.newURI(dirvalue, null, selfUri); - } catch(e) { - cspError(aCSPR, CSPLocalizer.getFormatStr("policyURIParseError", [dirvalue])); - return CSPRep.fromString("default-src 'none'", null, reportOnly); - } - - // 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.fromString("default-src 'none'", null, reportOnly); - } - if (selfUri.port !== uri.port){ - cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingPort", [uri.port.toString()])); - return CSPRep.fromString("default-src 'none'", null, reportOnly); - } - if (selfUri.scheme !== uri.scheme){ - cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingScheme", [uri.scheme])); - return CSPRep.fromString("default-src 'none'", null, reportOnly); - } - } - - // 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, reportOnly), null); - } - catch (e) { - // resume the document request and apply most restrictive policy - docRequest.resume(); - cspError(aCSPR, CSPLocalizer.getFormatStr("errorFetchingPolicy", [e.toString()])); - return CSPRep.fromString("default-src 'none'", null, reportOnly); - } - - // return a fully-open policy to be used until the contents of the - // policy-uri come back - return CSPRep.fromString("default-src *", null, reportOnly); - } - - // UNIDENTIFIED DIRECTIVE ///////////////////////////////////////////// - cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective", [dirname])); - - } // end directive: loop - - // If this is a Report-Only header and report-uri is not in the directive - // list, tell developer either specify report-uri directive or use - // a non-Report-Only CSP header. - if (aCSPR._reportOnlyMode && !aCSPR._directives.hasOwnProperty(UD.REPORT_URI)) { - cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotInReportOnlyHeader", - [selfUri ? selfUri.prePath : "undefined"])); - } - - return aCSPR; -}; - -CSPRep.prototype = { - /** - * Returns a space-separated list of all report uris defined, or 'none' if there are none. - */ - getReportURIs: - function() { - if (!this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI]) - return ""; - return this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI]; - }, - - /** - * Compares this CSPRep instance to another. - */ - equals: - function(that) { - if (this._directives.length != that._directives.length) { - return false; - } - for (var i in this._directives) { - if (!that._directives[i] || !this._directives[i].equals(that._directives[i])) { - return false; - } - } - return (this.allowsInlineScripts === that.allowsInlineScripts) - && (this.allowsEvalInScripts === that.allowsEvalInScripts) - && (this.allowsInlineStyles === that.allowsInlineStyles); - }, - - /** - * Generates canonical string representation of the policy. - */ - toString: - function csp_toString() { - var dirs = []; - - for (var i in this._directives) { - if (this._directives[i]) { - dirs.push(i + " " + this._directives[i].toString()); - } - } - return dirs.join("; "); - }, - - permitsNonce: - function csp_permitsNonce(aNonce, aDirective) { - if (!this._directives.hasOwnProperty(aDirective)) return false; - return this._directives[aDirective]._sources.some(function (source) { - return source instanceof CSPNonceSource && source.permits(aNonce); - }); - }, - - permitsHash: - function csp_permitsHash(aContent, aDirective) { - if (!this._directives.hasOwnProperty(aDirective)) return false; - return this._directives[aDirective]._sources.some(function (source) { - return source instanceof CSPHashSource && source.permits(aContent); - }); - }, - - /** - * Determines if this policy accepts a URI. - * @param aURI - * URI of the requested resource - * @param aDirective - * one of the SRC_DIRECTIVES defined above - * @returns - * true if the policy permits the URI in given context. - */ - permits: - function csp_permits(aURI, aDirective) { - if (!aURI) return false; - - // 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 right directive set is used - let DIRS = CSPRep.SRC_DIRECTIVES; - - let directiveInPolicy = false; - for (var i in DIRS) { - if (DIRS[i] === aDirective) { - // for catching calls with invalid contexts (below) - directiveInPolicy = true; - if (this._directives.hasOwnProperty(aDirective)) { - return this._directives[aDirective].permits(aURI); - } - //found matching dir, can stop looking - break; - } - } - - // frame-ancestors is a special case; it doesn't fall back to default-src. - if (aDirective === DIRS.FRAME_ANCESTORS) - return true; - - // All directives that don't fall back to default-src should have an escape - // hatch above (like frame-ancestors). - if (!directiveInPolicy) { - // if this code runs, there's probably something calling permits() that - // shouldn't be calling permits(). - CSPdebug("permits called with invalid load type: " + aDirective); - return false; - } - - // no directives specifically matched, fall back to default-src. - // (default-src may not be present for CSP 1.0-compliant policies, and - // indicates no relevant directives were present and the load should be - // permitted). - if (this._directives.hasOwnProperty(DIRS.DEFAULT_SRC)) { - return this._directives[DIRS.DEFAULT_SRC].permits(aURI); - } - - // no relevant directives present -- this means for CSP 1.0 that the load - // should be permitted. - return true; - }, - - /** - * Returns true if "eval" is enabled through the "eval" keyword. - */ - get allowsEvalInScripts () { - return this._allowEval; - }, - - /** - * Returns true if inline scripts are enabled through the "inline" - * keyword. - */ - get allowsInlineScripts () { - return this._allowInlineScripts; - }, - - /** - * Returns true if inline styles are enabled through the "inline-style" - * keyword. - */ - get allowsInlineStyles () { - return this._allowInlineStyles; - }, - - /** - * Sends a message to the error console and web developer console. - * @param aFlag - * The nsIScriptError flag constant indicating severity - * @param aMsg - * The message to send - * @param aSource (optional) - * The URL of the file in which the error occurred - * @param aScriptLine (optional) - * The line in the source file which the error occurred - * @param aLineNum (optional) - * The number of the line where the error occurred - */ - log: - function cspd_log(aFlag, aMsg, aSource, aScriptLine, aLineNum) { - var textMessage = "Content Security Policy: " + aMsg; - var consoleMsg = Components.classes["@mozilla.org/scripterror;1"] - .createInstance(Ci.nsIScriptError); - if (this._innerWindowID) { - consoleMsg.initWithWindowID(textMessage, aSource, aScriptLine, aLineNum, - 0, aFlag, - "CSP", - this._innerWindowID); - } else { - consoleMsg.init(textMessage, aSource, aScriptLine, aLineNum, 0, - aFlag, - "CSP"); - } - Components.classes["@mozilla.org/consoleservice;1"] - .getService(Ci.nsIConsoleService).logMessage(consoleMsg); - }, - -}; - -////////////////////////////////////////////////////////////////////// -/** - * Class to represent a list of sources - */ -this.CSPSourceList = function CSPSourceList() { - this._sources = []; - this._permitAllSources = false; - - // When this is true, the source list contains 'unsafe-inline'. - this._allowUnsafeInline = false; - - // When this is true, the source list contains 'unsafe-eval'. - this._allowUnsafeEval = false; - - // When this is true, the source list contains at least one nonce-source - this._hasNonceSource = false; - - // When this is true, the source list contains at least one hash-source - this._hasHashSource = false; -} - -/** - * Factory to create a new CSPSourceList, parsed from a string. - * - * @param aStr - * string rep of a CSP Source List - * @param aCSPRep - * the CSPRep to which this souce list belongs. If null, CSP errors and - * warnings will not be sent to the web console. - * @param self (optional) - * URI or CSPSource representing the "self" source - * @param enforceSelfChecks (optional) - * if present, and "true", will check to be sure "self" has the - * appropriate values to inherit when they are omitted from the source. - * @returns - * an instance of CSPSourceList - */ -CSPSourceList.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) { - // source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ] - // / *WSP "'none'" *WSP - - /* If self parameter is passed, convert to CSPSource, - unless it is already a CSPSource. */ - if (self && !(self instanceof CSPSource)) { - self = CSPSource.create(self, aCSPRep); - } - - var slObj = new CSPSourceList(); - slObj._CSPRep = aCSPRep; - aStr = aStr.trim(); - // w3 specifies case insensitive equality - if (aStr.toLowerCase() === "'none'") { - slObj._permitAllSources = false; - return slObj; - } - - var tokens = aStr.split(/\s+/); - for (var i in tokens) { - if (!R_SOURCEEXP.test(tokens[i])) { - cspWarn(aCSPRep, - CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource", - [tokens[i]])); - continue; - } - var src = CSPSource.create(tokens[i], aCSPRep, self, enforceSelfChecks); - if (!src) { - cspWarn(aCSPRep, - CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource", - [tokens[i]])); - continue; - } - - // if a source allows unsafe-inline, set our flag to indicate this. - if (src._allowUnsafeInline) - slObj._allowUnsafeInline = true; - - // if a source allows unsafe-eval, set our flag to indicate this. - if (src._allowUnsafeEval) - slObj._allowUnsafeEval = true; - - if (src instanceof CSPNonceSource) - slObj._hasNonceSource = true; - - if (src instanceof CSPHashSource) - slObj._hasHashSource = true; - - // if a source is a *, then we can permit all sources - if (src.permitAll) { - slObj._permitAllSources = true; - } else { - slObj._sources.push(src); - } - } - - return slObj; -}; - -CSPSourceList.prototype = { - /** - * Compares one CSPSourceList to another. - * - * @param that - * another CSPSourceList - * @returns - * true if they have the same data - */ - equals: - function(that) { - // special case to default-src * and 'none' to look different - // (both have a ._sources.length of 0). - if (that._permitAllSources != this._permitAllSources) { - return false; - } - if (that._sources.length != this._sources.length) { - return false; - } - // sort both arrays and compare like a zipper - // XXX (sid): I think we can make this more efficient - var sortfn = function(a,b) { - return a.toString.toLowerCase() > b.toString.toLowerCase(); - }; - var a_sorted = this._sources.sort(sortfn); - var b_sorted = that._sources.sort(sortfn); - for (var i in a_sorted) { - if (!a_sorted[i].equals(b_sorted[i])) { - return false; - } - } - return true; - }, - - /** - * Generates canonical string representation of the Source List. - */ - toString: - function() { - if (this.isNone()) { - return "'none'"; - } - if (this._permitAllSources) { - return "*"; - } - return this._sources.map(function(x) { return x.toString(); }).join(" "); - }, - - /** - * Returns whether or not this source list represents the "'none'" special - * case. - */ - isNone: - function() { - return (!this._permitAllSources) && (this._sources.length < 1); - }, - - /** - * Returns whether or not this source list permits all sources (*). - */ - isAll: - function() { - return this._permitAllSources; - }, - - /** - * Makes a new deep copy of this object. - * @returns - * a new CSPSourceList - */ - clone: - function() { - var aSL = new CSPSourceList(); - aSL._permitAllSources = this._permitAllSources; - aSL._CSPRep = this._CSPRep; - for (var i in this._sources) { - aSL._sources[i] = this._sources[i].clone(); - } - return aSL; - }, - - /** - * Determines if this directive accepts a URI. - * @param aURI - * the URI in question - * @returns - * true if the URI matches a source in this source list. - */ - permits: - function cspsd_permits(aURI) { - if (this.isNone()) return false; - if (this.isAll()) return true; - - for (var i in this._sources) { - if (this._sources[i].permits(aURI)) { - return true; - } - } - return false; - } -} - -////////////////////////////////////////////////////////////////////// -/** - * Class to model a source (scheme, host, port) - */ -this.CSPSource = function CSPSource() { - this._scheme = undefined; - this._port = undefined; - this._host = undefined; - - //when set to true, this allows all source - this._permitAll = false; - - // when set to true, this source represents 'self' - this._isSelf = false; - - // when set to true, this source allows inline scripts or styles - this._allowUnsafeInline = false; - - // when set to true, this source allows eval to be used - this._allowUnsafeEval = false; -} - -/** - * General factory method to create a new source from one of the following - * types: - * - nsURI - * - string - * - CSPSource (clone) - * @param aData - * string, nsURI, or CSPSource - * @param aCSPRep - * The CSPRep this source belongs to. If null, CSP errors and warnings - * will not be sent to the web console. - * @param self (optional) - * if present, string, URI, or CSPSource representing the "self" resource - * @param enforceSelfChecks (optional) - * if present, and "true", will check to be sure "self" has the - * appropriate values to inherit when they are omitted from the source. - * @returns - * an instance of CSPSource - */ -CSPSource.create = function(aData, aCSPRep, self, enforceSelfChecks) { - if (typeof aData === 'string') - return CSPSource.fromString(aData, aCSPRep, self, enforceSelfChecks); - - if (aData instanceof Ci.nsIURI) { - // clean userpass out of the URI (not used for CSP origin checking, but - // shows up in prePath). - let cleanedUri = aData.cloneIgnoringRef(); - try { - // GetUserPass throws for some protocols without userPass - cleanedUri.userPass = ''; - } catch (ex) {} - - return CSPSource.fromURI(cleanedUri, aCSPRep, self, enforceSelfChecks); - } - - if (aData instanceof CSPSource) { - var ns = aData.clone(); - ns._self = CSPSource.create(self); - return ns; - } - - return null; -} - -/** - * Factory to create a new CSPSource, from a nsIURI. - * - * Don't use this if you want to wildcard ports! - * - * @param aURI - * nsIURI rep of a URI - * @param aCSPRep - * The policy this source belongs to. If null, CSP errors and warnings - * will not be sent to the web console. - * @param self (optional) - * string or CSPSource representing the "self" source - * @param enforceSelfChecks (optional) - * if present, and "true", will check to be sure "self" has the - * appropriate values to inherit when they are omitted from aURI. - * @returns - * an instance of CSPSource - */ -CSPSource.fromURI = function(aURI, aCSPRep, self, enforceSelfChecks) { - if (!(aURI instanceof Ci.nsIURI)) { - cspError(aCSPRep, CSPLocalizer.getStr("cspSourceNotURI")); - return null; - } - - if (!self && enforceSelfChecks) { - cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided")); - return null; - } - - if (self && !(self instanceof CSPSource)) { - self = CSPSource.create(self, aCSPRep, undefined, false); - } - - var sObj = new CSPSource(); - sObj._self = self; - sObj._CSPRep = aCSPRep; - - // PARSE - // If 'self' is undefined, then use default port for scheme if there is one. - - // grab scheme (if there is one) - try { - sObj._scheme = aURI.scheme; - } catch(e) { - sObj._scheme = undefined; - cspError(aCSPRep, CSPLocalizer.getFormatStr("uriWithoutScheme", - [aURI.asciiSpec])); - return null; - } - - // grab host (if there is one) - try { - // if there's no host, an exception will get thrown - // (NS_ERROR_FAILURE) - sObj._host = CSPHost.fromString(aURI.host); - } catch(e) { - sObj._host = undefined; - } - - // grab port (if there is one) - // creating a source from an nsURI is limited in that one cannot specify "*" - // for port. In fact, there's no way to represent "*" differently than - // a blank port in an nsURI, since "*" turns into -1, and so does an - // absence of port declaration. - - // port is never inherited from self -- this gets too confusing. - // Instead, whatever scheme is used (an explicit one or the inherited - // one) dictates the port if no port is explicitly stated. - // Set it to undefined here and the default port will be resolved in the - // getter for .port. - sObj._port = undefined; - try { - // if there's no port, an exception will get thrown - // (NS_ERROR_FAILURE) - if (aURI.port > 0) { - sObj._port = aURI.port; - } - } catch(e) { - sObj._port = undefined; - } - - return sObj; -}; - -/** - * Factory to create a new CSPSource, parsed from a string. - * - * @param aStr - * string rep of a CSP Source - * @param aCSPRep - * the CSPRep this CSPSource belongs to - * @param self (optional) - * string, URI, or CSPSource representing the "self" source - * @param enforceSelfChecks (optional) - * if present, and "true", will check to be sure "self" has the - * appropriate values to inherit when they are omitted from aURI. - * @returns - * an instance of CSPSource - */ -CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) { - if (!aStr) - return null; - - if (!(typeof aStr === 'string')) { - cspError(aCSPRep, CSPLocalizer.getStr("argumentIsNotString")); - return null; - } - - var sObj = new CSPSource(); - sObj._self = self; - sObj._CSPRep = aCSPRep; - - - // if equal, return does match - if (aStr === "*") { - sObj._permitAll = true; - return sObj; - } - - if (!self && enforceSelfChecks) { - cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided")); - return null; - } - - if (self && !(self instanceof CSPSource)) { - self = CSPSource.create(self, aCSPRep, undefined, false); - } - - // check for 'unsafe-inline' (case insensitive) - if (aStr.toLowerCase() === "'unsafe-inline'"){ - sObj._allowUnsafeInline = true; - return sObj; - } - - // check for 'unsafe-eval' (case insensitive) - if (aStr.toLowerCase() === "'unsafe-eval'"){ - sObj._allowUnsafeEval = true; - return sObj; - } - - // Check for scheme-source match - this only matches if the source - // string is just a scheme with no host. - if (R_SCHEMESRC.test(aStr)) { - var schemeSrcMatch = R_GETSCHEME.exec(aStr); - sObj._scheme = schemeSrcMatch[0]; - if (!sObj._host) sObj._host = CSPHost.fromString("*"); - if (!sObj._port) sObj._port = "*"; - return sObj; - } - - // check for host-source or ext-host-source match - if (R_HOSTSRC.test(aStr) || R_EXTHOSTSRC.test(aStr)) { - var schemeMatch = R_GETSCHEME.exec(aStr); - // check that the scheme isn't accidentally matching the host. There should - // be '://' if there is a valid scheme in an (EXT)HOSTSRC - if (!schemeMatch || aStr.indexOf("://") == -1) { - sObj._scheme = self.scheme; - schemeMatch = null; - } else { - sObj._scheme = schemeMatch[0]; - } - - // Bug 916054: in CSP 1.0, source-expressions that are paths should have - // the path after the origin ignored and only the origin enforced. - if (R_EXTHOSTSRC.test(aStr)) { - var extHostMatch = R_EXTHOSTSRC.exec(aStr); - aStr = extHostMatch[1]; - } - - var hostMatch = R_HOSTSRC.exec(aStr); - if (!hostMatch) { - cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource", - [aStr])); - return null; - } - // Host regex gets scheme, so remove scheme from aStr. Add 3 for '://' - if (schemeMatch) { - hostMatch = R_HOSTSRC.exec(aStr.substring(schemeMatch[0].length + 3)); - } - - var portMatch = R_PORT.exec(hostMatch); - // Host regex also gets port, so remove the port here. - if (portMatch) { - hostMatch = R_HOSTSRC.exec(hostMatch[0].substring(0, hostMatch[0].length - portMatch[0].length)); - } - - sObj._host = CSPHost.fromString(hostMatch[0]); - if (!portMatch) { - // gets the default port for the given scheme - var defPort = Services.io.getProtocolHandler(sObj._scheme).defaultPort; - if (!defPort) { - cspError(aCSPRep, - CSPLocalizer.getFormatStr("couldntParseInvalidSource", - [aStr])); - 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 - if (R_HASHSRC.test(aStr)) { - return CSPHashSource.fromString(aStr, aCSPRep); - } - - // check for 'self' (case insensitive) - if (aStr.toLowerCase() === "'self'") { - if (!self) { - cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData")); - return null; - } - sObj._self = self.clone(); - sObj._isSelf = true; - return sObj; - } - - cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource", - [aStr])); - return null; -}; - -CSPSource.validSchemeName = function(aStr) { - // ::= - // ::= - // | - // ::= | | "+" | "." | "-" - - return aStr.match(/^[a-zA-Z][a-zA-Z0-9+.-]*$/); -}; - -CSPSource.prototype = { - - get scheme () { - if (this._isSelf && this._self) - return this._self.scheme; - if (!this._scheme && this._self) - return this._self.scheme; - return this._scheme; - }, - - get host () { - if (this._isSelf && this._self) - return this._self.host; - if (!this._host && this._self) - return this._self.host; - return this._host; - }, - - get permitAll () { - if (this._isSelf && this._self) - return this._self.permitAll; - return this._permitAll; - }, - - /** - * If this doesn't have a nonstandard port (hard-defined), use the default - * port for this source's scheme. Should never inherit port from 'self'. - */ - get port () { - if (this._isSelf && this._self) - return this._self.port; - if (this._port) return this._port; - // if no port, get the default port for the scheme - // (which may be inherited from 'self') - if (this.scheme) { - try { - var port = gIoService.getProtocolHandler(this.scheme).defaultPort; - if (port > 0) return port; - } catch(e) { - // if any errors happen, fail gracefully. - } - } - - return undefined; - }, - - /** - * Generates canonical string representation of the Source. - */ - toString: - function() { - if (this._isSelf) - return this._self.toString(); - - if (this._allowUnsafeInline) - return "'unsafe-inline'"; - - if (this._allowUnsafeEval) - return "'unsafe-eval'"; - - var s = ""; - if (this.scheme) - s = s + this.scheme + "://"; - if (this._host) - s = s + this._host; - - // CSP 1.0 4.11 says the report should use URI-reference from RFC 3986, - // 3.2.3 and indicates that the default port should be omitted. - // Non-default ports are included. - if (this.port && gIoService.getProtocolHandler(this.scheme).defaultPort != this.port) - s = s + ":" + this.port; - return s; - }, - - /** - * Makes a new deep copy of this object. - * @returns - * a new CSPSource - */ - clone: - function() { - var aClone = new CSPSource(); - aClone._self = this._self ? this._self.clone() : undefined; - aClone._scheme = this._scheme; - aClone._port = this._port; - aClone._host = this._host ? this._host.clone() : undefined; - aClone._isSelf = this._isSelf; - aClone._CSPRep = this._CSPRep; - return aClone; - }, - - /** - * Determines if this Source accepts a URI. - * @param aSource - * the URI, or CSPSource in question - * @returns - * true if the URI matches a source in this source list. - */ - permits: - function(aSource) { - if (!aSource) return false; - - if (!(aSource instanceof CSPSource)) - aSource = CSPSource.create(aSource, this._CSPRep); - - // verify scheme - if (this.scheme.toLowerCase() != aSource.scheme.toLowerCase()) - return false; - - // port is defined in 'this' (undefined means it may not be relevant - // to the scheme) AND this port (implicit or explicit) matches - // aSource's port - if (this.port && this.port !== "*" && this.port != aSource.port) - return false; - - // host is defined in 'this' (undefined means it may not be relevant - // to the scheme) AND this host (implicit or explicit) permits - // aSource's host. - if (this.host && !this.host.permits(aSource.host)) - return false; - - // all scheme, host and port matched! - return true; - }, - - /** - * Compares one CSPSource to another. - * - * @param that - * another CSPSource - * @param resolveSelf (optional) - * if present, and 'true', implied values are obtained from 'self' - * instead of assumed to be "anything" - * @returns - * true if they have the same data - */ - equals: - function(that, resolveSelf) { - // 1. schemes match - // 2. ports match - // 3. either both hosts are undefined, or one equals the other. - if (resolveSelf) - return this.scheme.toLowerCase() === that.scheme.toLowerCase() - && this.port === that.port - && (!(this.host || that.host) || - (this.host && this.host.equals(that.host))); - - // otherwise, compare raw (non-self-resolved values) - return this._scheme.toLowerCase() === that._scheme.toLowerCase() - && this._port === that._port - && (!(this._host || that._host) || - (this._host && this._host.equals(that._host))); - }, - -}; - -////////////////////////////////////////////////////////////////////// -/** - * Class to model a host *.x.y. - */ -this.CSPHost = function CSPHost() { - this._segments = []; -} - -/** - * Factory to create a new CSPHost, parsed from a string. - * - * @param aStr - * string rep of a CSP Host - * @returns - * an instance of CSPHost - */ -CSPHost.fromString = function(aStr) { - if (!aStr) return null; - - // host string must be LDH with dots and stars. - var invalidChar = aStr.match(R_INV_HCHAR); - if (invalidChar) { - CSPdebug("Invalid character '" + invalidChar + "' in host " + aStr); - return null; - } - - var hObj = new CSPHost(); - hObj._segments = aStr.split(/\./); - if (hObj._segments.length < 1) - return null; - - // validate data in segments - for (var i in hObj._segments) { - var seg = hObj._segments[i]; - if (seg == "*") { - if (i > 0) { - // Wildcard must be FIRST - CSPdebug("Wildcard char located at invalid position in '" + aStr + "'"); - return null; - } - } - else if (seg.match(R_COMP_HCHAR)) { - // Non-wildcard segment must be LDH string - CSPdebug("Invalid segment '" + seg + "' in host value"); - return null; - } - } - return hObj; -}; - -CSPHost.prototype = { - /** - * Generates canonical string representation of the Host. - */ - toString: - function() { - return this._segments.join("."); - }, - - /** - * Makes a new deep copy of this object. - * @returns - * a new CSPHost - */ - clone: - function() { - var aHost = new CSPHost(); - for (var i in this._segments) { - aHost._segments[i] = this._segments[i]; - } - return aHost; - }, - - /** - * Returns true if this host accepts the provided host (or the other way - * around). - * @param aHost - * the FQDN in question (CSPHost or String) - * @returns - */ - permits: - function(aHost) { - if (!aHost) { - aHost = CSPHost.fromString("*"); - } - - if (!(aHost instanceof CSPHost)) { - // -- compare CSPHost to String - aHost = CSPHost.fromString(aHost); - } - var thislen = this._segments.length; - var thatlen = aHost._segments.length; - - // don't accept a less specific host: - // \--> *.b.a doesn't accept b.a. - if (thatlen < thislen) { return false; } - - // check for more specific host (and wildcard): - // \--> *.b.a accepts d.c.b.a. - // \--> c.b.a doesn't accept d.c.b.a. - if ((thatlen > thislen) && this._segments[0] != "*") { - return false; - } - - // Given the wildcard condition (from above), - // only necessary to compare elements that are present - // in this host. Extra tokens in aHost are ok. - // * Compare from right to left. - for (var i=1; i <= thislen; i++) { - if (this._segments[thislen-i] != "*" && - (this._segments[thislen-i].toLowerCase() != - aHost._segments[thatlen-i].toLowerCase())) { - return false; - } - } - - // at this point, all conditions are met, so the host is allowed - return true; - }, - - /** - * Compares one CSPHost to another. - * - * @param that - * another CSPHost - * @returns - * true if they have the same data - */ - equals: - function(that) { - if (this._segments.length != that._segments.length) - return false; - - for (var i=0; i= this._policies.length) { - throw Cr.NS_ERROR_FAILURE; - } - return this._policies[index]; - }, - - _buildViolatedDirectiveString: - function(aDirectiveName, aPolicy) { - var SD = CSPRep.SRC_DIRECTIVES; - var cspContext = (SD[aDirectiveName] in aPolicy._directives) ? SD[aDirectiveName] : SD.DEFAULT_SRC; - var directive = aPolicy._directives[cspContext]; - return cspContext + ' ' + directive.toString(); - }, - - /** - * Returns policy string representing the policy at "index". - */ - getPolicy: function(index) { - return this._getPolicyInternal(index).toString(); - }, - - /** - * Returns count of policies. - */ - get numPolicies() { - return this._policies.length; - }, - - getAllowsInlineScript: function(shouldReportViolations) { - // report it? (for each policy, is it violated?) - shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsInlineScripts; }); - - // allow it to execute? (Do all the policies allow it to execute)? - return this._policies.every(function(a) { - return a._reportOnlyMode || a.allowsInlineScripts; - }); - }, - - getAllowsEval: function(shouldReportViolations) { - // report it? (for each policy, is it violated?) - shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsEvalInScripts; }); - - // allow it to execute? (Do all the policies allow it to execute)? - return this._policies.every(function(a) { - return a._reportOnlyMode || a.allowsEvalInScripts; - }); - }, - - getAllowsInlineStyle: function(shouldReportViolations) { - // report it? (for each policy, is it violated?) - shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsInlineStyles; }); - - // allow it to execute? (Do all the policies allow it to execute)? - return this._policies.every(function(a) { - return a._reportOnlyMode || a.allowsInlineStyles; - }); - }, - - getAllowsNonce: function(aNonce, aContentType, shouldReportViolation) { - if (!(aContentType == Ci.nsIContentPolicy.TYPE_SCRIPT || - aContentType == Ci.nsIContentPolicy.TYPE_STYLESHEET)) { - CSPdebug("Nonce check requested for an invalid content type (not script or style): " + aContentType); - return false; - } - - let directive = ContentSecurityPolicy._MAPPINGS[aContentType]; - let policyAllowsNonce = [ policy.permitsNonce(aNonce, directive) - for (policy of this._policies) ]; - - shouldReportViolation.value = this._policies.some(function(policy, i) { - // Don't report a violation if the policy didn't use nonce-source - return policy._directives.hasOwnProperty(directive) && - policy._directives[directive]._hasNonceSource && - !policyAllowsNonce[i]; - }); - - // allow it to execute? (Do all the policies allow it to execute)? - return this._policies.every(function(policy, i) { - return policy._reportOnlyMode || policyAllowsNonce[i]; - }); - }, - - getAllowsHash: function(aContent, aContentType, shouldReportViolation) { - if (!(aContentType == Ci.nsIContentPolicy.TYPE_SCRIPT || - aContentType == Ci.nsIContentPolicy.TYPE_STYLESHEET)) { - CSPdebug("Hash check requested for an invalid content type (not script or style): " + aContentType); - return false; - } - - let directive = ContentSecurityPolicy._MAPPINGS[aContentType]; - let policyAllowsHash = [ policy.permitsHash(aContent, directive) - for (policy of this._policies) ]; - - shouldReportViolation.value = this._policies.some(function(policy, i) { - // Don't report a violation if the policy didn't use hash-source - return policy._directives.hasOwnProperty(directive) && - policy._directives[directive]._hasHashSource && - !policyAllowsHash[i]; - }); - - // allow it to execute? (Do all the policies allow it to execute)? - return this._policies.every(function(policy, i) { - return policy._reportOnlyMode || policyAllowsHash[i]; - }); - }, - - /** - * For each policy, log any violation on the Error Console and send a report - * if a report-uri is present in the policy - * - * @param aViolationType - * one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval - * @param aSourceFile - * name of the source file containing the violation (if available) - * @param aContentSample - * sample of the violating content (to aid debugging) - * @param aLineNum - * source line number of the violation (if available) - * @param aNonce - * (optional) If this is a nonce violation, include the nonce so we can - * recheck to determine which policies were violated and send the - * appropriate reports. - * @param aContent - * (optional) If this is a hash violation, include contents of the inline - * resource in the question so we can recheck the hash in order to - * determine which policies were violated and send the appropriate - * reports. - */ - logViolationDetails: - function(aViolationType, aSourceFile, aScriptSample, aLineNum, aNonce, aContent) { - for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) { - let policy = this._policies[policyIndex]; - - // call-sites to the eval/inline checks recieve two return values: allows - // and violates. Policies that are report-only allow the - // loads/compilations but violations should still be reported. Not all - // policies in this nsIContentSecurityPolicy instance will be violated, - // which is why we must check again here. - switch (aViolationType) { - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE: - if (!policy.allowsInlineStyles) { - var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT: - if (!policy.allowsInlineScripts) { - var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL: - if (!policy.allowsEvalInScripts) { - var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - EVAL_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_NONCE_SCRIPT: - var scriptType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_SCRIPT]; - if (!policy.permitsNonce(aNonce, scriptType)) { - var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - SCRIPT_NONCE_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_NONCE_STYLE: - var styleType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_STYLE]; - if (!policy.permitsNonce(aNonce, styleType)) { - var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - STYLE_NONCE_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_HASH_SCRIPT: - var scriptType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_SCRIPT]; - if (!policy.permitsHash(aContent, scriptType)) { - var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - SCRIPT_HASH_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_HASH_STYLE: - var styleType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_STYLE]; - if (!policy.permitsHash(aContent, styleType)) { - var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC', policy); - this._asyncReportViolation('self', null, violatedDirective, policyIndex, - STYLE_HASH_VIOLATION_OBSERVER_SUBJECT, - aSourceFile, aScriptSample, aLineNum); - } - break; - } - } - }, - - /** - * Given an nsIHttpChannel, fill out the appropriate data. - */ - setRequestContext: - function(aSelfURI, aReferrerURI, aPrincipal, aChannel) { - - // this requires either a self URI or a http channel. - if (!aSelfURI && !aChannel) - return; - - if (aChannel) { - // Save the docRequest for fetching a policy-uri - this._weakDocRequest = Cu.getWeakReference(aChannel); - } - - // save the document URI (minus ) and referrer for reporting - let uri = aSelfURI ? aSelfURI.cloneIgnoringRef() : aChannel.URI.cloneIgnoringRef(); - try { // GetUserPass throws for some protocols without userPass - uri.userPass = ''; - } catch (ex) {} - this._request = uri.asciiSpec; - this._requestOrigin = uri; - - // store a reference to the principal, that can later be used in shouldLoad - if (aPrincipal) { - this._weakRequestPrincipal = Cu.getWeakReference(aPrincipal); - } else if (aChannel) { - this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager) - .getChannelPrincipal(aChannel)); - } else { - CSPdebug("No principal or channel for document context; violation reports may not work."); - } - - // pick one: referrerURI, channel's referrer, or null (first available) - let ref = null; - if (aReferrerURI) - ref = aReferrerURI; - else if (aChannel instanceof Ci.nsIHttpChannel) - ref = aChannel.referrer; - - if (ref) { - let referrer = aChannel.referrer.cloneIgnoringRef(); - try { // GetUserPass throws for some protocols without userPass - referrer.userPass = ''; - } catch (ex) {} - this._referrer = referrer.asciiSpec; - } - }, - -/* ........ Methods .............. */ - - /** - * Adds a new policy to our list of policies for this CSP context. - * @returns the count of policies. - */ - appendPolicy: - function csp_appendPolicy(aPolicy, selfURI, aReportOnly) { - return this._appendPolicyInternal(aPolicy, selfURI, aReportOnly, true); - }, - - /** - * Adds a new policy to our list of policies for this CSP context. - * Only to be called from this module (not exported) - * @returns the count of policies. - */ - _appendPolicyInternal: - function csp_appendPolicy(aPolicy, selfURI, aReportOnly, aEnforceSelfChecks) { -#ifndef MOZ_B2G - CSPdebug("APPENDING POLICY: " + aPolicy); - CSPdebug(" SELF: " + (selfURI ? selfURI.asciiSpec : " null")); -#endif - - // For nested schemes such as view-source: make sure we are taking the - // innermost URI to use as 'self' since that's where we will extract the - // scheme, host and port from - if (selfURI instanceof Ci.nsINestedURI) { -#ifndef MOZ_B2G - CSPdebug(" INNER: " + selfURI.innermostURI.asciiSpec); -#endif - selfURI = selfURI.innermostURI; - } - - // stay uninitialized until policy setup is done - var newpolicy; - - // If there is a policy-uri, fetch the policy, then re-call this function. - // (1) parse and create a CSPRep object - // Note that we pass the full URI since when it's parsed as 'self' to construct a - // CSPSource only the scheme, host, and port are kept. - - newpolicy = CSPRep.fromString(aPolicy, selfURI, aReportOnly, - this._weakDocRequest.get(), - this, aEnforceSelfChecks); - newpolicy._isInitialized = true; - this._policies.push(newpolicy); - this._cache.clear(); // reset cache since effective policy changes - }, - - /** - * Removes a policy from the array of policies. - */ - removePolicy: - function csp_removePolicy(index) { - if (index < 0 || index >= this._policies.length) { - CSPdebug("Cannot remove policy " + index + "; not enough policies."); - return; - } - this._policies.splice(index, 1); - this._cache.clear(); // reset cache since effective policy changes - }, - - /** - * Generates and sends a violation report to the specified report URIs. - */ - sendReports: - function(blockedUri, originalUri, violatedDirective, - violatedPolicyIndex, aSourceFile, - aScriptSample, aLineNum) { - - let policy = this._getPolicyInternal(violatedPolicyIndex); - if (!policy) { - CSPdebug("ERROR in SendReports: policy " + violatedPolicyIndex + " is not defined."); - return; - } - - var uriString = policy.getReportURIs(); - var uris = uriString.split(/\s+/); - if (uris.length > 0) { - // see if we need to sanitize the blocked-uri - let blocked = ''; - if (originalUri) { - // We've redirected, only report the blocked origin - try { - let clone = blockedUri.clone(); - clone.path = ''; - blocked = clone.asciiSpec; - } catch(e) { - CSPdebug(".... blockedUri can't be cloned: " + blockedUri); - } - } - else if (blockedUri instanceof Ci.nsIURI) { - blocked = blockedUri.cloneIgnoringRef().asciiSpec; - } - else { - // blockedUri is a string for eval/inline-script violations - blocked = blockedUri; - } - - // Generate report to send composed of - // { - // csp-report: { - // document-uri: "http://example.com/file.html?params", - // referrer: "...", - // blocked-uri: "...", - // violated-directive: "..." - // } - // } - var report = { - 'csp-report': { - 'document-uri': this._request, - 'referrer': this._referrer, - 'blocked-uri': blocked, - 'violated-directive': violatedDirective - } - } - - // extra report fields for script errors (if available) - if (originalUri) - report["csp-report"]["original-uri"] = originalUri.cloneIgnoringRef().asciiSpec; - if (aSourceFile) - report["csp-report"]["source-file"] = aSourceFile; - if (aScriptSample) - report["csp-report"]["script-sample"] = aScriptSample; - if (aLineNum) - report["csp-report"]["line-number"] = aLineNum; - - var reportString = JSON.stringify(report); - CSPdebug("Constructed violation report:\n" + reportString); - - // For each URI in the report list, send out a report. - // We make the assumption that all of the URIs are absolute URIs; this - // should be taken care of in CSPRep.fromString (where it converts any - // relative URIs into absolute ones based on "self"). - for (let i in uris) { - if (uris[i] === "") - continue; - - try { - var chan = Services.io.newChannel(uris[i], null, null); - if (!chan) { - CSPdebug("Error creating channel for " + uris[i]); - continue; - } - - var content = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - content.data = reportString + "\n\n"; - - // make sure this is an anonymous request (no cookies) so in case the - // policy URI is injected, it can't be abused for CSRF. - chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS; - - // we need to set an nsIChannelEventSink on the channel object - // so we can tell it to not follow redirects when posting the reports - chan.notificationCallbacks = new CSPReportRedirectSink(policy); - if (this._weakDocRequest.get()) { - chan.loadGroup = this._weakDocRequest.get().loadGroup; - } - - chan.QueryInterface(Ci.nsIUploadChannel) - .setUploadStream(content, "application/json", content.available()); - - try { - // if this is an HTTP channel, set the request method to post - chan.QueryInterface(Ci.nsIHttpChannel); - chan.requestMethod = "POST"; - } catch(e) {} // throws only if chan is not an nsIHttpChannel. - - // check with the content policy service to see if we're allowed to - // send this request. - try { - var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"] - .getService(Ci.nsIContentPolicy); - if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_CSP_REPORT, - chan.URI, this._requestOrigin, - null, null, null, this._weakRequestPrincipal.get()) - != Ci.nsIContentPolicy.ACCEPT) { - continue; // skip unauthorized URIs - } - } catch(e) { - continue; // refuse to load if we can't do a security check. - } - - //send data (and set up error notifications) - chan.asyncOpen(new CSPViolationReportListener(uris[i]), null); - CSPdebug("Sent violation report to " + uris[i]); - } catch(e) { - // it's possible that the URI was invalid, just log a - // warning and skip over that. - policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]])); - policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorWas", [e.toString()])); - } - } - } - }, - - /** - * Logs a meaningful CSP warning to the developer console. - */ - logToConsole: - function(blockedUri, originalUri, violatedDirective, aViolatedPolicyIndex, - aSourceFile, aScriptSample, aLineNum, aObserverSubject) { - let policy = this._policies[aViolatedPolicyIndex]; - switch(aObserverSubject.data) { - case INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT: - violatedDirective = CSPLocalizer.getStr("inlineStyleBlocked"); - break; - case INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT: - violatedDirective = CSPLocalizer.getStr("inlineScriptBlocked"); - break; - case EVAL_VIOLATION_OBSERVER_SUBJECT: - violatedDirective = CSPLocalizer.getStr("scriptFromStringBlocked"); - break; - } - var violationMessage = null; - if (blockedUri && blockedUri["asciiSpec"]) { - let localizeString = policy._reportOnlyMode ? "CSPROViolationWithURI" : "CSPViolationWithURI"; - violationMessage = CSPLocalizer.getFormatStr(localizeString, [violatedDirective, blockedUri.asciiSpec]); - } else { - let localizeString = policy._reportOnlyMode ? "CSPROViolation" : "CSPViolation"; - violationMessage = CSPLocalizer.getFormatStr(localizeString, [violatedDirective]); - } - policy.log(WARN_FLAG, violationMessage, - (aSourceFile) ? aSourceFile : null, - (aScriptSample) ? aScriptSample : null, - (aLineNum) ? aLineNum : null); - }, - -/** - * Exposed Method to analyze docShell for approved frame ancestry. - * NOTE: Also sends violation reports if necessary. - * @param docShell - * the docShell for this policy's resource. - * @return - * true if the frame ancestry is allowed by this policy and the load - * should progress. - */ - permitsAncestry: - function(docShell) { - // Cannot shortcut checking all the policies since violation reports have - // to be triggered if any policy wants it. - var permitted = true; - for (let i = 0; i < this._policies.length; i++) { - // spec says don't check the policies that are report-only (monitored) - if (this._policies[i]._reportOnlyMode) { - continue; - } - if (!this._permitsAncestryInternal(docShell, this._policies[i], i)) { - permitted = false; - } - } - return permitted; - }, - - _permitsAncestryInternal: - function(docShell, policy, policyIndex) { - if (!docShell) { return false; } - - // walk up this docShell tree until we hit chrome - var dst = docShell.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShellTreeItem); - - // collect ancestors and make sure they're allowed. - var ancestors = []; - while (dst.parent) { - dst = dst.parent; - let it = dst.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation); - if (it.currentURI) { - if (it.currentURI.scheme === "chrome") { - break; - } - // delete any userpass - let ancestor = it.currentURI.cloneIgnoringRef(); - try { // GetUserPass throws for some protocols without userPass - ancestor.userPass = ''; - } catch (ex) {} - -#ifndef MOZ_B2G - CSPdebug(" found frame ancestor " + ancestor.asciiSpec); -#endif - ancestors.push(ancestor); - } - } - - // scan the discovered 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.FRAME_ANCESTORS; - for (let i in ancestors) { - let ancestor = ancestors[i]; - if (!policy.permits(ancestor, cspContext)) { - // report the frame-ancestor violation - let directive = policy._directives[cspContext]; - let violatedPolicy = 'frame-ancestors ' + directive.toString(); - - // spec says don't report ancestors for cross-origin violations (it is - // a violation of same-origin) - let ssm = Services.scriptSecurityManager; - let blockedURI = null; - try { - if (Services.scriptSecurityManager - .checkSameOriginURI(ancestors[i], this._requestOrigin, false)) { - blockedURI = ancestors[i]; - } - } catch (ex) { - // cross-origin, don't send the ancestor - } - - this._asyncReportViolation(blockedURI, null, violatedPolicy, - policyIndex); - - // need to lie if we are testing in report-only mode - return policy._reportOnlyMode; - } - } - return true; - }, - - /** - * Creates a cache key from content location and content type. - */ - _createCacheKey: - function (aContentLocation, aContentType) { - if (aContentType != Ci.nsIContentPolicy.TYPE_SCRIPT && - aContentLocation.scheme == "data") { - // For non-script data: URI, use ("data:", aContentType) as the cache key. - return aContentLocation.scheme + ":" + aContentType; - } - - let uri = aContentLocation.spec; - if (uri.length > CSP_CACHE_URI_CUTOFF_SIZE) { - // Don't cache for a URI longer than the cutoff size. - return null; - } - return uri + "!" + aContentType; - }, - - /** - * Delegate method called by the service when sub-elements of the protected - * document are being loaded. Given a bit of information about the request, - * decides whether or not the policy is satisfied. - */ - shouldLoad: - function csp_shouldLoad(aContentType, - aContentLocation, - aRequestOrigin, - aContext, - aMimeTypeGuess, - aOriginalUri) { - let key = this._createCacheKey(aContentLocation, aContentType); - if (key && this._cache.has(key)) { - return this._cache.get(key); - } - -#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 - - // 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; - - // Infer if this is a preload for elements that use nonce-source. Since, - // for preloads, aContext is the document and not the element associated - // with the resource, we cannot determine the nonce. See Bug 612921 and - // Bug 855326. - let nonceSourceValid = aContentType == cp.TYPE_SCRIPT || - aContentType == cp.TYPE_STYLESHEET; - var possiblePreloadNonceConflict = nonceSourceValid && - aContext instanceof Ci.nsIDOMHTMLDocument; - - // iterate through all the _policies and send reports where a policy is - // violated. After the check, determine the overall effect (blocked or - // loaded?) and cache it. - let policyAllowsLoadArray = []; - for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) { - let policy = this._policies[policyIndex]; - - cspContext = ContentSecurityPolicy._MAPPINGS[aContentType]; - -#ifndef MOZ_B2G - CSPdebug("policy is " + (policy._reportOnlyMode ? - "report-only" : "blocking")); - CSPdebug("shouldLoad cspContext = " + cspContext); -#endif - - // if the mapping is null, there's no policy, let it through. - if (!cspContext) { - return Ci.nsIContentPolicy.ACCEPT; - } - - // check if location is permitted - let permitted = policy.permits(aContentLocation, cspContext); - - // check any valid content type for nonce if location is not permitted - if (!permitted && nonceSourceValid && - aContext instanceof Ci.nsIDOMHTMLElement && - aContext.hasAttribute('nonce')) { - permitted = policy.permitsNonce(aContext.getAttribute('nonce'), - cspContext); - } - - // record whether the thing should be blocked or just reported. - policyAllowsLoadArray.push(permitted || policy._reportOnlyMode); - let res = permitted ? cp.ACCEPT : cp.REJECT_SERVER; - - // frame-ancestors is taken care of early on (as this document is loaded) - - // If the result is *NOT* ACCEPT, then send report - // Do not send report if this is a nonce-source preload - the decision may - // be wrong and will incorrectly fail the unit tests. - if (res != cp.ACCEPT && !possiblePreloadNonceConflict) { - CSPdebug("blocking request for " + aContentLocation.asciiSpec); - try { - let directive = "unknown directive", - violatedPolicy = "unknown policy"; - - // The policy might not explicitly declare each source directive (so - // the cspContext may be implicit). If so, we have to report - // violations as appropriate: specific or the default-src directive. - if (policy._directives.hasOwnProperty(cspContext)) { - directive = policy._directives[cspContext]; - violatedPolicy = cspContext + ' ' + directive.toString(); - } else if (policy._directives.hasOwnProperty("default-src")) { - directive = policy._directives["default-src"]; - violatedPolicy = "default-src " + directive.toString(); - } else { - violatedPolicy = "unknown directive"; - CSPdebug('ERROR in blocking content: ' + - 'CSP is not sure which part of the policy caused this block'); - } - - this._asyncReportViolation(aContentLocation, - aOriginalUri, - violatedPolicy, - policyIndex); - } catch(e) { - CSPdebug('---------------- ERROR: ' + e); - } - } - } // end for-each loop over policies - - // the ultimate decision is based on whether any policies want to reject - // the load. The array keeps track of whether the policies allowed the - // loads. If any doesn't, we'll reject the load (and cache the result). - let ret = (policyAllowsLoadArray.some(function(a,b) { return !a; }) ? - cp.REJECT_SERVER : cp.ACCEPT); - - // Do not cache the result if this is a nonce-source preload - if (key && !possiblePreloadNonceConflict) { - this._cache.set(key, ret); - } - return ret; - }, - - shouldProcess: - function csp_shouldProcess(aContentType, - aContentLocation, - aRequestOrigin, - aContext, - aMimeType, - aExtra) { - // frame-ancestors check is done outside the ContentPolicy - var res = Ci.nsIContentPolicy.ACCEPT; - CSPdebug("shouldProcess aContext=" + aContext); - return res; - }, - - /** - * Asynchronously notifies any nsIObservers listening to the CSP violation - * topic that a violation occurred. Also triggers report sending. All - * asynchronous on the main thread. - * - * @param aBlockedContentSource - * Either a CSP Source (like 'self', as string) or nsIURI: the source - * of the violation. - * @param aOriginalUri - * The original URI if the blocked content is a redirect, else null - * @param aViolatedDirective - * the directive that was violated (string). - * @param aViolatedPolicyIndex - * the index of the policy that was violated (so we know where to send - * the reports). - * @param aObserverSubject - * optional, subject sent to the nsIObservers listening to the CSP - * violation topic. - * @param aSourceFile - * name of the file containing the inline script violation - * @param aScriptSample - * a sample of the violating inline script - * @param aLineNum - * source line number of the violation (if available) - */ - _asyncReportViolation: - function(aBlockedContentSource, aOriginalUri, aViolatedDirective, - aViolatedPolicyIndex, aObserverSubject, - aSourceFile, aScriptSample, aLineNum) { - // if optional observerSubject isn't specified, default to the source of - // the violation. - if (!aObserverSubject) - aObserverSubject = aBlockedContentSource; - - // gotta wrap things that aren't nsISupports, since it's sent out to - // observers as such. Objects that are not nsISupports are converted to - // strings and then wrapped into a nsISupportsCString. - if (!(aObserverSubject instanceof Ci.nsISupports)) { - let d = aObserverSubject; - aObserverSubject = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); - aObserverSubject.data = d; - } - - var reportSender = this; - Services.tm.mainThread.dispatch( - function() { - Services.obs.notifyObservers(aObserverSubject, - CSP_VIOLATION_TOPIC, - aViolatedDirective); - reportSender.sendReports(aBlockedContentSource, aOriginalUri, - aViolatedDirective, aViolatedPolicyIndex, - aSourceFile, aScriptSample, aLineNum); - reportSender.logToConsole(aBlockedContentSource, aOriginalUri, - aViolatedDirective, aViolatedPolicyIndex, - aSourceFile, aScriptSample, - aLineNum, aObserverSubject); - - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - -/* ........ nsISerializable Methods: .............. */ - - read: - function(aStream) { - - this._requestOrigin = aStream.readObject(true); - this._requestOrigin.QueryInterface(Ci.nsIURI); - - for (let pCount = aStream.read32(); pCount > 0; pCount--) { - let polStr = aStream.readString(); - let reportOnly = aStream.readBoolean(); - // don't need self info because when the policy is turned back into a - // string, 'self' is replaced with the explicit source expression. - this._appendPolicyInternal(polStr, null, reportOnly, false); - } - - // NOTE: the document instance that's deserializing this object (via its - // principal) should hook itself into this._principal manually. If they - // don't, the CSP reports will likely be blocked by nsMixedContentBlocker. - }, - - write: - function(aStream) { - // need to serialize the context: request origin and such. They are - // used when sending reports. Since _request and _requestOrigin are just - // different representations of the same thing, only save _requestOrigin - // (an nsIURI). - aStream.writeCompoundObject(this._requestOrigin, Ci.nsIURI, true); - - // we can't serialize a reference to the principal that triggered this - // instance to serialize, so when this is deserialized by the principal the - // caller must hook it up manually by calling setRequestContext on this - // instance with the appropriate nsIChannel. - - // Finally, serialize all the policies. - aStream.write32(this._policies.length); - - for each (var policy in this._policies) { - aStream.writeWStringZ(policy.toString()); - aStream.writeBoolean(policy._reportOnlyMode); - } - }, -}; - -// The POST of the violation report (if it happens) should not follow -// redirects, per the spec. hence, we implement an nsIChannelEventSink -// with an object so we can tell XHR to abort if a redirect happens. -function CSPReportRedirectSink(policy) { - this._policy = policy; -} - -CSPReportRedirectSink.prototype = { - QueryInterface: function requestor_qi(iid) { - if (iid.equals(Ci.nsISupports) || - iid.equals(Ci.nsIInterfaceRequestor) || - iid.equals(Ci.nsIChannelEventSink)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - // nsIInterfaceRequestor - getInterface: function requestor_gi(iid) { - if (iid.equals(Ci.nsIChannelEventSink)) - return this; - - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - // nsIChannelEventSink - asyncOnChannelRedirect: function channel_redirect(oldChannel, newChannel, - flags, callback) { - this._policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec])); - - // cancel the old channel so XHR failure callback happens - oldChannel.cancel(Cr.NS_ERROR_ABORT); - - // notify an observer that we have blocked the report POST due to a redirect, - // used in testing, do this async since we're in an async call now to begin with - Services.tm.mainThread.dispatch( - function() { - observerSubject = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); - observerSubject.data = oldChannel.URI.asciiSpec; - - Services.obs.notifyObservers(observerSubject, - CSP_VIOLATION_TOPIC, - "denied redirect while sending violation report"); - }, Ci.nsIThread.DISPATCH_NORMAL); - - // throw to stop the redirect happening - throw Cr.NS_BINDING_REDIRECTED; - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentSecurityPolicy]); diff --git a/content/base/src/contentSecurityPolicy.manifest b/content/base/src/contentSecurityPolicy.manifest deleted file mode 100644 index 233f2a39a80..00000000000 --- a/content/base/src/contentSecurityPolicy.manifest +++ /dev/null @@ -1,2 +0,0 @@ -component {d1680bb4-1ac0-4772-9437-1188375e44f2} contentSecurityPolicy.js -contract @mozilla.org/contentsecuritypolicy;1 {d1680bb4-1ac0-4772-9437-1188375e44f2} diff --git a/content/base/src/moz.build b/content/base/src/moz.build index e92bd4844cb..8ae00098f64 100644 --- a/content/base/src/moz.build +++ b/content/base/src/moz.build @@ -198,19 +198,10 @@ SOURCES += [ EXTRA_COMPONENTS += [ 'contentAreaDropListener.js', 'contentAreaDropListener.manifest', - 'contentSecurityPolicy.manifest', 'messageWakeupService.js', 'messageWakeupService.manifest', ] -EXTRA_PP_COMPONENTS += [ - 'contentSecurityPolicy.js', -] - -EXTRA_JS_MODULES += [ - 'CSPUtils.jsm', -] - FAIL_ON_WARNINGS = True MSVC_ENABLE_PGO = True diff --git a/content/base/src/nsCSPService.cpp b/content/base/src/nsCSPService.cpp index d48018b6b60..0b1aafc5e72 100644 --- a/content/base/src/nsCSPService.cpp +++ b/content/base/src/nsCSPService.cpp @@ -28,7 +28,6 @@ using namespace mozilla; /* Keeps track of whether or not CSP is enabled */ bool CSPService::sCSPEnabled = true; -bool CSPService::sNewBackendEnabled = true; #ifdef PR_LOGGING static PRLogModuleInfo* gCspPRLog; @@ -37,7 +36,6 @@ static PRLogModuleInfo* gCspPRLog; CSPService::CSPService() { Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable"); - Preferences::AddBoolVarCache(&sNewBackendEnabled, "security.csp.newbackend.enable"); #ifdef PR_LOGGING if (!gCspPRLog) @@ -100,8 +98,9 @@ CSPService::ShouldLoad(uint32_t aContentType, // These content types are not subject to CSP content policy checks: - // TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT - // (their mappings are null in contentSecurityPolicy.js) + // TYPE_CSP_REPORT -- csp can't block csp reports + // TYPE_REFRESH -- never passed to ShouldLoad (see nsIContentPolicy.idl) + // TYPE_DOCUMENT -- used for frame-ancestors if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT || aContentType == nsIContentPolicy::TYPE_REFRESH || aContentType == nsIContentPolicy::TYPE_DOCUMENT) { diff --git a/content/base/src/nsCSPService.h b/content/base/src/nsCSPService.h index 0654123eec2..17db70f650d 100644 --- a/content/base/src/nsCSPService.h +++ b/content/base/src/nsCSPService.h @@ -26,7 +26,6 @@ public: CSPService(); static bool sCSPEnabled; - static bool sNewBackendEnabled; protected: virtual ~CSPService(); diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 4e147b33da6..a1c701cbc64 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -2764,17 +2764,7 @@ nsDocument::InitCSP(nsIChannel* aChannel) } } - // Create new CSP object: - // * by default we are trying to use the new C++ implmentation - // * however, we still support XCSP headers during the transition phase - // and fall back to the JS implementation if we find an XCSP header. - - if (CSPService::sNewBackendEnabled) { - csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv); - } - else { - csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv); - } + csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv); if (NS_FAILED(rv)) { #ifdef PR_LOGGING diff --git a/content/base/test/unit/test_csp_ignores_path.js b/content/base/test/unit/test_csp_ignores_path.js deleted file mode 100644 index d76e27fcaef..00000000000 --- a/content/base/test/unit/test_csp_ignores_path.js +++ /dev/null @@ -1,159 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import('resource://gre/modules/CSPUtils.jsm'); - -var ioService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); -var self = ioService.newURI("http://test1.example.com:80", null, null); - -function testValidSRCsHostSourceWithSchemeAndPath() { - var csps = [ - "http://test1.example.com", - "http://test1.example.com/", - "http://test1.example.com/path-1", - "http://test1.example.com/path-1/", - "http://test1.example.com/path-1/path_2/", - "http://test1.example.com/path-1/path_2/file.js", - "http://test1.example.com/path-1/path_2/file_1.js", - "http://test1.example.com/path-1/path_2/file-2.js", - "http://test1.example.com/path-1/path_2/f.js", - "http://test1.example.com/path-1/path_2/f.oo.js" - ] - - var obj; - var expected = "http://test1.example.com"; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - dump("expected: " + expected + "\n"); - dump("got: " + obj._sources[0] + "\n"); - do_check_eq(1, obj._sources.length); - do_check_eq(obj._sources[0], expected); - } -} - -function testValidSRCsRegularHost() { - var csps = [ - "test1.example.com", - "test1.example.com/", - "test1.example.com/path-1", - "test1.example.com/path-1/", - "test1.example.com/path-1/path_2/", - "test1.example.com/path-1/path_2/file.js", - "test1.example.com/path-1/path_2/file_1.js", - "test1.example.com/path-1/path_2/file-2.js", - "test1.example.com/path-1/path_2/f.js", - "test1.example.com/path-1/path_2/f.oo.js" - ] - - var obj; - var expected = "http://test1.example.com"; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - do_check_eq(1, obj._sources.length); - do_check_eq(obj._sources[0], expected); - } -} - -function testValidSRCsWildCardHost() { - var csps = [ - "*.example.com", - "*.example.com/", - "*.example.com/path-1", - "*.example.com/path-1/", - "*.example.com/path-1/path_2/", - "*.example.com/path-1/path_2/file.js", - "*.example.com/path-1/path_2/file_1.js", - "*.example.com/path-1/path_2/file-2.js", - "*.example.com/path-1/path_2/f.js", - "*.example.com/path-1/path_2/f.oo.js" - ] - - var obj; - var expected = "http://*.example.com"; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - do_check_eq(1, obj._sources.length); - do_check_eq(obj._sources[0], expected); - } -} - -function testValidSRCsRegularPort() { - var csps = [ - "test1.example.com:80", - "test1.example.com:80/", - "test1.example.com:80/path-1", - "test1.example.com:80/path-1/", - "test1.example.com:80/path-1/path_2", - "test1.example.com:80/path-1/path_2/", - "test1.example.com:80/path-1/path_2/file.js", - "test1.example.com:80/path-1/path_2/f.ile.js" - ] - - var obj; - var expected = "http://test1.example.com"; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - do_check_eq(1, obj._sources.length); - do_check_eq(obj._sources[0], expected); - } -} - -function testValidSRCsWildCardPort() { - var csps = [ - "test1.example.com:*", - "test1.example.com:*/", - "test1.example.com:*/path-1", - "test1.example.com:*/path-1/", - "test1.example.com:*/path-1/path_2", - "test1.example.com:*/path-1/path_2/", - "test1.example.com:*/path-1/path_2/file.js", - "test1.example.com:*/path-1/path_2/f.ile.js" - ] - - var obj; - var expected = "http://test1.example.com:*"; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - do_check_eq(1, obj._sources.length); - do_check_eq(obj._sources[0], expected); - } -} - - -function testInvalidSRCs() { - var csps = [ - "test1.example.com:88path-1/", - "test1.example.com:80.js", - "test1.example.com:*.js", - "test1.example.com:*." - ] - - var obj; - var expected = []; - for (let i in csps) { - var src = csps[i]; - obj = CSPSourceList.fromString(src, undefined, self); - do_check_eq(0, obj._sources.length); - } -} - -function run_test() { - testValidSRCsHostSourceWithSchemeAndPath(); - testValidSRCsRegularHost(); - testValidSRCsWildCardHost(); - testValidSRCsRegularPort(); - testValidSRCsWildCardPort(); - testInvalidSRCs(); - do_test_finished(); -} diff --git a/content/base/test/unit/test_csputils.js b/content/base/test/unit/test_csputils.js deleted file mode 100644 index 81e3242e373..00000000000 --- a/content/base/test/unit/test_csputils.js +++ /dev/null @@ -1,879 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -//load('CSPUtils.jsm'); -Cu.import('resource://gre/modules/CSPUtils.jsm'); -Cu.import('resource://gre/modules/NetUtil.jsm'); - -var httpServer = new HttpServer(); -httpServer.start(-1); - -const POLICY_FROM_URI = "default-src 'self'; img-src *"; -const POLICY_PORT = httpServer.identity.primaryPort; -const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy"; -const POLICY_URI_RELATIVE = "/policy"; - -//converts string to nsIURI -function URI(uriString) { - var ioService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - return ioService.newURI(uriString, null, null); -} - - -// helper to assert that an array has the given value somewhere. -function do_check_in_array(arr, val, stack) { - if (!stack) - stack = Components.stack.caller; - - var text = val + " in [" + arr.join(",") + "]"; - - for(var i in arr) { - //dump(".......... " + i + "> " + arr[i] + "\n"); - if(arr[i] == val) { - //succeed - ++_passedChecks; - dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " + - stack.lineNumber + "] " + text + "\n"); - return; - } - } - do_throw(text, stack); -} - -// helper to assert that an object or array must have a given key -function do_check_has_key(foo, key, stack) { - if (!stack) - stack = Components.stack.caller; - - var keys = []; - for (let k in foo) { keys.push(k); } - var text = key + " in [" + keys.join(",") + "]"; - - for (var x in foo) { - if (x == key) { - //succeed - ++_passedChecks; - dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " + - stack.lineNumber + "] " + text + "\n"); - return; - } - } - do_throw(text, stack); -} - -// helper to use .equals on stuff -function do_check_equivalent(foo, bar, stack) { - if (!stack) - stack = Components.stack.caller; - - var text = foo + ".equals(" + bar + ")"; - - if(foo.equals && foo.equals(bar)) { - ++_passedChecks; - dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " + - stack.lineNumber + "] " + text + "\n"); - return; - } - do_throw(text, stack); -} - -var tests = []; -function test(fcn) { - tests.push(fcn); -} - -test( - function test_CSPHost_fromstring() { - var h; - - h = CSPHost.fromString("*"); - do_check_neq(null, h); // "* lone wildcard should work" - - h = CSPHost.fromString("foo.bar"); - do_check_neq(null, h); // "standard tuple failed" - - h = CSPHost.fromString("*.bar"); - do_check_neq(null, h); // "wildcard failed" - - h = CSPHost.fromString("foo.*.bar"); - do_check_eq(null, h); // "wildcard in wrong place worked" - - h = CSPHost.fromString("com"); - do_check_neq(null, h); // "lone symbol should not fail" - - h = CSPHost.fromString("f00b4r.com"); - do_check_neq(null, h); // "Numbers in hosts should work" - - h = CSPHost.fromString("foo-bar.com"); - do_check_neq(null, h); // "dashes in hosts should work" - - - h = CSPHost.fromString("foo!bar.com"); - do_check_eq(null, h); // "special chars in hosts should fail" - - h = CSPHost.fromString("{app-url-is-uid}"); - do_check_neq(null, h); // "Packaged apps URLs failed" - }); - -test( - function test_CSPHost_clone() { - h = CSPHost.fromString("*.a.b.c"); - h2 = h.clone(); - for(var i in h._segments) { - // "cloned segments should match" - do_check_eq(h._segments[i], h2._segments[i]); - } - }); - -test( - function test_CSPHost_permits() { - var h = CSPHost.fromString("*.b.c"); - var h2 = CSPHost.fromString("a.b.c"); - do_check_true( h.permits(h2)); //"CSPHost *.b.c should allow CSPHost a.b.c" - do_check_true( h.permits("a.b.c")); //"CSPHost *.b.c should allow string a.b.c" - do_check_false(h.permits("b.c")); //"CSPHost *.b.c should not allow string b.c" - do_check_false(h.permits("a.a.c")); //"CSPHost *.b.c should not allow string a.a.c" - do_check_false(h2.permits(h)); //"CSPHost a.b.c should not allow CSPHost *.b.c" - do_check_false(h2.permits("b.c")); //"CSPHost a.b.c should not allow string b.c" - do_check_true( h2.permits("a.b.c")); //"CSPHost a.b.c should allow string a.b.c" - }); - - -///////////////////// Test the Source object ////////////////////// - -test( - function test_CSPSource_fromString() { - // can't do these tests because "self" is not defined. - //"basic source should not be null."); - do_check_neq(null, CSPSource.fromString("a.com", undefined, "http://abc.com")); - - //"ldh characters should all work for host."); - do_check_neq(null, CSPSource.fromString("a2-c.com", undefined, "https://a.com")); - - //"wildcard should work in first token for host."); - do_check_neq(null, CSPSource.fromString("*.a.com", undefined, "http://abc.com")); - - //print(" --- Ignore the following two errors if they print ---"); - //"wildcard should not work in non-first token for host."); - do_check_eq(null, CSPSource.fromString("x.*.a.com", undefined, "http://a.com")); - - //"funny characters (#) should not work for host."); - do_check_eq(null, CSPSource.fromString("a#2-c.com", undefined, "http://a.com")); - - //print(" --- Stop ignoring errors that print ---\n"); - - //"failed to parse host with port."); - do_check_neq(null, CSPSource.create("a.com:23", undefined, "http://a.com")); - //"failed to parse host with scheme."); - do_check_neq(null, CSPSource.create("https://a.com", undefined, "http://a.com")); - //"failed to parse host with scheme and port."); - do_check_neq(null, CSPSource.create("https://a.com:200", undefined, "http://a.com")); - - //Check to make sure we don't match multiple instances with regex - do_check_eq(null, CSPSource.create("http://foo.com:bar.com:23")); - //Port parsing should work for all schemes - do_check_neq(null, CSPSource.create("data:")); - do_check_neq(null, CSPSource.create("javascript:")); - - //"app:// URLs should work, including the {} characters."); - do_check_neq(null, CSPSource.fromString("{app-host-is-uid}", undefined, "app://{app-host-is-uid}")); - }); - -test( - function test_CSPSource_fromString_withSelf() { - var src; - src = CSPSource.create("a.com", undefined, "https://foobar.com:443"); - //"src should inherit port * - do_check_true(src.permits("https://a.com:443")); - //"src should inherit and require https scheme - do_check_false(src.permits("http://a.com")); - //"src should inherit scheme 'https'" - do_check_true(src.permits("https://a.com")); - - src = CSPSource.create("http://a.com", undefined, "https://foobar.com:443"); - //"src should inherit and require http scheme" - do_check_false(src.permits("https://a.com")); - //"src should inherit scheme 'http'" - do_check_true(src.permits("http://a.com")); - //"src should inherit port and scheme from parent" - //"src should inherit default port for 'http'" - do_check_true(src.permits("http://a.com:80")); - - src = CSPSource.create("'self'", undefined, "https://foobar.com:443"); - //"src should inherit port * - do_check_true(src.permits("https://foobar.com:443")); - //"src should inherit and require https scheme - do_check_false(src.permits("http://foobar.com")); - //"src should inherit scheme 'https'" - do_check_true(src.permits("https://foobar.com")); - //"src should reject other hosts" - do_check_false(src.permits("https://a.com")); - - src = CSPSource.create("javascript:", undefined, "https://foobar.com:443"); - //"hostless schemes should be parseable." - var aUri = NetUtil.newURI("javascript:alert('foo');"); - do_check_true(src.permits(aUri)); - //"src should reject other hosts" - do_check_false(src.permits("https://a.com")); - //"nothing else should be allowed" - do_check_false(src.permits("https://foobar.com")); - - src = CSPSource.create("{app-host-is-uid}", undefined, "app://{app-host-is-uid}"); - //"src should inherit and require 'app' scheme" - do_check_false(src.permits("https://{app-host-is-uid}")); - //"src should inherit scheme 'app'" - do_check_true(src.permits("app://{app-host-is-uid}")); - - }); - -///////////////////// Test the source list ////////////////////// - -test( - function test_CSPSourceList_fromString() { - var sd = CSPSourceList.fromString("'none'"); - //"'none' -- should parse" - do_check_neq(null,sd); - // "'none' should be a zero-length list" - do_check_eq(0, sd._sources.length); - do_check_true(sd.isNone()); - - sd = CSPSourceList.fromString("*"); - //"'*' should be a zero-length list" - do_check_eq(0, sd._sources.length); - - //print(" --- Ignore the following three errors if they print ---"); - //"funny char in host" - do_check_true(CSPSourceList.fromString("f!oo.bar").isNone()); - //"funny char in scheme" - do_check_true(CSPSourceList.fromString("ht!ps://f-oo.bar").isNone()); - //"funny char in port" - do_check_true(CSPSourceList.fromString("https://f-oo.bar:3f").isNone()); - //print(" --- Stop ignoring errors that print ---\n"); - }); - -test( - function test_CSPSourceList_fromString_twohost() { - var str = "foo.bar:21 https://ras.bar"; - var parsed = "http://foo.bar:21 https://ras.bar"; - var sd = CSPSourceList.fromString(str, undefined, URI("http://self.com:80")); - //"two-host list should parse" - do_check_neq(null,sd); - //"two-host list should parse to two hosts" - do_check_eq(2, sd._sources.length); - //"two-host list should contain original data" - do_check_eq(parsed, sd.toString()); - }); - -test( - function test_CSPSourceList_permits() { - var nullSourceList = CSPSourceList.fromString("'none'"); - var simpleSourceList = CSPSourceList.fromString("a.com", undefined, URI("http://self.com")); - var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88", - undefined, - URI("http://self.com:88")); - var allSourceList = CSPSourceList.fromString("*"); - var allAndMoreSourceList = CSPSourceList.fromString("* https://bar.com 'none'"); - var wildcardHostSourceList = CSPSourceList.fromString("*.foo.com", - undefined, URI("http://self.com")); - var allDoubledHostSourceList = CSPSourceList.fromString("**"); - var allGarbageHostSourceList = CSPSourceList.fromString("*a"); - - //'none' should permit none." - do_check_false( nullSourceList.permits("http://a.com")); - //a.com should permit a.com" - do_check_true( simpleSourceList.permits("http://a.com")); - //wrong host" - do_check_false( simpleSourceList.permits("http://b.com")); - //double list permits http://bar.com:88" - do_check_true( doubleSourceList.permits("http://bar.com:88")); - //double list permits https://bar.com:88" - do_check_false( doubleSourceList.permits("https://bar.com:88")); - //double list does not permit http://bar.com:443" - do_check_false( doubleSourceList.permits("http://bar.com:443")); - //"double list permits https://foo.com:88" (should not inherit port) - do_check_false( doubleSourceList.permits("https://foo.com:88")); - //"double list does not permit foo.com on http" - do_check_false( doubleSourceList.permits("http://foo.com")); - - //"* does not permit specific host" - do_check_true( allSourceList.permits("http://x.com:23")); - //"* does not permit a long host with no port" - do_check_true( allSourceList.permits("http://a.b.c.d.e.f.g.h.i.j.k.l.x.com")); - - //* short circuts parsing - do_check_true(allAndMoreSourceList.permits("http://a.com")); - - //"** permits all" - do_check_false(allDoubledHostSourceList.permits("http://barbaz.com")); - //"*a permits all" - do_check_false(allGarbageHostSourceList.permits("http://barbaz.com")); - - //"*.foo.com does not permit somerandom.foo.com" - do_check_true(wildcardHostSourceList.permits("http://somerandom.foo.com")); - //"*.foo.com permits all" - do_check_false(wildcardHostSourceList.permits("http://barbaz.com")); - }); - -//////////////// TEST CSP REP SPEC COMPLIANT PARSER //////////// -test( - function test_CSPRep_fromString() { - - var cspr; - var cspr_allowval; - var SD = CSPRep.SRC_DIRECTIVES; - var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.IMG_SRC, SD.SCRIPT_SRC, SD.FONT_SRC, - SD.OBJECT_SRC, SD.FRAME_SRC, SD.CONNECT_SRC]; - - // check default policy "default-src *" - 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); - - for(var x in DEFAULTS) { - // each of these should be equivalent to DEFAULT_SRC - //DEFAULTS[x] + " does not use default rule." - do_check_true(cspr.permits("http://bar.com", DEFAULTS[x])); - } - }); - - -test( - function test_CSPRep_fromString_oneDir() { - - var cspr; - var SD = CSPRep.SRC_DIRECTIVES; - var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.IMG_SRC, - SD.FRAME_SRC, SD.CONNECT_SRC]; - - // check one-directive policies - cspr = CSPRep.fromString("default-src bar.com; script-src https://foo.com", - URI("http://self.com")); - - for(var x in DEFAULTS) { - //DEFAULTS[x] + " does not use default rule." - do_check_false(cspr.permits("http://bar.com:22", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule." - do_check_true(cspr.permits("http://bar.com:80", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule." - do_check_false(cspr.permits("https://foo.com:400", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule." - do_check_false(cspr.permits("https://foo.com", DEFAULTS[x])); - } - //"script-src false positive in policy. - 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 DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.FRAME_SRC, - SD.CONNECT_SRC]; - - // check two-directive policies - var polstr = "default-src allow.com; " + - "script-src https://foo.com; " + - "img-src bar.com:*"; - cspr = CSPRep.fromString(polstr, URI("http://self.com")); - - for(var x in DEFAULTS) { - do_check_true(cspr.permits("http://allow.com", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule. - do_check_false(cspr.permits("https://foo.com:400", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule. - do_check_false(cspr.permits("http://bar.com:400", DEFAULTS[x])); - //DEFAULTS[x] + " does not use default rule. - } - //"img-src does not use default rule. - do_check_false(cspr.permits("http://allow.com:22", SD.IMG_SRC)); - //"img-src does not use default rule. - do_check_false(cspr.permits("https://foo.com:400", SD.IMG_SRC)); - //"img-src does not use default rule. - do_check_true(cspr.permits("http://bar.com:88", SD.IMG_SRC)); - - //"script-src does not use default rule. - do_check_false(cspr.permits("http://allow.com:22", SD.SCRIPT_SRC)); - //"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 self = "https://self.com:34"; - var SD = CSPRep.SRC_DIRECTIVES; - - // check one-directive policies - cspr = CSPRep.fromString("default-src '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 - do_check_true(cspr.permits(self, SD.IMG_SRC)); - //"script-src is too relaxed - do_check_false(cspr.permits("http://evil.com", SD.SCRIPT_SRC)); - //"script-src should allow self - do_check_true(cspr.permits(self, SD.SCRIPT_SRC)); - //"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 self = "http://self.com:34"; - var SD = CSPRep.SRC_DIRECTIVES; - - cspr = CSPRep.fromString("default-src 'none'", URI(self)); - - //"frame-ancestors should default to * not 'default-src' 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)); - do_check_true(cspr.permits("http://self.com", SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits("http://subd.self.com:34", SD.FRAME_ANCESTORS)); - - cspr = CSPRep.fromString("default-src 'none'; frame-ancestors 'self'", URI(self)); - - //"frame-ancestors should only allow self" - do_check_true(cspr.permits("http://self.com:34", SD.FRAME_ANCESTORS)); - 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 self = "http://self"; //TLD only, no .com or anything. - - cspr = CSPRep.fromString("default-src '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)); - do_check_true(cspr.permits("http://foo:80", SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits("http://bar", SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits("http://three:80", SD.FRAME_ANCESTORS)); - - do_check_false(cspr.permits("https://foo:400", SD.FRAME_ANCESTORS)); - 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_FrameAncestor_ignores_userpass_bug779918() { - var cspr; - var SD = CSPRep.SRC_DIRECTIVES; - var self = "http://self.com/bar"; - var testPolicy = "default-src 'self'; frame-ancestors 'self'"; - - cspr = CSPRep.fromString(testPolicy, URI(self)); - - // wrapped in URI() because of source parsing - do_check_true(cspr.permits(URI("http://username:password@self.com/foo"), SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits(URI("http://other:pass1@self.com/foo"), SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits(URI("http://self.com:80/foo"), SD.FRAME_ANCESTORS)); - do_check_true(cspr.permits(URI("http://self.com/foo"), SD.FRAME_ANCESTORS)); - - // construct fake ancestry with CSP applied to the child. - // [aChildUri] -> [aParentUri] -> (root/top) - // and then test "permitsAncestry" on the child/self docshell. - function testPermits(aChildUri, aParentUri, aContentType) { - let cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"] - .createInstance(Ci.nsIContentSecurityPolicy); - cspObj.appendPolicy(testPolicy, aChildUri, false); - let docshellparent = Cc["@mozilla.org/docshell;1"] - .createInstance(Ci.nsIDocShell); - let docshellchild = Cc["@mozilla.org/docshell;1"] - .createInstance(Ci.nsIDocShell); - docshellparent.setCurrentURI(aParentUri); - docshellchild.setCurrentURI(aChildUri); - docshellparent.addChild(docshellchild); - return cspObj.permitsAncestry(docshellchild); - }; - - // check parent without userpass - do_check_true(testPermits(URI("http://username:password@self.com/foo"), - URI("http://self.com/bar"))); - do_check_true(testPermits(URI("http://user1:pass1@self.com/foo"), - URI("http://self.com/bar"))); - do_check_true(testPermits(URI("http://self.com/foo"), - URI("http://self.com/bar"))); - - // check parent with userpass - do_check_true(testPermits(URI("http://username:password@self.com/foo"), - URI("http://username:password@self.com/bar"))); - do_check_true(testPermits(URI("http://user1:pass1@self.com/foo"), - URI("http://username:password@self.com/bar"))); - do_check_true(testPermits(URI("http://self.com/foo"), - URI("http://username:password@self.com/bar"))); - }); - -test(function test_CSP_ReportURI_parsing() { - var cspr; - var SD = CSPRep.SRC_DIRECTIVES; - var self = "http://self.com:34"; - var parsedURIs = []; - - var uri_valid_absolute = self + "/report.py"; - var uri_other_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"; - var uri_valid_relative2_expanded = self + "/" + uri_valid_relative2; - var uri_invalid_relative = "javascript:alert(1)"; - var uri_other_scheme_absolute = "https://self.com/report.py"; - var uri_other_scheme_and_host_absolute = "https://foo.com/report.py"; - - cspr = CSPRep.fromString("default-src *; report-uri " + uri_valid_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, uri_valid_absolute); - do_check_eq(parsedURIs.length, 1); - - cspr = CSPRep.fromString("default-src *; report-uri " + uri_other_host_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, uri_other_host_absolute); - do_check_eq(parsedURIs.length, 1); // the empty string is in there. - - cspr = CSPRep.fromString("default-src *; report-uri " + uri_invalid_relative, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, ""); - do_check_eq(parsedURIs.length, 1); - - cspr = CSPRep.fromString("default-src *; report-uri " + uri_valid_relative, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, uri_valid_relative_expanded); - do_check_eq(parsedURIs.length, 1); - - cspr = CSPRep.fromString("default-src *; report-uri " + uri_valid_relative2, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - dump(parsedURIs.length); - do_check_in_array(parsedURIs, uri_valid_relative2_expanded); - do_check_eq(parsedURIs.length, 1); - - // make sure cross-scheme reporting works - cspr = CSPRep.fromString("default-src *; report-uri " + uri_other_scheme_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - dump(parsedURIs.length); - do_check_in_array(parsedURIs, uri_other_scheme_absolute); - do_check_eq(parsedURIs.length, 1); - - // make sure cross-scheme, cross-host reporting works - cspr = CSPRep.fromString("default-src *; report-uri " + uri_other_scheme_and_host_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - dump(parsedURIs.length); - do_check_in_array(parsedURIs, uri_other_scheme_and_host_absolute); - do_check_eq(parsedURIs.length, 1); - - // combination! - cspr = CSPRep.fromString("default-src *; report-uri " + - uri_valid_relative2 + " " + - uri_valid_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, uri_valid_relative2_expanded); - do_check_in_array(parsedURIs, uri_valid_absolute); - do_check_eq(parsedURIs.length, 2); - - cspr = CSPRep.fromString("default-src *; report-uri " + - uri_valid_relative2 + " " + - uri_other_host_absolute + " " + - uri_valid_absolute, URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, uri_valid_relative2_expanded); - do_check_in_array(parsedURIs, uri_other_host_absolute); - do_check_in_array(parsedURIs, uri_valid_absolute); - do_check_eq(parsedURIs.length, 3); - }); - -test( - function test_bug634778_duplicateDirective_Detection() { - var cspr; - var SD = CSPRep.SRC_DIRECTIVES; - 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 - // Spec says first directive persists (subsequent re-statement is - // ignored) - cspr = CSPRep.fromString("default-src " + self + "; default-src " + - firstDomain, URI(self)); - do_check_true(cspr.permits(self, SD.DEFAULT_SRC)); - do_check_false(cspr.permits(firstDomain, SD.DEFAULT_SRC)); - - // check for duplicate report-uri directives - cspr = CSPRep.fromString("default-src *; report-uri " + self + "/report.py; report-uri " - + firstDomain + "/report.py", URI(self)); - parsedURIs = cspr.getReportURIs().split(/\s+/); - do_check_in_array(parsedURIs, self + "/report.py"); - do_check_eq(parsedURIs.length, 1); - - // check for three directives with duplicates - cspr = CSPRep.fromString("img-src " + firstDomain + "; default-src " + self - + "; img-src " + secondDomain, URI(self)); - do_check_true(cspr.permits(firstDomain, SD.IMG_SRC)); - do_check_false(cspr.permits(secondDomain, SD.IMG_SRC)); - do_check_true(cspr.permits(self, SD.DEFAULT_SRC)); - - // check for three directives with duplicates - cspr = CSPRep.fromString("img-src " + firstDomain + "; default-src " + self - + "; img-src " + secondDomain, URI(self)); - do_check_true(cspr.permits(firstDomain, SD.IMG_SRC)); - do_check_false(cspr.permits(secondDomain, SD.IMG_SRC)); - - // check for three directives with duplicates - cspr = CSPRep.fromString("default-src " + self + "; img-src " + firstDomain - + "; img-src " + secondDomain, URI(self)); - do_check_true(cspr.permits(firstDomain, SD.IMG_SRC)); - do_check_false(cspr.permits(secondDomain, SD.IMG_SRC)); - - // check for four directives with duplicates - cspr = CSPRep.fromString("default-src " + self + "; img-src " + firstDomain - + "; img-src " + secondDomain + "; img-src " - + thirdDomain, URI(self)); - do_check_true(cspr.permits(firstDomain, SD.IMG_SRC)); - do_check_false(cspr.permits(secondDomain, SD.IMG_SRC)); - do_check_false(cspr.permits(thirdDomain, SD.IMG_SRC)); - - // check for four directives with two duplicates - cspr = CSPRep.fromString("default-src " + self + "; style-src " - + firstDomain + "; media-src " + firstDomain - + "; media-src " + secondDomain + "; style-src " - + thirdDomain, URI(self)); - do_check_true(cspr.permits(self, SD.DEFAULT_SRC)); - do_check_true(cspr.permits(firstDomain, SD.STYLE_SRC)); - do_check_true(cspr.permits(firstDomain, SD.MEDIA_SRC)); - do_check_false(cspr.permits(secondDomain, SD.MEDIA_SRC)); - do_check_false(cspr.permits(thirdDomain, SD.STYLE_SRC)); - }); - -test( - function test_bug672961_withNonstandardSelfPort() { - /** - * When a protected document has a non-standard port, other host names - * listed as sources should inherit the scheme of the protected document - * but NOT the port. Other hosts should use the default port for the - * inherited scheme. For example, since 443 is default for HTTPS: - * - * Document with CSP: https://foobar.com:4443 - * Transmitted policy: - * "default-src 'self' a.com" - * Explicit policy: - * "default-src https://foobar.com:4443 https://a.com:443" - * - * This test examines scheme and nonstandard port inheritance. - */ - - var src; - src = CSPSource.create("a.com", undefined, "https://foobar.com:4443"); - //"src should inherit and require https scheme - do_check_false(src.permits("http://a.com")); - //"src should inherit scheme 'https'" - do_check_true(src.permits("https://a.com")); - //"src should get default port - do_check_true(src.permits("https://a.com:443")); - - src = CSPSource.create("http://a.com", undefined, "https://foobar.com:4443"); - //"src should require http scheme" - do_check_false(src.permits("https://a.com")); - //"src should keep scheme 'http'" - do_check_true(src.permits("http://a.com")); - //"src should inherit default port for 'http'" - do_check_true(src.permits("http://a.com:80")); - - src = CSPSource.create("'self'", undefined, "https://foobar.com:4443"); - //"src should inherit nonstandard port from self - do_check_true(src.permits("https://foobar.com:4443")); - do_check_false(src.permits("https://foobar.com")); - do_check_false(src.permits("https://foobar.com:443")); - - //"src should inherit and require https scheme from self - do_check_false(src.permits("http://foobar.com:4443")); - do_check_false(src.permits("http://foobar.com")); - - }); - -test( - function test_bug634773_noneAndStarAreDifferent() { - /** - * Bug 634773 is that default-src * and default-src 'none' end up "equal" via - * CSPSourceList.prototype.equals(), which is wrong. This tests that - * doesn't happen. - */ - - var p_none = CSPSourceList.fromString("'none'", undefined, "http://foo.com", false); - var p_all = CSPSourceList.fromString("*", undefined, "http://foo.com", false); - var p_one = CSPSourceList.fromString("bar.com", undefined, "http://foo.com", false); - - do_check_false(p_none.equals(p_all)); - do_check_false(p_none.equals(p_one)); - do_check_false(p_all.equals(p_none)); - do_check_false(p_all.equals(p_one)); - - do_check_true(p_all.permits("http://bar.com")); - do_check_true(p_one.permits("http://bar.com")); - do_check_false(p_none.permits("http://bar.com")); - }); - - -test( - function test_bug764937_defaultSrcMissing() { - var cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"] - .createInstance(Ci.nsIContentSecurityPolicy); - var selfURI = URI("http://self.com/"); - - function testPermits(cspObj, aUri, aContentType) { - return cspObj.shouldLoad(aContentType, aUri, null, null, null, null) - == Ci.nsIContentPolicy.ACCEPT; - }; - - const policy = "script-src 'self'"; - cspObj.appendPolicy(policy, selfURI, false); - - // Spec-Compliant policy default-src defaults to *. - // This means all images are allowed, and only 'self' - // script is allowed. - do_check_true(testPermits(cspObj, - URI("http://bar.com/foo.png"), - Ci.nsIContentPolicy.TYPE_IMAGE)); - do_check_true(testPermits(cspObj, - URI("http://self.com/foo.png"), - Ci.nsIContentPolicy.TYPE_IMAGE)); - do_check_true(testPermits(cspObj, - URI("http://self.com/foo.js"), - Ci.nsIContentPolicy.TYPE_SCRIPT)); - do_check_false(testPermits(cspObj, - URI("http://bar.com/foo.js"), - Ci.nsIContentPolicy.TYPE_SCRIPT)); - - }); - -test(function test_equals_does_case_insensitive_comparison() { - // NOTE: For scheme, host and keyword-host: - // (1) compare the same lower-case in two distinct objects - // (2) compare upper-case with lower-case inputs - // to test case insensitivity. - - // CSPSource equals ignores case - var upperCaseHost = "http://FOO.COM"; - var lowerCaseHost = "http://foo.com"; - var src1 = CSPSource.fromString(lowerCaseHost); - var src2 = CSPSource.fromString(lowerCaseHost); - do_check_true(src1.equals(src2)) - var src3 = CSPSource.fromString(upperCaseHost); - do_check_true(src1.equals(src3)) - - // CSPHost equals ignores case - var upperCaseScheme = "HTTP"; - var lowerCaseScheme = "http"; - src1 = CSPHost.fromString(lowerCaseScheme); - src2 = CSPHost.fromString(lowerCaseScheme); - do_check_true(src1.equals(src2)); - src3 = CSPHost.fromString(upperCaseScheme); - do_check_true(src1.equals(src3)); - - // CSPSourceList equals (mainly for testing keywords) - var upperCaseKeywords = "'SELF'"; - var lowerCaseKeywords = "'self'"; - src1 = CSPSourceList.fromString(lowerCaseKeywords); - src2 = CSPSourceList.fromString(lowerCaseKeywords); - do_check_true(src1.equals(src2)) - src3 = CSPSourceList.fromString(upperCaseKeywords); - do_check_true(src1.equals(src3)) - - }); - -test(function test_csp_permits_case_insensitive() { - var cspr; - var SD = CSPRep.SRC_DIRECTIVES; - - // checks directives can be case-insensitive - var selfHost = "http://self.com"; - var testPolicy1 = "DEFAULT-src 'self';"; - cspr = CSPRep.fromString(testPolicy1, URI(selfHost)); - do_check_true(cspr.permits(URI("http://self.com"), SD.DEFAULT_SRC)); - - // checks hosts can be case-insensitive - var testPolicy2 = "default-src 'self' http://FOO.COM"; - cspr = CSPRep.fromString(testPolicy2, URI(selfHost)); - do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC)); - - // checks schemes can be case-insensitive - var testPolicy3 = "default-src 'self' HTTP://foo.com"; - cspr = CSPRep.fromString(testPolicy3, URI(selfHost)); - do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC)); - - // checks keywords can be case-insensitive - var testPolicy4 = "default-src 'NONE'"; - cspr = CSPRep.fromString(testPolicy4, URI(selfHost)); - do_check_false(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC)); - }); -/* - -test(function test_CSPRep_fromPolicyURI_failswhenmixed() { - var cspr; - var self = "http://localhost:" + POLICY_PORT; - var closed_policy = CSPRep.fromString("default-src 'none'"); - var my_uri_policy = "policy-uri " + POLICY_URI; - - //print(" --- Ignore the following two errors if they print ---"); - cspr = CSPRep.fromString("default-src *; " + my_uri_policy, URI(self)); - - //"Parsing should fail when 'policy-uri' is mixed with default-src directive" - do_check_equivalent(cspr, closed_policy); - cspr = CSPRep.fromString("img-src 'self'; " + my_uri_policy, URI(self)); - - //"Parsing should fail when 'policy-uri' is mixed with other directives" - do_check_equivalent(cspr, closed_policy); - //print(" --- Stop ignoring errors that print ---\n"); - - }); -*/ - -// TODO: test reporting -// TODO: test refinements (?) -// TODO: test 'eval' and 'inline' keywords - -function run_test() { - function policyresponder(request,response) { - response.setStatusLine(request.httpVersion, 200, "OK"); - response.setHeader("Content-Type", "text/csp", false); - response.bodyOutputStream.write(POLICY_FROM_URI, POLICY_FROM_URI.length); - } - //server.registerDirectory("/", nsILocalFileForBasePath); - httpServer.registerPathHandler("/policy", policyresponder); - - for(let i in tests) { - add_task(tests[i]); - } - - do_register_cleanup(function () { - //teardown - httpServer.stop(function() { }); - }); - - run_next_test(); -} - - - diff --git a/content/base/test/unit/xpcshell.ini b/content/base/test/unit/xpcshell.ini index b0951dbb278..0f444ac197f 100644 --- a/content/base/test/unit/xpcshell.ini +++ b/content/base/test/unit/xpcshell.ini @@ -22,7 +22,6 @@ support-files = [test_bug553888.js] [test_bug737966.js] -[test_csputils.js] [test_cspreports.js] [test_error_codes.js] run-sequentially = Hardcoded 4444 port. @@ -32,4 +31,3 @@ skip-if = os == 'mac' [test_xhr_document.js] [test_xhr_standalone.js] [test_xmlserializer.js] -[test_csp_ignores_path.js] diff --git a/dom/ipc/preload.js b/dom/ipc/preload.js index b26d7858321..f557d449cd5 100644 --- a/dom/ipc/preload.js +++ b/dom/ipc/preload.js @@ -19,7 +19,6 @@ const BrowserElementIsPreloaded = true; Cu.import("resource://gre/modules/AppsServiceChild.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); - Cu.import("resource://gre/modules/CSPUtils.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/Geometry.jsm"); @@ -70,7 +69,7 @@ const BrowserElementIsPreloaded = true; Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]); Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]); Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]); - Cc["@mozilla.org/contentsecuritypolicy;1"].createInstance(Ci["nsIContentSecurityPolicy"]); + Cc["@mozilla.org/cspcontext;1"].createInstance(Ci["nsIContentSecurityPolicy"]); /* Applications Specific Helper */ try { diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index f075c8526df..f0256bfabe0 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -381,8 +381,6 @@ @BINPATH@/components/formautofill.manifest @BINPATH@/components/FormAutofillContentService.js @BINPATH@/components/FormAutofillStartup.js -@BINPATH@/components/contentSecurityPolicy.manifest -@BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js @BINPATH@/components/messageWakeupService.js diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index de4ef146f64..611cacfecca 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1647,7 +1647,6 @@ pref("security.notification_enable_delay", 500); pref("security.csp.enable", true); pref("security.csp.debug", false); pref("security.csp.experimentalEnabled", false); -pref("security.csp.newbackend.enable", true); // Mixed content blocking pref("security.mixed_content.block_active_content", false);