Mozilla Home
Privacy
Cookies
Legal
Bugzilla
Browse
Advanced Search
New Bug
Reports
Documentation
Log In
Log In with GitHub
or
Remember me
Browse
Advanced Search
New Bug
Reports
Documentation
Attachment 389817 Details for
Bug 493857
[patch]
CSP Work in Progress (v4.1)
CSP.wip4_1.patch (text/plain), 94.75 KB, created by
Sid Stamm [:geekboy or :sstamm]
(
hide
)
Description:
CSP Work in Progress (v4.1)
Filename:
MIME Type:
Creator:
Sid Stamm [:geekboy or :sstamm]
Size:
94.75 KB
patch
obsolete
>diff -r 944b2cd986d9 content/base/public/IContentSecurityPolicy.idl >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/public/IContentSecurityPolicy.idl Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,132 @@ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is the nsCSPContentPolicy service. >+ * >+ * The Initial Developer of the Original Code is >+ * Sid Stamm <sid@mozilla.com> >+ * >+ * Contributor(s): >+ * Brandon Sterne <bsterne@mozilla.com> >+ * Daniel Veditz <dveditz@mozilla.com> >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either the GNU General Public License Version 2 or later (the "GPL"), or >+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+#include "nsISupports.idl" >+ >+interface nsIURI; >+interface nsIHttpChannel; >+interface nsIDocShell; >+ >+/** >+ * IContentSecurityPolicy >+ * Describes an XPCOM component used to model an enforce CSPs. >+ */ >+ >+[scriptable, uuid(AB36A2BF-CB32-4AA6-AB41-6B4E4444A221)] >+interface IContentSecurityPolicy : nsISupports >+{ >+ >+ /** >+ * Set to true when the CSP has been read in and parsed and is ready to >+ * enforce. This is a barrier for the nsDocument so it doesn't load any >+ * sub-content until either it knows that a CSP is ready or will not be used. >+ */ >+ attribute boolean isInitialized; >+ >+ /** >+ * A read-only string version of the policy for debugging. >+ */ >+ readonly attribute AString policy; >+ >+ /** >+ * Whether this policy allows in-page script >+ */ >+ readonly attribute boolean allowsInlineScript; >+ >+ /** >+ * whether this policy allows eval and eval-like functions >+ * such as setTimeout("code string", time) >+ */ >+ readonly attribute boolean allowsEval; >+ >+ /** >+ * Triggers violation report sending given a URI and reason. >+ * The URI may be null, in which case "self" is sent. >+ * @param blockedURI >+ * the URI that violated the policy >+ * @param violatedDirective >+ * the directive that was violated. >+ * @return >+ * nothing. >+ */ >+ void sendReports(in AString blockedURI, in AString violatedDirective); >+ >+ /** >+ * Called after the CSP object is created to fill in the appropriate request >+ * and request header information needed in case a report needs to be sent. >+ */ >+ void scanRequestData(in nsIHttpChannel aChannel); >+ >+ /** >+ * Updates the policy currently stored in the CSP to be "refined" or >+ * tightened by the one specified in the string policyString. >+ */ >+ void refinePolicy(in AString policyString, in nsIURI selfURI); >+ >+ /** >+ * Verifies ancestry as permitted by the policy. >+ * @param docShell >+ * containing the protected resource >+ * @return >+ * true if the frame's ancestors are all permitted by policy >+ */ >+ boolean permitsAncestry(in nsIDocShell docShell); >+ >+ /** >+ * 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. >+ */ >+ short shouldLoad(in unsigned long aContentType, >+ in nsIURI aContentLocation, >+ in nsIURI aRequestOrigin, >+ in nsISupports aContext, >+ in ACString aMimeTypeGuess, >+ in nsISupports aExtra); >+ >+ /** >+ * Delegate method called by the service when sub-elements of the protected >+ * document are being processed. Given a bit of information about the request, >+ * decides whether or not the policy is satisfied. >+ */ >+ short shouldProcess(in unsigned long aContentType, >+ in nsIURI aContentLocation, >+ in nsIURI aRequestOrigin, >+ in nsISupports aContext, >+ in ACString aMimeType, >+ in nsISupports aExtra); >+ >+}; >diff -r 944b2cd986d9 content/base/public/Makefile.in >--- a/content/base/public/Makefile.in Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/public/Makefile.in Tue Jul 21 16:46:09 2009 -0700 >@@ -106,12 +106,13 @@ XPIDLSRCS = \ > nsISyncLoadDOMService.idl \ > nsIDragDropHandler.idl \ > nsIScriptEventHandler.idl \ > nsIScriptEventManager.idl \ > nsIImageLoadingContent.idl \ > nsIObjectLoadingContent.idl \ > nsIFrameLoader.idl \ > nsIXMLHttpRequest.idl \ >+ IContentSecurityPolicy.idl \ > $(NULL) > > include $(topsrcdir)/config/rules.mk > >diff -r 944b2cd986d9 content/base/public/nsIDocument.h >--- a/content/base/public/nsIDocument.h Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/public/nsIDocument.h Tue Jul 21 16:46:09 2009 -0700 >@@ -52,16 +52,17 @@ > #include "nsCompatibility.h" > #include "nsTObserverArray.h" > #include "nsTHashtable.h" > #include "nsHashKeys.h" > #include "nsNodeInfoManager.h" > #include "nsIStreamListener.h" > #include "nsIObserver.h" > #include "nsAutoPtr.h" >+#include "IContentSecurityPolicy.h" > #ifdef MOZ_SMIL > class nsSMILAnimationController; > #endif // MOZ_SMIL > > class nsIContent; > class nsPresContext; > class nsIPresShell; > class nsIDocShell; >@@ -1057,16 +1058,24 @@ public: > "have a presentation or a docshell or a window"); > NS_PRECONDITION(aDisplayDocument != this, "Should be different document"); > NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(), > "Display documents should not nest"); > mDisplayDocument = aDisplayDocument; > } > > /** >+ * Return the Content Security Policy object for the document >+ */ >+ IContentSecurityPolicy* GetContentSecurityPolicy() const >+ { >+ return mCSP; >+ } >+ >+ /** > * A class that represents an external resource load that has begun but > * doesn't have a document yet. Observers can be registered on this object, > * and will be notified after the document is created. Observers registered > * after the document has been created will NOT be notified. When observers > * are notified, the subject will be the newly-created document, the topic > * will be "external-resource-document-created", and the data will be null. > * If document creation fails for some reason, observers will still be > * notified, with a null document pointer. >@@ -1266,16 +1275,19 @@ protected: > PRUint32 mSubtreeModifiedDepth; > > // If we're an external resource document, this will be non-null and will > // point to our "display document": the one that all resource lookups should > // go to. > nsCOMPtr<nsIDocument> mDisplayDocument; > > PRUint32 mEventsSuppressed; >+ >+ // CSP policy object/data >+ nsCOMPtr<IContentSecurityPolicy> mCSP; > }; > > NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) > > /** > * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified > * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified > * object is deleted. >diff -r 944b2cd986d9 content/base/src/CSPUtils.jsm >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/src/CSPUtils.jsm Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,942 @@ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is the Content Security Policy data structures. >+ * >+ * The Initial Developer of the Original Code is >+ * Sid Stamm <sid@mozilla.com> >+ * >+ * Contributor(s): >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either the GNU General Public License Version 2 or later (the "GPL"), or >+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+/** >+ * Content Security Policy Utilities >+ * >+ * Overview >+ * This contains a set of classes and utilities for CSP. It is in this >+ * separate file for testing purposes. >+ */ >+ >+// Module stuff >+var EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", >+ "CSPHost", "CSPWarning", "CSPError", "CSPdebug"]; >+ >+function CSPWarning(aMsg) { >+ //customize this to redirect output. >+ aMsg = 'CSP WARN: ' + aMsg + "\n"; >+ dump(aMsg); >+ Components.classes["@mozilla.org/consoleservice;1"] >+ .getService(Components.interfaces.nsIConsoleService) >+ .logStringMessage(aMsg); >+} >+function CSPError(aMsg) { >+ aMsg = 'CSP ERROR: ' + aMsg + "\n"; >+ dump(aMsg); >+ Components.classes["@mozilla.org/consoleservice;1"] >+ .getService(Components.interfaces.nsIConsoleService) >+ .logStringMessage(aMsg); >+} >+function CSPdebug(aMsg) { >+ aMsg = 'CSP debug: ' + aMsg + "\n"; >+ dump(aMsg); >+ Components.classes["@mozilla.org/consoleservice;1"] >+ .getService(Components.interfaces.nsIConsoleService) >+ .logStringMessage(aMsg); >+} >+ >+ >+//:::::::::::::::::::::::: CLASSES ::::::::::::::::::::::::::// >+ >+/** >+ * Class that represents a parsed policy structure. >+ */ >+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; >+ >+ // don't auto-populate _directives, so it is easier to find bugs >+ this._directives = {}; >+ >+} >+CSPRep.SRC_DIRECTIVES = { >+ ALLOW: "allow", >+ 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" >+}; >+CSPRep.URI_DIRECTIVES = { >+ REPORT_URI: "report-uri", >+ POLICY_URI: "policy-uri" >+}; >+ >+ >+/** >+ * Factory to create a new CSPRep, parsed from a string. >+ * >+ * @param aStr >+ * string rep of a CSP >+ * @param self (optional) >+ * string or CSPSource representing the "self" source >+ * @returns >+ * an instance of CSPRep >+ */ >+CSPRep.fromString = function(aStr, self) { >+ var SD = CSPRep.SRC_DIRECTIVES; >+ var UD = CSPRep.URI_DIRECTIVES; >+ var pieces = aStr.split(/;\s*/); >+ var aCSPR = new CSPRep(); >+ piece:for(var i in pieces) { >+ //split off first token and figure out what to do >+ var dirname = pieces[i].match(/(\S+)/)[0]; >+ >+ //careful to trim the stuff so there's no extra tokens >+ var dirvalue = pieces[i].substring(dirname.length) >+ .replace(/^\s+/,'') >+ .replace(/\s+$/,''); >+ >+ //If it is the script-src directive, pull out "eval" and "inline" >+ if(dirname == SD.STYLE_SRC) { >+ let listchunks = dirvalue.split(/\s+/); >+ for(let i in listchunks) { >+ if(listchunks[i] == "eval") { >+ listchunks.splice(i,1); >+ aCSPR._allowEval = true; >+ } >+ if(listchunks[i] == "inline") { >+ listchunks.splice(i,1); >+ aCSPR._allowInlineScripts = true; >+ } >+ } >+ // repair source list without the keywords >+ dirvalue = listchunks.join(" "); >+ } >+ >+ //is it a SRC directive? >+ for(var sdi in SD) { >+ if(dirname == SD[sdi]) { >+ var dv = CSPSourceList.fromString(dirvalue, self); >+ //TODO: more robust check for errors in making DV >+ aCSPR._directives[SD[sdi]] = dv; >+ continue piece; >+ } >+ } >+ >+ //not a SRC directive, check URI directives >+ if(dirname == UD.POLICY_URI) { >+ // POLICY_URI can only be alone >+ if(aCSPR._directives.length > 0 || pieces.length > 1) { >+ CSPError("policy-uri directive can only appear alone"); >+ return CSPRep.fromString("allow none"); >+ } >+ >+ //return; >+ //TODO: verify that policy URI is same-origin >+ var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] >+ .createInstance(Components.interfaces.nsIXMLHttpRequest); >+ >+ // insert error hook >+ req.onerror = CSPError; >+ >+ // synchronous -- otherwise we need to architect a callback into the >+ // xpcom component so that whomever creates the policy object gets >+ // notified when it's loaded and ready to go. >+ req.open("GET", dirvalue, false); >+ >+ // make request anonymous >+ // This prevents sending cookies with the request, in case the policy URI >+ // is injected, it can't be abused for CSRF. >+ req.channel.loadFlags |= Components.interfaces.nsIChannel.LOAD_ANONYMOUS; >+ >+ //CSPdebug("Fetching URI " + dirvalue); >+ req.send(null); >+ if(req.status == 200) { >+ //CSPdebug("XHR returned: " + req.responseText); >+ aCSPR = CSPRep.fromString(req.responseText, self); >+ //remember where we got the policy >+ aCSPR._directives[UD.POLICY_URI] = dirvalue; >+ return aCSPR; >+ } >+ CSPError("Error fetching policy URI: server response was " + req.status); >+ return CSPRep.fromString("allow none"); >+ } >+ if(dirname == UD.REPORT_URI) { >+ //TODO: do we want to support multiple URIs? >+ //TODO: verify that report URI(s) is(are) same-origin >+ aCSPR._directives[UD.REPORT_URI] = dirvalue; >+ continue piece; >+ } >+ >+ //no directives matched >+ CSPWarning("Couldn't process unknown directive '" + dirname + "'"); >+ } >+ >+ if(aCSPR._directives.length < 1) { >+ CSPError("invalid policy -- no recognized directives"); >+ return CSPRep.fromString("allow none"); >+ } >+ >+ if(!aCSPR._directives[SD.ALLOW]) { >+ CSPWarning("allow directive is not present, using 'allow none'"); >+ aCSPR._directives[SD.ALLOW] = CSPSourceList.fromString("none"); >+ } >+ >+ aCSPR.makeExplicit(); >+ return aCSPR; >+}; >+ >+CSPRep.prototype = { >+ /** >+ * Returns array of all report uris defined, or none if there are none. >+ */ >+ getReportURIs: >+ function() { >+ if(!this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI]) >+ return []; >+ >+ var uris = this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI].split(","); >+ for(let i in uris) { >+ //trim off surrounding whitespace >+ uris[i] = uris[i].replace(/^\s+/, "").replace(/\s+$/, ""); >+ } >+ return uris; >+ }, >+ >+ /** >+ * 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 true; >+ }, >+ >+ /** >+ * Generates string representation of the policy. Should be fairly similar >+ * to the original. >+ */ >+ 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("; "); >+ }, >+ >+ >+ /** >+ * Determines if this policy accepts a URI. >+ * @param aContext >+ * one of the SRC_DIRECTIVES defined above >+ * @returns >+ * true if the policy permits the URI in given context. >+ */ >+ permits: >+ function csp_permits(aURI, aContext) { >+ >+ // GLOBALLY ALLOW "about:" SCHEME >+ if(aURI instanceof String && aURI.substring(0,6) == "about:") { >+ return true; >+ } >+ if(aURI instanceof Components.interfaces.nsIURI >+ && aURI.scheme == "about") { >+ return true; >+ } >+ >+ //make sure the context is valid >+ for(var i in CSPRep.SRC_DIRECTIVES) { >+ if(CSPRep.SRC_DIRECTIVES[i] == aContext) { >+ return this._directives[aContext].permits(aURI); >+ } >+ } >+ return false; >+ }, >+ >+ /** >+ * Intersects with another CSPRep, deciding the subset policy >+ * that should be enforced, and returning a new instance. >+ * @param aCSPRep >+ * a CSPRep instance to use as "other" CSP >+ * @returns >+ * a new CSPRep instance of the intersection >+ */ >+ intersectWith: >+ function cspsd_intersectWith(aCSPRep) { >+ var newRep = new CSPRep(); >+ >+ for(var dir in CSPRep.SRC_DIRECTIVES) { >+ var dirv = CSPRep.SRC_DIRECTIVES[dir]; >+ newRep._directives[dirv] = this._directives[dirv] >+ .intersectWith(aCSPRep._directives[dirv]); >+ } >+ return newRep; >+ }, >+ >+ >+ /** >+ * Copies default source list to each unspecified directive. >+ * @returns >+ * true if the makeExplicit succeeds >+ * false if it fails (for some weird reason) >+ */ >+ makeExplicit: >+ function cspsd_makeExplicit() { >+ var SD = CSPRep.SRC_DIRECTIVES; >+ var allowDir = this._directives[SD.ALLOW]; >+ if(!allowDir) { >+ return false; >+ } >+ >+ for(var dir in SD) { >+ var dirv = SD[dir]; >+ if(dirv == SD.ALLOW) { >+ continue; >+ } >+ if(!this._directives[dirv]) { >+ //implicit directive, make explicit >+ this._directives[dirv] = allowDir.clone(); >+ } >+ } >+ this._isInitialized = true; >+ 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; >+ }, >+}; >+ >+ >+////////////////////////////////////////////////////////////////////// >+/** >+ * Class to represent a list of sources >+ */ >+function CSPSourceList() { >+ this._sources = []; >+ this._permitAllSources = false; >+} >+ >+/** >+ * Factory to create a new CSPSourceList, parsed from a string. >+ * >+ * @param aStr >+ * string rep of a CSP Source List >+ * @param self (optional) >+ * string or CSPSource representing the "self" source >+ * @returns >+ * an instance of CSPSourceList >+ */ >+CSPSourceList.fromString = function(aStr, self) { >+ //Source list is: >+ // <host-dir-value> ::= <source-list> >+ // | "none" >+ // <source-list> ::= <source> >+ // | <source-list>" "<source> >+ >+ var slObj = new CSPSourceList(); >+ if(aStr == "none") { >+ return slObj; >+ } >+ >+ var ss = aStr.split(/\s+/); >+ for(var i in ss) { >+ var o = CSPSource.fromString(ss[i], self); >+ //make sure none came back undefined >+ if(!o) { return null; } >+ slObj._sources[i] = o; >+ } >+ return slObj; >+}; >+ >+CSPSourceList.prototype = { >+ >+ /** >+ * Compares one CSPSourceList to another. >+ * >+ * @param that >+ * another CSPSourceList >+ * @returns >+ * true if they have the same data >+ */ >+ equals: >+ function(that) { >+ 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() > b.toString(); >+ }; >+ 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 string representation of the Source List. >+ * Should be fairly similar to the original. >+ */ >+ toString: >+ function() { >+ if(this.isNone()) { >+ return "none"; >+ } >+ 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); >+ }, >+ >+ /** >+ * Makes a new instance that resembles this object. >+ * @returns >+ * a new CSPSourceList >+ */ >+ clone: >+ function() { >+ var aSL = new CSPSourceList(); >+ aSL._permitAllSources = this._permitAllSources; >+ 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; >+ } >+ for(var i in this._sources) { >+ if(this._sources[i].permits(aURI)) { >+ return true; >+ } >+ } >+ return false; >+ }, >+ >+ >+ /** >+ * Intersects with another CSPSourceList, deciding the subset directive >+ * that should be enforced, and returning a new instance. >+ * @param that >+ * the other CSPSourceList to intersect "this" with >+ * @returns >+ * a new instance of a CSPSourceList representing the intersection >+ */ >+ intersectWith: >+ function cspsd_intersectWith(that) { >+ if(this._permitAllSources) { >+ return that.clone(); >+ } >+ if(that._permitAllSources) { >+ return this.clone(); >+ } >+ >+ // "none" case >+ if(this.isNone() || that.isNone()) { >+ return CSPSourceList.fromString("none"); >+ } >+ >+ // -- find only common sources >+ >+ //XXX (sid): figure out a good algorithm for this. >+ // This is horribly inefficient. O(n^2) >+ var isrcs = []; >+ for(var i in this._sources) { >+ for(var j in that._sources) { >+ var s = that._sources[j].intersectWith(this._sources[i]); >+ if(s) { >+ isrcs.push(s); >+ } >+ } >+ } >+ //Next, remove duplicates >+ dup:for(var i = 0; i < isrcs.length; i++) { >+ for(var j = 0; j < i; j++) { >+ if(isrcs[i].equals(isrcs[j])) { >+ isrcs.splice(i, 1); >+ i--; >+ continue dup; >+ } >+ } >+ } >+ var newCSPSrcList = new CSPSourceList(); >+ newCSPSrcList._sources = isrcs; >+ return newCSPSrcList; >+ } >+} >+ >+ >+////////////////////////////////////////////////////////////////////// >+/** >+ * Class to model a source (scheme, host, port) >+ */ >+function CSPSource() { >+ this._scheme = "*"; >+ this._port = "*"; >+ this._host = "*"; >+} >+ >+CSPSource._PORT_RE = /(:(\*|\d+))$/; >+CSPSource._SCHM_RE = /^((\*|[a-zA-Z][a-zA-Z\+\.\-]*):\/{0,2})/; >+CSPSource._HOST_RE = /^(localhost|\*|(((\*|[a-zA-Z0-9\-]+)\.)([a-zA-Z0-9\-]+\.)*([a-zA-Z0-9\-]+)))$/; >+ >+/** >+ * Factory to create a new CSPSource, from a nsIURI. >+ * >+ * @param aURI >+ * nsIURI rep of a URI >+ * @param self (optional) >+ * string or CSPSource representing the "self" source >+ * @returns >+ * an instance of CSPSource >+ */ >+CSPSource.fromURI = function(aURI, self) { >+ >+ if( !(aURI instanceof Components.interfaces.nsIURI) ){ >+ CSPError("Provided argument is not an nsIURI"); >+ return null; >+ } >+ >+ var sObj = new CSPSource(); >+ >+ try { // try block in case there's no host specified in the URI >+ if(!aURI.host) >+ sObj._host = "*"; >+ else >+ sObj._host = aURI.host; >+ } catch(e) {} >+ >+ // Don't forget to handle "self" tokens in the URI >+ if(sObj._host == "self") { >+ if(!self) { >+ CSPError("self keyword used, but no self data specified"); >+ return null; >+ } >+ if(self == "self") { >+ CSPError("self reference cannot be 'self'"); >+ return null; >+ } >+ if(!(self instanceof CSPSource)) { >+ self = CSPSource.fromString(self); >+ } >+ sObj._host = self._host; >+ if(!sObj._port) { >+ sObj._port = self._port; >+ } >+ } >+ >+ try { // it's possible there's not an available port >+ if(aURI.port) >+ sObj._port = aURI.port; >+ else >+ sObj._port = (self ? self._port : "*"); >+ } catch(e) {} >+ >+ if(aURI.scheme) >+ sObj._scheme = aURI.scheme; >+ else >+ sObj._scheme = (self ? self._scheme : "*"); >+ >+ return sObj; >+}; >+ >+/** >+ * Factory to create a new CSPSource, parsed from a string. >+ * >+ * @param aStr >+ * string rep of a CSP Source >+ * @param self (optional) >+ * string or CSPSource representing the "self" source >+ * @returns >+ * an instance of CSPSource >+ */ >+CSPSource.fromString = function(aStr, self) { >+ if(aStr == "self") { >+ if(!self) { >+ CSPError("self keyword used, but no self data specified"); >+ return null; >+ } >+ if(self == "self") { >+ CSPError("self reference cannot be 'self'"); >+ return null; >+ } >+ if(self instanceof CSPSource) { >+ return self.clone(); >+ } >+ return CSPSource.fromString(self); >+ } >+ >+ var s = aStr; >+ var sObj = new CSPSource(); >+ >+ if(self && !(self instanceof CSPSource)) { >+ self = CSPSource.fromString(self); >+ } >+ >+ // IMPORTANT: scan for port first, because host can be mistaken for scheme >+ // due to the allowance of dots in the scheme (and a colon before the port) >+ if(CSPSource._PORT_RE.test(s)) { >+ sObj._port = s.match(CSPSource._PORT_RE)[0].replace(/:/,''); >+ s = s.replace(CSPSource._PORT_RE, ''); >+ } else if(self) { >+ sObj._port = self._port; >+ } >+ >+ if(CSPSource._SCHM_RE.test(s)) { >+ sObj._scheme = s.match(CSPSource._SCHM_RE)[0].replace(/[:\/]+/,''); >+ s = s.replace(CSPSource._SCHM_RE, ''); >+ } else if(self) { >+ sObj._scheme = self._scheme; >+ } >+ >+ >+ if(CSPSource._HOST_RE.test(s)) { >+ sObj._host = CSPHost.fromString(s.match(CSPSource._HOST_RE)[0]); >+ if(sObj._host) { >+ return sObj; >+ } >+ } >+ CSPError("failed parsing host: '" + s + "'"); >+ return null; >+}; >+ >+CSPSource.prototype = { >+ >+ /** >+ * Generates string representation of the Source. >+ * Should be fairly similar to the original. >+ */ >+ toString: >+ function() { >+ var s = this._host; >+ if(this._port && this._port != "*") { >+ s = s + ":" + this._port; >+ } >+ if(this._scheme && this._scheme != "*") { >+ s = this._scheme + "://" + s; >+ } >+ return s; >+ }, >+ >+ /** >+ * Makes a new instance that resembles this object. >+ * @returns >+ * a new CSPSource >+ */ >+ clone: >+ function() { >+ var aClone = new CSPSource(); >+ aClone._scheme = this._scheme; >+ aClone._port = this._port; >+ aClone._host = this._host.clone(); >+ return aClone; >+ }, >+ >+ /** >+ * Determines if this Source accepts a URI. >+ * @param aURI >+ * the URI in question >+ * @returns >+ * true if the URI matches a source in this source list. >+ */ >+ permits: >+ function(aSource) { >+ if(aSource instanceof Components.interfaces.nsIURI) { >+ //figure out based on URI >+ return this.permits(CSPSource.fromURI(aSource)); >+ } >+ if(!(aSource instanceof CSPSource)) { >+ //convert to CSPSource first >+ return this.permits(CSPSource.fromString(aSource)); >+ } >+ >+ //verify scheme, host, and port >+ if( (this._scheme == "*" || this._scheme == aSource._scheme) >+ && (this._port == "*" || this._port == aSource._port) ){ >+ return this._host.permits(aSource._host); >+ } >+ // something didn't match. >+ return false; >+ }, >+ >+ /** >+ * Determines the intersection of two sources. >+ * Returns a null object if intersection generates no >+ * hosts that satisfy it. >+ * @param that >+ * the other CSPSource to intersect "this" with >+ * @returns >+ * a new instance of a CSPSource representing the intersection >+ */ >+ intersectWith: >+ function(that) { >+ var newSource = new CSPSource(); >+ >+ //port >+ if(this._port == "*") { >+ newSource._port = that._port; >+ } else if(that._port == "*") { >+ newSource._port = this._port; >+ } else if(that._port == this._port) { >+ newSource._port = this._port; >+ } else { >+ //CSPError("Could not intersect " + this + " with " + that >+ // + " due to port problems."); >+ return null; >+ } >+ >+ //scheme >+ if(this._scheme == "*") { >+ newSource._scheme = that._scheme; >+ } else if(that._scheme == "*") { >+ newSource._scheme = this._scheme; >+ } else if(that._scheme == this._scheme) { >+ newSource._scheme = this._scheme; >+ } else { >+ //CSPError("Could not intersect " + this + " with " + that >+ // + " due to scheme problems."); >+ return null; >+ } >+ >+ //host >+ newSource._host = this._host.intersectWith(that._host); >+ >+ return newSource._host ? newSource : null; >+ }, >+ >+ /** >+ * Compares one CSPSource to another. >+ * >+ * @param that >+ * another CSPSource >+ * @returns >+ * true if they have the same data >+ */ >+ equals: >+ function(aSrc) { >+ return this._scheme == aSrc._scheme >+ && this._port == aSrc._port >+ && this._host.equals(aSrc._host); >+ } >+}; >+ >+ >+////////////////////////////////////////////////////////////////////// >+/** >+ * Class to model a host *.x.y. >+ */ >+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) { >+ var hObj = new CSPHost(); >+ hObj._segments = aStr.split(/\./); >+ if(hObj._segments.length < 1 || >+ hObj._segments.length == 1 && hObj._segments[0] != "*" >+ && hObj._segments[0] != "localhost") { >+ //only short hosts allowed are "*" and "localhost" >+ return null; >+ } >+ //TODO: double-check that each segment is appropriate. >+ return hObj; >+}; >+ >+CSPHost.prototype = { >+ /** >+ * Generates string representation of the Source. >+ * Should be fairly similar to the original. >+ */ >+ toString: >+ function() { >+ return this._segments.join("."); >+ }, >+ >+ /** >+ * Makes a new instance that resembles 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 instanceof CSPHost)) { >+ // -- compare CSPHost to String >+ return this.permits(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] != aHost._segments[thatlen-i]) ) { >+ return false; >+ } >+ } >+ >+ //at this point, all conditions are met, so the host is allowed >+ return true; >+ }, >+ >+ /** >+ * Determines the intersection of two Hosts. >+ * Basically, they must be the same, or one must have a wildcard. >+ * @param that >+ * the other CSPHost to intersect "this" with >+ * @returns >+ * a new instance of a CSPHost representing the intersection >+ * (or null, if they can't be intersected) >+ */ >+ intersectWith: >+ function(that) { >+ if(!(this.permits(that) || that.permits(this))) { >+ // host definitions cannot co-exist without a more general host >+ // ... one must be a subset of the other, or intersection makes no sense. >+ return null; >+ } >+ >+ // pick the more specific one, if both are same length. >+ if(this._segments.length == that._segments.length) { >+ // *.a vs b.a : b.a >+ return (this._segments[0] == "*") ? that.clone() : this.clone(); >+ } >+ >+ //different lengths... >+ // *.b.a vs *.a : *.b.a >+ // *.b.a vs d.c.b.a : d.c.b.a >+ return (this._segments.length > that._segments.length) >+ ? this.clone() : that.clone(); >+ }, >+ >+ /** >+ * 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._segments.length; i++) { >+ if(this._segments[i] != that._segments[i]) { >+ return false; >+ } >+ } >+ return true; >+ } >+}; >diff -r 944b2cd986d9 content/base/src/Makefile.in >--- a/content/base/src/Makefile.in Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/src/Makefile.in Tue Jul 21 16:46:09 2009 -0700 >@@ -117,16 +117,17 @@ CPPSRCS = \ > nsContentAreaDragDrop.cpp \ > nsContentIterator.cpp \ > nsContentList.cpp \ > nsContentPolicy.cpp \ > nsContentSink.cpp \ > nsContentUtils.cpp \ > nsCopySupport.cpp \ > nsCrossSiteListenerProxy.cpp \ >+ nsCSPService.cpp \ > nsDataDocumentContentPolicy.cpp \ > nsDOMAttribute.cpp \ > nsDOMAttributeMap.cpp \ > nsDOMDocumentType.cpp \ > nsDOMFile.cpp \ > nsDOMLists.cpp \ > nsDOMParser.cpp \ > nsDOMSerializer.cpp \ >@@ -176,17 +177,24 @@ CPPSRCS = \ > $(NULL) > > GQI_SRCS = contentbase.gqi > > # we don't want the shared lib, but we want to force the creation of a > # static lib. > FORCE_STATIC_LIB = 1 > >-EXTRA_COMPONENTS = $(srcdir)/nsBadCertHandler.js >+EXTRA_COMPONENTS = \ >+ $(srcdir)/nsBadCertHandler.js \ >+ contentSecurityPolicy.js \ >+ $(NULL) >+ >+EXTRA_JS_MODULES = \ >+ CSPUtils.jsm \ >+ $(NULL) > > include $(topsrcdir)/config/rules.mk > > INCLUDES += \ > -I$(srcdir)/../../events/src \ > -I$(srcdir)/../../xml/content/src \ > -I$(srcdir)/../../xul/base/src \ > -I$(srcdir)/../../xul/content/src \ >diff -r 944b2cd986d9 content/base/src/contentSecurityPolicy.js >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/src/contentSecurityPolicy.js Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,359 @@ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is the nsCSPContentPolicy service. >+ * >+ * The Initial Developer of the Original Code is >+ * Sid Stamm <sid@mozilla.com> >+ * >+ * Contributor(s): >+ * Brandon Sterne <bsterne@mozilla.com> >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either the GNU General Public License Version 2 or later (the "GPL"), or >+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+ >+/** >+ * Content Security Policy >+ * >+ * Overview >+ * This is a stub component that will be fleshed out to do all the fancy stuff >+ * that ContentSecurityPolicy has to do. >+ */ >+ >+/* :::::::: Constants and Helpers ::::::::::::::: */ >+ >+const Cc = Components.classes; >+const Ci = Components.interfaces; >+const Cr = Components.results; >+const Cu = Components.utils; >+ >+Cu.import("resource://gre/modules/XPCOMUtils.jsm"); >+Cu.import("resource://gre/modules/CSPUtils.jsm"); >+ >+/* ::::: Policy Parsing & Data structures :::::: */ >+ >+function ContentSecurityPolicy() { >+ CSPdebug("CSP CREATED"); >+ this._isInitialized = false; >+ this._policy = CSPRep.fromString("allow *"); >+ this._requestHeaders = []; >+ this._request = ""; >+ CSPdebug("CSP POLICY INITED TO 'allow *'"); >+} >+ >+/* >+ * Set up mappings from nsIContentPolicy content types to CSP directives. >+ */ >+with(Ci.nsIContentPolicy) { >+ let csp = ContentSecurityPolicy; >+ let cspr_sd = CSPRep.SRC_DIRECTIVES; >+ >+ csp._MAPPINGS=[]; >+ >+ /* default, catch-all case */ >+ csp._MAPPINGS[TYPE_OTHER] = cspr_sd.ALLOW; >+ >+ /* self */ >+ csp._MAPPINGS[TYPE_DOCUMENT] = null; >+ >+ /* shouldn't see this one */ >+ csp._MAPPINGS[TYPE_REFRESH] = null; >+ >+ /* categorized content types */ >+ csp._MAPPINGS[TYPE_SCRIPT] = cspr_sd.SCRIPT_SRC; >+ csp._MAPPINGS[TYPE_IMAGE] = cspr_sd.IMG_SRC; >+ csp._MAPPINGS[TYPE_STYLESHEET] = cspr_sd.STYLE_SRC; >+ csp._MAPPINGS[TYPE_OBJECT] = cspr_sd.OBJECT_SRC; >+ csp._MAPPINGS[TYPE_SUBDOCUMENT] = cspr_sd.FRAME_SRC; >+ csp._MAPPINGS[TYPE_MEDIA] = cspr_sd.MEDIA_SRC; >+ >+ >+ /* These must go through the catch-all */ >+ csp._MAPPINGS[TYPE_XBL] = cspr_sd.ALLOW; >+ csp._MAPPINGS[TYPE_PING] = cspr_sd.ALLOW; >+ csp._MAPPINGS[TYPE_XMLHTTPREQUEST] = cspr_sd.ALLOW; >+ csp._MAPPINGS[TYPE_OBJECT_SUBREQUEST] = cspr_sd.ALLOW; >+ csp._MAPPINGS[TYPE_DTD] = cspr_sd.ALLOW; >+ csp._MAPPINGS[TYPE_FONT] = cspr_sd.ALLOW; >+} >+ >+ContentSecurityPolicy.prototype = { >+ classDescription: "Content Security Policy Component", >+ contractID: "@mozilla.org/contentsecuritypolicy;1", >+ classID: Components.ID("{AB36A2BF-CB32-4AA6-AB41-6B4E4444A221}"), >+ QueryInterface: XPCOMUtils.generateQI([Ci.IContentSecurityPolicy]), >+ >+ // get this contractID registered for certain categories via XPCOMUtils >+ _xpcom_categories: [ ], >+ >+ get isInitialized() { >+ return this._isInitialized; >+ }, >+ >+ set isInitialized (foo) { >+ this._isInitialized = foo; >+ }, >+ >+ get policy () { >+ return this._policy.toString(); >+ }, >+ >+ get allowsInlineScript() { >+ return this._policy.allowsInlineScripts; >+ }, >+ >+ get allowsEval() { >+ return this._policy.allowsEvalInScripts; >+ }, >+ >+ /* >+ // Having a setter is a bad idea... opens up the policy to "loosening" >+ // Instead, use "refinePolicy." >+ set policy (aStr) { >+ this._policy = CSPRep.fromString(aStr); >+ }, >+ */ >+ >+ /** >+ * Given an nsIHttpChannel, fill out the appropriate data. >+ */ >+ scanRequestData: >+ function(aChannel) { >+ // grab the request line >+ var internalChannel = aChannel.QueryInterface(Ci.nsIHttpChannelInternal); >+ var reqMaj = {}; >+ var reqMin = {}; >+ var reqVersion = internalChannel.getRequestVersion(reqMaj, reqMin); >+ this._request = aChannel.requestMethod + " " >+ + aChannel.URI >+ + " HTTP/" + reqMaj.value + "." + reqMin.value; >+ >+ // grab the request headers >+ var self = this; >+ aChannel.visitRequestHeaders({ >+ visitHeader: function(aHeader, aValue) { >+ self._requestHeaders.push(aHeader + ": " + aValue); >+ }}); >+ }, >+ >+/* ........ Methods .............. */ >+ >+ /** >+ * Given a new policy, intersects the currently enforced policy with the new >+ * one and stores the result. The effect is a "tightening" or refinement of >+ * an old policy. This is called any time a new policy is encountered and >+ * the effective policy has to be refined. >+ */ >+ refinePolicy: >+ function csp_refinePolicy(aPolicy, selfURI) { >+ CSPdebug("REFINE POLICY: " + aPolicy); >+ CSPdebug(" SELF: " + selfURI.asciiSpec); >+ >+ // stay uninitialized until policy merging is done >+ this._isInitialized = false; >+ >+ // If there is a policy-uri, fetch the policy, then re-call this function. >+ // (1) parse and create a CSPRep object >+ var newpolicy = CSPRep.fromString(aPolicy, >+ selfURI.scheme + "://" + selfURI.hostPort); >+ >+ // (2) Intersect the currently installed CSPRep object with the new one >+ var intersect = this._policy.intersectWith(newpolicy); >+ //TODO: make sure intersect worked >+ >+ // (3) Save the result >+ this._policy = intersect; >+ this._isInitialized = true; >+ }, >+ >+ /** >+ * Generates and sends a violation report to the specified report URIs. >+ */ >+ sendReports: >+ function(blockedUri, violatedDirective) { >+ var uris = this._policy.getReportURIs(); >+ if(uris.length > 0) { >+ // Generate report to send composed of: >+ // <csp-report> >+ // <request>GET /index.html HTTP/1.1</request> >+ // <request-headers>Host: example.com >+ // User-Agent: ... >+ // ... >+ // </request-headers> >+ // <blocked-uri>...</blocked-uri> >+ // <violated-directive>...</violated-directive> >+ // </csp-report> >+ // >+ var strHeaders = ""; >+ for(let i in this._requestHeaders) { >+ strHeaders += this._requestHeaders[i] + "\n"; >+ } >+ >+ var report = "<csp-report>\n" + >+ " <request>" + this._request + "</request>\n" + >+ " <request-headers><![CDATA[\n" + >+ strHeaders + >+ " ]]></request-headers>\n" + >+ " <blocked-uri>" + blockedUri + "</blocked-uri>\n" + >+ " <violated-directive>" + violatedDirective + "</violated-directive>\n" + >+ "</csp-report>\n"; >+ >+ // For each URI in the report list, send out a report. >+ for(let i in uris) { >+ var failure = function(aEvt) { >+ if(req.readyState == 4 && req.status != 200) { >+ CSPError("Failed to send report to " + reportURI); >+ } >+ }; >+ var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] >+ .createInstance(Ci.nsIXMLHttpRequest); >+ >+ req.open("POST", uris[i], true); >+ req.upload.addEventListener("error", failure, false); >+ req.upload.addEventListener("abort", failure, false); >+ //req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; >+ >+ // make request anonymous >+ // This prevents sending cookies with the request, >+ // in case the policy URI is injected, it can't be >+ // abused for CSRF. >+ req.channel.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS; >+ >+ req.send(report); >+ } >+ } >+ }, >+ >+ /** >+ * Exposed Method to analyze docShell for approved frame ancestry. >+ * 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. >+ */ >+ permitsAncestry: >+ function(docShell) { >+ if(!docShell) { return false; } >+ CSPdebug(" in permitsAncestry(), docShell = " + docShell); >+ >+ // 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; >+ } >+ let ancestor = it.currentURI.scheme + "://" + it.currentURI.hostPort; >+ CSPdebug(" found frame ancestor " + ancestor); >+ ancestors.push(ancestor); >+ } >+ } >+ >+ // scan the discovered ancestors >+ let cspContext = CSPRep.SRC_DIRECTIVES.FRAME_ANCESTORS; >+ for(let i in ancestors) { >+ if(!this._policy.permits(ancestors[i], cspContext)) { >+ // report the frame-ancestor violation >+ this.sendReports("self", cspContext +" "+ this._policy._directives[cspContext]); >+ return false; >+ } >+ } >+ return true; >+ }, >+ >+ /** >+ * 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, >+ aExtra) { >+ >+ //don't filter chrome stuff >+ if(aContentLocation.scheme == 'chrome') { >+ return Ci.nsIContentPolicy.ACCEPT; >+ } >+ >+ //interpret the context, and then pass off to the decision structure >+ CSPdebug("shouldLoad location = " + aContentLocation.asciiSpec); >+ CSPdebug("shouldLoad content type = " + aContentType); >+ var cspContext = ContentSecurityPolicy._MAPPINGS[aContentType]; >+ //CSPdebug("shouldLoad CSP directive =" + cspContext); >+ >+ // if the mapping is null, there's no policy, let it through. >+ if(!cspContext) { >+ return Ci.nsIContentPolicy.ACCEPT; >+ } >+ >+ // otherwise, honor the translation >+ //var source = aContentLocation.scheme + "://" + aContentLocation.hostPort; >+ var res = this._policy.permits(aContentLocation, cspContext) >+ ? Ci.nsIContentPolicy.ACCEPT >+ : Ci.nsIContentPolicy.REJECT_SERVER; >+ >+ // frame-ancestors is taken care of early on (as this document is loaded) >+ >+ // If the result is *NOT* ACCEPT, then send report >+ if(res != Ci.nsIContentPolicy.ACCEPT) { >+ CSPdebug("blocking request for " + aContentLocation.asciiSpec); >+ this.sendReports(aContentLocation, >+ cspContext + " " + this._policy._directives[cspContext]); >+ } >+ >+ return res; >+ }, >+ >+ 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; >+ }, >+ >+}; >+ >+ >+ >+function NSGetModule(aComMgr, aFileSpec) >+ XPCOMUtils.generateModule([ContentSecurityPolicy]); >diff -r 944b2cd986d9 content/base/src/nsCSPService.cpp >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/src/nsCSPService.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,203 @@ >+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is mozilla.org code. >+ * >+ * The Initial Developer of the Original Code is >+ * Mozilla Corporation. >+ * Portions created by the Initial Developer are Copyright (C) 2009 >+ * the Initial Developer. All Rights Reserved. >+ * >+ * Contributor(s): >+ * Brandon Sterne <bsterne@mozilla.com> >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either of the GNU General Public License Version 2 or later (the "GPL"), >+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+#include "nsIURI.h" >+#include "nsString.h" >+#include "nsCSPService.h" >+#include "nsIDocument.h" >+#include "nsIContent.h" >+#include "IContentSecurityPolicy.h" >+#include "nsContentUtils.h" >+#include "prlog.h" >+ >+/* Keeps track of whether or not CSP is enabled */ >+static PRBool gCSPEnabled = PR_FALSE; >+ >+#ifdef PR_LOGGING >+static PRLogModuleInfo* gCspPRLog; >+#endif >+ >+CSPService::CSPService() >+{ >+ gCSPEnabled = nsContentUtils::GetBoolPref("security.csp.enable", PR_TRUE); >+ nsContentUtils::AddBoolPrefVarCache("security.csp.enable", &gCSPEnabled); >+ >+#ifdef PR_LOGGING >+ if (!gCspPRLog) >+ gCspPRLog = PR_NewLogModule("CSP"); >+#endif >+} >+ >+CSPService::~CSPService() >+{ >+} >+ >+NS_IMPL_ISUPPORTS1(CSPService, nsIContentPolicy) >+ >+/* nsIContentPolicy implementation */ >+NS_IMETHODIMP >+CSPService::ShouldLoad(PRUint32 aContentType, >+ nsIURI *aContentLocation, >+ nsIURI *aRequestOrigin, >+ nsISupports *aRequestContext, >+ const nsACString &aMimeTypeGuess, >+ nsISupports *aExtra, >+ PRInt16 *aDecision) >+{ >+ if (!aContentLocation) >+ return NS_ERROR_FAILURE; >+ >+#ifdef PR_LOGGING >+ { >+ nsCAutoString location("None"); >+ aContentLocation->GetSpec(location); >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("CSPService::ShouldLoad called for %s", location.get())); >+ } >+#endif >+ // default decision, CSP can revise it if there's a policy to enforce >+ *aDecision = nsIContentPolicy::ACCEPT; >+ >+ // No need to continue processing if CSP is disabled >+ if (!gCSPEnabled) { >+ //printf(" *** CSPService -- CSP is disabled, returning.\n"); >+ return NS_OK; >+ } >+ >+ // find the nsDocument that initiated this request and see if it has a >+ // CSP policy object >+ nsCOMPtr<nsIDocument> doc; >+ nsCOMPtr<nsIContent> node(do_QueryInterface(aRequestContext)); >+ if (node) { >+ doc = node->GetOwnerDoc(); >+ } >+ if (!doc) { >+ doc = do_QueryInterface(aRequestContext); >+ } >+ >+ if (doc) { >+ IContentSecurityPolicy* csp = doc->GetContentSecurityPolicy(); >+ if (csp) { >+#ifdef PR_LOGGING >+ nsAutoString policy; >+ csp->GetPolicy(policy); >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("Document has CSP: %s", ToNewCString(policy))); >+#endif >+ // obtain the enforcement decision >+ csp->ShouldLoad(aContentType, >+ aContentLocation, >+ aRequestOrigin, >+ aRequestContext, >+ aMimeTypeGuess, >+ aExtra, >+ aDecision); >+ } >+ } >+#ifdef PR_LOGGING >+ else { >+ nsCAutoString uriSpec("None"); >+ aContentLocation->GetSpec(uriSpec); >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("COULD NOT get nsIDocument for location: %s", uriSpec.get())); >+ } >+#endif >+ >+ return NS_OK; >+} >+ >+NS_IMETHODIMP >+CSPService::ShouldProcess(PRUint32 aContentType, >+ nsIURI *aContentLocation, >+ nsIURI *aRequestOrigin, >+ nsISupports *aRequestContext, >+ const nsACString &aMimeTypeGuess, >+ nsISupports *aExtra, >+ PRInt16 *aDecision) >+{ >+ if (!aContentLocation) >+ return NS_ERROR_FAILURE; >+ >+ // default decision is to accept the item >+ *aDecision = nsIContentPolicy::ACCEPT; >+ >+ // No need to continue processing if CSP is disabled >+ if (!gCSPEnabled) { >+ //printf(" *** CSPService -- CSP is disabled, returning.\n"); >+ return NS_OK; >+ } >+ >+ // find the nsDocument that initiated this request and see if it has a >+ // CSP policy object >+ nsCOMPtr<nsIDocument> doc; >+ nsCOMPtr<nsIContent> node(do_QueryInterface(aRequestContext)); >+ if (node) { >+ doc = node->GetOwnerDoc(); >+ } >+ if (!doc) { >+ doc = do_QueryInterface(aRequestContext); >+ } >+ if (doc) { >+ IContentSecurityPolicy* csp = doc->GetContentSecurityPolicy(); >+ if (csp) { >+#ifdef PR_LOGGING >+ nsAutoString policy; >+ csp->GetPolicy(policy); >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("shouldProcess - document has policy: %s", ToNewCString(policy))); >+#endif >+ // obtain the enforcement decision >+ csp->ShouldProcess(aContentType, >+ aContentLocation, >+ aRequestOrigin, >+ aRequestContext, >+ aMimeTypeGuess, >+ aExtra, >+ aDecision); >+ } >+ } >+#ifdef PR_LOGGING >+ else { >+ nsCAutoString uriSpec("None"); >+ aContentLocation->GetSpec(uriSpec); >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("COULD NOT get nsIDocument for location: %s", uriSpec.get())); >+ } >+#endif >+ return NS_OK; >+} >diff -r 944b2cd986d9 content/base/src/nsCSPService.h >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/src/nsCSPService.h Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,67 @@ >+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is mozilla.org code. >+ * >+ * The Initial Developer of the Original Code is >+ * Mozilla Corporation. >+ * Portions created by the Initial Developer are Copyright (C) 2009 >+ * the Initial Developer. All Rights Reserved. >+ * >+ * Contributor(s): >+ * Brandon Sterne <bsterne@mozilla.com> >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either of the GNU General Public License Version 2 or later (the "GPL"), >+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+#include "nsIGenericFactory.h" >+ >+#include "nsCOMPtr.h" >+#include "nsXPCOM.h" >+#include "nsIServiceManager.h" >+#include "nsICategoryManager.h" >+ >+#include "nsMemory.h" >+ >+#include "nsIContentPolicy.h" >+#include "nsIObserver.h" >+ >+#define CSPSERVICE_CONTRACTID "@mozilla.org/cspservice;1" >+#define CSPSERVICE_CID \ >+{ 0x8d2f40b2, 0x4875, 0x4c95, \ >+ { 0x97, 0xd9, 0x3f, 0x7d, 0xca, 0x2c, 0xb4, 0x60 } } >+ >+class CSPService : public nsIContentPolicy >+{ >+public: >+ NS_DECL_ISUPPORTS >+ NS_DECL_NSICONTENTPOLICY >+ >+ CSPService(); >+ virtual ~CSPService(); >+ >+private: >+ PRBool mEnabled; >+}; >diff -r 944b2cd986d9 content/base/src/nsDocument.cpp >--- a/content/base/src/nsDocument.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/src/nsDocument.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -174,25 +174,29 @@ static NS_DEFINE_CID(kDOMEventGroupCID, > #include "mozAutoDocUpdate.h" > > #ifdef MOZ_SMIL > #include "nsSMILAnimationController.h" > #include "imgIContainer.h" > #include "nsSVGUtils.h" > #endif // MOZ_SMIL > >+// FOR CSP (autogenerated by xpidl) >+#include "IContentSecurityPolicy.h" >+ > > #ifdef MOZ_LOGGING > // so we can get logging even in release builds > #define FORCE_PR_LOG 1 > #endif > #include "prlog.h" > > #ifdef PR_LOGGING > static PRLogModuleInfo* gDocumentLeakPRLog; >+static PRLogModuleInfo* gCspPRLog; > #endif > > void > nsUint32ToContentHashEntry::Destroy() > { > HashSet* set = GetHashSet(); > if (set) { > delete set; >@@ -1473,16 +1477,20 @@ nsDocument::nsDocument(const char* aCont > > #ifdef PR_LOGGING > if (!gDocumentLeakPRLog) > gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak"); > > if (gDocumentLeakPRLog) > PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, > ("DOCUMENT %p created", this)); >+ >+ if (!gCspPRLog) >+ gCspPRLog = PR_NewLogModule("CSP"); >+ > #endif > > // Start out mLastStyleSheetSet as null, per spec > SetDOMStringToNull(mLastStyleSheetSet); > } > > static PLDHashOperator > ClearAllBoxObjects(const void* aKey, nsPIBoxObject* aBoxObject, void* aUserArg) >@@ -2230,16 +2238,101 @@ nsDocument::StartDocumentLoad(const char > semicolon = start; > FindCharInReadable(';', semicolon, end); > mContentType = Substring(start, semicolon); > } > > RetrieveRelevantHeaders(aChannel); > > mChannel = aChannel; >+ >+// -------------- Initialize CSP >+ >+ PRBool cspEnabled; >+ cspEnabled = nsContentUtils::GetBoolPref("security.csp.enable", PR_FALSE); >+ >+ if(!cspEnabled) { >+ // CSP not enabled, don't do all this CSP init stuff. >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("CSP is disabled, skipping CSP init for document %p", this)); >+#endif >+ return NS_OK; >+ } >+ >+ nsresult rv; >+ PRBool system; >+ nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager(); >+ nsAutoString cspHeaderValue; >+ nsIAtom* cspHeaderAtom = NS_NewAtom("x-content-security-policy"); >+ >+ this->GetHeaderData(cspHeaderAtom, cspHeaderValue); >+ >+ if(NS_SUCCEEDED(ssm->IsSystemPrincipal(NodePrincipal(), &system)) && system) { >+ //only makes sense to register new CSP if this document is not priviliged >+ return NS_OK; >+ } >+ >+ if(cspHeaderValue.IsEmpty()) { >+ // no CSP header present >+ return NS_OK; >+ } >+ >+ >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("CSP header specified for document %p", this)); >+#endif >+ >+ mCSP = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv); >+ >+ if(NS_FAILED(rv)) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %6x", rv)); >+#endif >+ return rv; >+ } >+ >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, >+ ("CSP created, policy: \"%s\"", ToNewCString(cspHeaderValue))); >+#endif >+ >+ // Store the request context for violation reports >+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); >+ mCSP->ScanRequestData(httpChannel); >+ >+ // Start parsing the policy >+ nsCOMPtr<nsIURI> chanURI; >+ aChannel->GetURI(getter_AddRefs(chanURI)); >+ mCSP->RefinePolicy(cspHeaderValue, chanURI); >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP Policy refined")); >+#endif >+ >+ // Check for frame-ancestor violation >+ nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer); >+ if(docShell != NULL) { >+ //PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP has a doc shell!")); >+ >+ PRBool safeAncestry = false; >+ // PermitsAncestry sends violation reports when necessary >+ rv = mCSP->PermitsAncestry(docShell, &safeAncestry); >+ if(NS_FAILED(rv)) { return rv; } >+ >+ if(!safeAncestry) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP doesn't like frame's ancestry, not loading.")); >+#endif >+ // stop! ERROR page! >+ aChannel->Cancel( NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, 99) ); >+ } >+ } >+ >+// -------------- CSP initialized > > return NS_OK; > } > > void > nsDocument::StopDocumentLoad() > { > if (mParser) { >@@ -6567,16 +6660,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC > > static const char *const headers[] = { > "default-style", > "content-style-type", > "content-language", > "content-disposition", > "refresh", > "x-dns-prefetch-control", >+ "x-content-security-policy", > // add more http headers if you need > // XXXbz don't add content-location support without reading bug > // 238654 and its dependencies/dups first. > 0 > }; > > nsCAutoString headerVal; > const char *const *name = headers; >@@ -6870,16 +6964,19 @@ nsDocument::CanSavePresentation(nsIReque > > void > nsDocument::Destroy() > { > // The ContentViewer wants to release the document now. So, tell our content > // to drop any references to the document so that it can be destroyed. > if (mIsGoingAway) > return; >+ >+ // Take down CSP >+ mCSP = 0; > > mIsGoingAway = PR_TRUE; > > RemovedFromDocShell(); > > PRUint32 i, count = mChildren.ChildCount(); > for (i = 0; i < count; ++i) { > mChildren.ChildAt(i)->DestroyContent(); >diff -r 944b2cd986d9 content/base/src/nsScriptLoader.cpp >--- a/content/base/src/nsScriptLoader.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/src/nsScriptLoader.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -65,16 +65,22 @@ > #include "nsUnicharUtils.h" > #include "nsAutoPtr.h" > #include "nsIXPConnect.h" > #include "nsContentErrors.h" > #include "nsIParser.h" > #include "nsThreadUtils.h" > #include "nsIChannelClassifier.h" > #include "nsDocShellCID.h" >+#include "prlog.h" >+ >+#ifdef PR_LOGGING >+static PRLogModuleInfo* gCspPRLog; >+#endif >+ > > ////////////////////////////////////////////////////////////// > // Per-request data structure > ////////////////////////////////////////////////////////////// > > class nsScriptLoadRequest : public nsISupports { > public: > nsScriptLoadRequest(nsIScriptElement* aElement, >@@ -122,18 +128,38 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLo > // > ////////////////////////////////////////////////////////////// > > nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) > : mDocument(aDocument), > mBlockerCount(0), > mEnabled(PR_TRUE), > mDeferEnabled(PR_FALSE), >- mUnblockOnloadWhenDoneProcessing(PR_FALSE) >+ mUnblockOnloadWhenDoneProcessing(PR_FALSE), >+ mInlineScriptsAllowed(PR_TRUE) > { >+ // enable logging for CSP >+#ifdef PR_LOGGING >+ if (!gCspPRLog) >+ gCspPRLog = PR_NewLogModule("CSP"); >+#endif >+ >+ IContentSecurityPolicy *csp = mDocument->GetContentSecurityPolicy(); >+ if (csp) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****")); >+#endif >+ PRBool inlineOK; >+ if (NS_SUCCEEDED(csp->GetAllowsInlineScript(&inlineOK))) { >+ mInlineScriptsAllowed = inlineOK; >+ } >+ } >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i NO CSP")); >+#endif > } > > nsScriptLoader::~nsScriptLoader() > { > mObservers.Clear(); > > for (PRInt32 i = 0; i < mRequests.Count(); i++) { > mRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); >@@ -518,16 +544,36 @@ nsScriptLoader::ProcessScriptElement(nsI > !request->mDefer) { > nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsScriptLoader>(this, > &nsScriptLoader::ProcessPendingRequests)); > } > > return request->mDefer ? NS_OK : NS_ERROR_HTMLPARSER_BLOCK; > } > } >+ else { >+ // in-line script >+ IContentSecurityPolicy *csp = mDocument->GetContentSecurityPolicy(); >+ if (csp) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("ScriptLoader's document has CSP")); >+#endif >+ PRBool inlineOK = PR_TRUE; >+ csp->GetAllowsInlineScript(&inlineOK); >+ if (!inlineOK) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, (" CSP blocked inline scripts")); >+#endif >+ return NS_ERROR_FAILURE; >+ } >+ } >+#ifdef PR_LOGGING >+ else PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("ScriptLoader's document NO CSP")); >+#endif >+ } > > // Create a request object for this script > request = new nsScriptLoadRequest(aElement, version); > NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY); > > request->mDefer = mDeferEnabled && aElement->GetScriptDeferred(); > > // First check to see if this is an external script >@@ -536,16 +582,23 @@ nsScriptLoader::ProcessScriptElement(nsI > request->mIsInline = PR_FALSE; > request->mLoading = PR_TRUE; > > rv = StartLoad(request, type); > if (NS_FAILED(rv)) { > return rv; > } > } else { >+ // in-line script >+ if (!mInlineScriptsAllowed) { >+#ifdef PR_LOGGING >+ PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts --2nd chance!")); >+#endif >+ return NS_ERROR_FAILURE; >+ } > request->mLoading = PR_FALSE; > request->mIsInline = PR_TRUE; > request->mURI = mDocument->GetDocumentURI(); > > request->mLineNo = aElement->GetScriptLineNumber(); > > // If we've got existing pending requests, add ourselves > // to this list. >diff -r 944b2cd986d9 content/base/src/nsScriptLoader.h >--- a/content/base/src/nsScriptLoader.h Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/base/src/nsScriptLoader.h Tue Jul 21 16:46:09 2009 -0700 >@@ -321,11 +321,12 @@ protected: > > nsCOMPtr<nsIScriptElement> mCurrentScript; > // XXXbz do we want to cycle-collect these or something? Not sure. > nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders; > PRUint32 mBlockerCount; > PRPackedBool mEnabled; > PRPackedBool mDeferEnabled; > PRPackedBool mUnblockOnloadWhenDoneProcessing; >+ PRPackedBool mInlineScriptsAllowed; > }; > > #endif //__nsScriptLoader_h__ >diff -r 944b2cd986d9 content/base/test/unit/test_csputils.js >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/content/base/test/unit/test_csputils.js Tue Jul 21 16:46:09 2009 -0700 >@@ -0,0 +1,472 @@ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is the Content Security Policy Data Structures testing code. >+ * >+ * The Initial Developer of the Original Code is >+ * Sid Stamm <sid@mozilla.com> >+ * >+ * Contributor(s): >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either the GNU General Public License Version 2 or later (the "GPL"), or >+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+//load('CSPUtils.jsm'); >+Components.utils.import('resource://gre/modules/CSPUtils.jsm'); >+ >+// load the HTTP server >+do_load_httpd_js(); >+ >+var httpServer = new nsHttpServer(); >+ >+const POLICY_FROM_URI = "allow self; img-src *"; >+const POLICY_PORT = 9000; >+const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy"; >+ >+// 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 keys) { 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("com"); >+ do_check_eq(null, h); // "lone symbol should 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" >+ >+ // Not enforced in CSPHost >+ //h = CSPHost.fromString("foo!bar.com"); >+ //ASSERT_NULL(h, "special chars in hosts should fail"); >+ }); >+ >+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_true(!h.permits("b.c")); //"CSPHost *.b.c should not allow string b.c" >+ do_check_true(!h.permits("a.a.c")); //"CSPHost *.b.c should not allow string a.a.c" >+ do_check_true(!h2.permits(h)); //"CSPHost a.b.c should not allow CSPHost *.b.c" >+ do_check_true(!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( >+ function test_CSPHost_intersectWith() { >+ var h = CSPHost.fromString("*.b.c"); >+ //"*.a.b.c ^ *.b.c should be *.a.b.c" >+ do_check_eq("*.a.b.c", h.intersectWith(CSPHost.fromString("*.a.b.c")).toString()); >+ >+ //"*.b.c ^ *.d.e should not work (null)" >+ do_check_eq(null, h.intersectWith(CSPHost.fromString("*.d.e"))); >+ }); >+ >+///////////////////// Test the Source object ////////////////////// >+ >+test( >+ function test_CSPSource_fromString() { >+ //"basic source should not be null."); >+ /* >+ do_check_neq(null, CSPSource.fromString("a.com")); >+ >+ //"ldh characters should all work for host."); >+ do_check_neq(null, CSPSource.fromString("a2-c.com")); >+ >+ //"wildcard should work in first token for host."); >+ do_check_neq(null, CSPSource.fromString("*.a.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")); >+ >+ //"funny characters (#) should not work for host, _HOST_RE is probably broken."); >+ do_check_eq(null, CSPSource.fromString("a#2-c.com")); >+ //print(" --- Stop ignoring errors that print ---\n"); >+ */ >+ >+ //"failed to parse host with port."); >+ do_check_neq(null, CSPSource.fromString("a.com:23")); >+ //"failed to parse host with scheme."); >+ do_check_neq(null, CSPSource.fromString("https://a.com")); >+ //"failed to parse host with scheme and port."); >+ do_check_neq(null, CSPSource.fromString("https://a.com:200")); >+ }); >+ >+test( >+ function test_CSPSource_fromString_withSelf() { >+ var src; >+ src = CSPSource.fromString("a.com", "https://self.com"); >+ //"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.fromString("self", "https://self.com"); >+ //"src should inherit port * >+ do_check_true(src.permits("https://self.com:443")); >+ //"src should inherit and require https scheme >+ do_check_false(src.permits("http://self.com")); >+ //"src should inherit scheme 'https'" >+ do_check_true(src.permits("https://self.com")); >+ //"src should reject other hosts" >+ do_check_false(src.permits("https://a.com")); >+ }); >+ >+///////////////////// 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); >+ >+ sd = CSPSourceList.fromString("*"); >+ //"'*' should be a one-length list" >+ do_check_eq(1, sd._sources.length); >+ >+ //print(" --- Ignore the following three errors if they print ---"); >+ //"funny char in host" >+ do_check_eq(null,CSPSourceList.fromString("f!oo.bar")); >+ //"funny char in scheme" >+ do_check_eq(null,CSPSourceList.fromString("ht1ps://f-oo.bar")); >+ //"funny char in port" >+ do_check_eq(null,CSPSourceList.fromString("https://f-oo.bar:3f")); >+ //print(" --- Stop ignoring errors that print ---\n"); >+ }); >+ >+test( >+ function test_CSPSourceList_fromString_twohost() { >+ var str = "foo.bar:21 https://ras.bar"; >+ var sd = CSPSourceList.fromString(str); >+ //"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(str, sd.toString()); >+ }); >+ >+test( >+ function test_CSPSourceList_permits() { >+ var nullSourceList = CSPSourceList.fromString("none"); >+ var simpleSourceList = CSPSourceList.fromString("a.com"); >+ var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88"); >+ var allSourceList = CSPSourceList.fromString("*"); >+ >+ //'none' should permit none." >+ do_check_true( !nullSourceList.permits("a.com")); >+ //a.com should permit a.com" >+ do_check_true( simpleSourceList.permits("a.com")); >+ //wrong host" >+ do_check_true( !simpleSourceList.permits("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_true( !doubleSourceList.permits("https://bar.com:88")); >+ //double list does not permit http://bar.com:443" >+ do_check_true( !doubleSourceList.permits("http://bar.com:443")); >+ //"double list permits https://foo.com:88" >+ do_check_true( doubleSourceList.permits("https://foo.com:88")); >+ //"double list does not permit foo.com" >+ do_check_true( !doubleSourceList.permits("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.x.com")); >+ >+ }); >+ >+test( >+ function test_CSPSourceList_intersect() { >+ var nullSourceList = CSPSourceList.fromString("none"); >+ var simpleSourceList = CSPSourceList.fromString("a.com"); >+ var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88"); >+ var singleFooSourceList = CSPSourceList.fromString("https://foo.com"); >+ var allSourceList = CSPSourceList.fromString("*"); >+ >+ //"Intersection of one source with 'none' source list should be none."); >+ do_check_true(nullSourceList.intersectWith(simpleSourceList).isNone()); >+ //"Intersection of two sources with 'none' source list should be none."); >+ do_check_true(nullSourceList.intersectWith(doubleSourceList).isNone()); >+ //"Intersection of '*' with 'none' source list should be none."); >+ do_check_true(nullSourceList.intersectWith(allSourceList).isNone()); >+ >+ //"Intersection of one source with '*' source list should be one source."); >+ do_check_equivalent(allSourceList.intersectWith(simpleSourceList), >+ simpleSourceList); >+ //"Intersection of two sources with '*' source list should be two sources."); >+ do_check_equivalent(allSourceList.intersectWith(doubleSourceList), >+ doubleSourceList); >+ >+ //"Non-overlapping source lists should intersect to 'none'"); >+ do_check_true(simpleSourceList.intersectWith(doubleSourceList).isNone()); >+ >+ //"subset and superset should intersect to subset."); >+ do_check_equivalent(singleFooSourceList, >+ doubleSourceList.intersectWith(singleFooSourceList)); >+ >+ //TODO: write more tests? >+ >+ }); >+ >+///////////////////// Test the Whole CSP rep object ////////////////////// >+ >+test( >+ function test_CSPRep_fromString() { >+ >+ // check default init >+ //ASSERT(!(new CSPRep())._isInitialized, "Uninitialized rep thinks it is.") >+ >+ var cspr; >+ var cspr_allowval; >+ >+ // check default policy "allow *" >+ cspr = CSPRep.fromString("allow *"); >+ //"ALLOW directive is missing when specified in fromString" >+ do_check_has_key(cspr._directives, CSPRep.SRC_DIRECTIVES.ALLOW); >+ >+ // ... and check that the other directives were auto-filled with the >+ // ALLOW one. >+ var SD = CSPRep.SRC_DIRECTIVES; >+ cspr_allowval = cspr._directives[SD.ALLOW]; >+ for(var d in CSPRep.SRC_DIRECTIVES) { >+ //"Missing key " + d >+ do_check_has_key(cspr._directives, SD[d]); >+ //"Implicit directive " + d + " has non-allow value." >+ do_check_eq(cspr._directives[SD[d]].toString(), cspr_allowval.toString()); >+ } >+ }); >+ >+ >+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_ANCESTORS, SD.FRAME_SRC]; >+ >+ // check one-directive policies >+ cspr = CSPRep.fromString("allow bar.com; script-src https://foo.com"); >+ >+ for(var x in DEFAULTS) { >+ //DEFAULTS[x] + " does not use default rule." >+ do_check_true(cspr.permits("http://bar.com:22", DEFAULTS[x])); >+ //DEFAULTS[x] + " does not use default rule." >+ do_check_false(cspr.permits("https://foo.com:400", 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:400", 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_ANCESTORS, SD.FRAME_SRC]; >+ >+ // check two-directive policies >+ cspr = CSPRep.fromString("allow allow.com; " >+ + "script-src https://foo.com; " >+ + "img-src bar.com"); >+ for(var x in DEFAULTS) { >+ do_check_true(cspr.permits("http://allow.com:22", 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:400", 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:400", SD.SCRIPT_SRC)); >+ //"script-src does not use default rule. >+ do_check_false(cspr.permits("http://bar.com:400", SD.SCRIPT_SRC)); >+ }); >+ >+test(function test_CSPRep_fromString_withself() { >+ var cspr; >+ var SD = CSPRep.SRC_DIRECTIVES; >+ var self = "https://self.com:34"; >+ >+ // check one-directive policies >+ cspr = CSPRep.fromString("allow self; script-src self https://*:*", >+ 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 POLICY_URI ////////////////////// >+test(function test_CSPRep_fromPolicyURI() { >+ var cspr; >+ var SD = CSPRep.SRC_DIRECTIVES; >+ var self = "http://localhost:" + POLICY_PORT; >+ >+ cspr = CSPRep.fromString("policy-uri " + POLICY_URI, self); >+ cspr_static = CSPRep.fromString(POLICY_FROM_URI, self); >+ >+ //"policy-uri failed to load" >+ do_check_neq(null,cspr); >+ >+ // other directives inherit self >+ for(var i in SD) { >+ //SD[i] + " parsed wrong from policy uri" >+ do_check_equivalent(cspr._directives[SD[i]], >+ cspr_static._directives[SD[i]]); >+ } >+ }); >+/* >+ >+test(function test_CSPRep_fromPolicyURI_failswhenmixed() { >+ var cspr; >+ var self = "http://localhost:" + POLICY_PORT; >+ var closed_policy = CSPRep.fromString("allow none"); >+ var my_uri_policy = "policy-uri " + POLICY_URI; >+ >+ //print(" --- Ignore the following two errors if they print ---"); >+ cspr = CSPRep.fromString("allow *; " + my_uri_policy, self); >+ >+ //"Parsing should fail when 'policy-uri' is mixed with allow directive" >+ do_check_equivalent(cspr, closed_policy); >+ cspr = CSPRep.fromString("img-src self; " + my_uri_policy, 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); >+ httpServer.start(POLICY_PORT); >+ >+ for(let i in tests) { >+ tests[i](); >+ } >+ >+ //teardown >+ httpServer.stop(function() { }); >+ do_test_finished(); >+} >+ >+ >+ >diff -r 944b2cd986d9 content/events/src/nsEventListenerManager.cpp >--- a/content/events/src/nsEventListenerManager.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/content/events/src/nsEventListenerManager.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -87,16 +87,17 @@ > #include "nsContentUtils.h" > #include "nsJSUtils.h" > #include "nsIDOMEventGroup.h" > #include "nsContentCID.h" > #include "nsEventDispatcher.h" > #include "nsDOMJSUtils.h" > #include "nsDOMScriptObjectHolder.h" > #include "nsDataHashtable.h" >+#include "IContentSecurityPolicy.h" > > #define EVENT_TYPE_EQUALS( ls, type, userType ) \ > (ls->mEventType && ls->mEventType == type && \ > (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType)) > > #define EVENT_TYPE_DATA_EQUALS( type1, type2 ) \ > (type1 && type2 && type1->iid && type2->iid && \ > type1->iid->Equals(*(type2->iid))) >@@ -684,16 +685,29 @@ nsEventListenerManager::AddScriptEventLi > } > } > > if (!global) { > // This can happen; for example this document might have been > // loaded as data. > return NS_OK; > } >+ >+ // return early preventing the event listener from being added >+ if (doc) { >+ IContentSecurityPolicy* csp = doc->GetContentSecurityPolicy(); >+ if (csp) { >+ nsAutoString attr; >+ aName->ToString(attr); >+ //printf(" *** CSP bailing on adding event listener for: %s\n", >+ // ToNewCString(attr)); >+ //TODO: send CSP error report >+ return NS_OK; >+ } >+ } > > // This might be the first reference to this language in the global > // We must init the language before we attempt to fetch its context. > if (NS_FAILED(global->EnsureScriptEnvironment(aLanguage))) { > NS_WARNING("Failed to setup script environment for this language"); > // but fall through and let the inevitable failure below handle it. > } > >diff -r 944b2cd986d9 docshell/base/nsDocShell.cpp >--- a/docshell/base/nsDocShell.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/docshell/base/nsDocShell.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -3559,16 +3559,24 @@ nsDocShell::DisplayLoadError(nsresult aE > NS_ENSURE_ARG_POINTER(aURI); > // Get the host > nsCAutoString host; > aURI->GetHost(host); > CopyUTF8toUTF16(host, formatStrs[0]); > formatStrCount = 1; > error.AssignLiteral("netTimeout"); > } >+ else if ( NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY >+ && NS_ERROR_GET_CODE(aError) == 99) { >+ // CSP error >+ error.AssignLiteral("forbiddenFrameAncestor"); >+ cssClass.AssignLiteral("neterror"); >+ // TODO: localize this, or at least put the message into a resource >+ messageStr.AssignLiteral("CSP Blocked loading of this page because it doesn't trust the page embedding it."); >+ } > else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { > nsCOMPtr<nsINSSErrorsService> nsserr = > do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID); > > PRUint32 errorClass; > if (!nsserr || > NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) { > errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL; >diff -r 944b2cd986d9 dom/base/nsJSTimeoutHandler.cpp >--- a/dom/base/nsJSTimeoutHandler.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/dom/base/nsJSTimeoutHandler.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -238,16 +238,40 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW > ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)", > *aIsInterval ? kSetIntervalStr : kSetTimeoutStr); > > // Return an error that nsGlobalWindow can recognize and turn into NS_OK. > return NS_ERROR_DOM_TYPE_ERR; > } > > if (expr) { >+ // if CSP is enabled, and setTimeout/setInterval was called with a string >+ // or object, disable the registration and log an error >+ nsCOMPtr<nsIDOMDocument> domdoc; >+ aWindow->GetDocument(getter_AddRefs(domdoc)); >+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc); >+ >+ if (doc) { >+ IContentSecurityPolicy* csp = doc->GetContentSecurityPolicy(); >+ if(csp) { >+ PRBool allowsEval; >+ rv = csp->GetAllowsEval(&allowsEval); >+ NS_ENSURE_SUCCESS(rv, rv); >+ if (csp && !allowsEval) { >+ // CSP policy does NOT allow "eval" >+ // XXX: we should make this error report a little more informative. >+ csp->SendReports( >+ NS_LITERAL_STRING("self"), >+ NS_LITERAL_STRING("setInterval or setTimeout with string")); >+ return NS_OK; >+ } >+ } >+ } // if there's no document, we don't have to do anything. >+ >+ > rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); > NS_ENSURE_SUCCESS(rv, rv); > > mExpr = expr; > > nsIPrincipal *prin = aWindow->GetPrincipal(); > > // Get the calling location. >diff -r 944b2cd986d9 layout/build/nsLayoutModule.cpp >--- a/layout/build/nsLayoutModule.cpp Fri Jul 17 16:05:04 2009 -0700 >+++ b/layout/build/nsLayoutModule.cpp Tue Jul 21 16:46:09 2009 -0700 >@@ -268,16 +268,17 @@ static nsresult Initialize(nsIModule* aS > static void Shutdown(); > > #ifdef MOZ_XTF > #include "nsIXTFService.h" > #include "nsIXMLContentBuilder.h" > #endif > > #include "nsGeolocation.h" >+#include "nsCSPService.h" > > // Transformiix > /* {0C351177-0159-4500-86B0-A219DFDE4258} */ > #define TRANSFORMIIX_XPATH1_SCHEME_CID \ > { 0xc351177, 0x159, 0x4500, { 0x86, 0xb0, 0xa2, 0x19, 0xdf, 0xde, 0x42, 0x58 } } > > /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */ > #define TRANSFORMIIX_NODESET_CID \ >@@ -850,16 +851,90 @@ CreateWindowControllerWithSingletonComma > NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScriptObjectFactory) > NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseDOMException) > > #define NS_GEOLOCATION_SERVICE_CID \ > { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } } > > NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService) > >+static NS_METHOD >+CSPServiceRegistration(nsIComponentManager *aCompMgr, >+ nsIFile *aPath, >+ const char *registryLocation, >+ const char *componentType, >+ const nsModuleComponentInfo *info) >+{ >+ nsresult rv; >+ nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv); >+ if (NS_FAILED(rv)) >+ return rv; >+ >+ nsCOMPtr<nsICategoryManager> catman; >+ rv = servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID, >+ NS_GET_IID(nsICategoryManager), >+ getter_AddRefs(catman)); >+ if (NS_FAILED(rv)) >+ return rv; >+ >+ char* previous = nsnull; >+ rv = catman->AddCategoryEntry("xpcom-startup", >+ "CSPService", >+ CSPSERVICE_CONTRACTID, >+ PR_TRUE, >+ PR_TRUE, >+ &previous); >+ if (previous) >+ nsMemory::Free(previous); >+ >+ rv = catman->AddCategoryEntry("content-policy", >+ "CSPService", >+ CSPSERVICE_CONTRACTID, >+ PR_TRUE, >+ PR_TRUE, >+ &previous); >+ if (previous) >+ nsMemory::Free(previous); >+ >+ return rv; >+} >+ >+static NS_METHOD >+CSPServiceUnregistration(nsIComponentManager *aCompMgr, >+ nsIFile *aPath, >+ const char *registryLocation, >+ const nsModuleComponentInfo *info){ >+ nsresult rv; >+ >+ nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv); >+ if (NS_FAILED(rv)) >+ return rv; >+ >+ nsCOMPtr<nsICategoryManager> catman; >+ rv = servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID, >+ NS_GET_IID(nsICategoryManager), >+ getter_AddRefs(catman)); >+ if (NS_FAILED(rv)) >+ return rv; >+ >+ rv = catman->DeleteCategoryEntry("xpcom-startup", >+ "CSPService", >+ PR_TRUE); >+ >+ rv = catman->DeleteCategoryEntry("content-policy", >+ "CSPService", >+ PR_TRUE); >+ >+ return rv; >+} >+ >+NS_GENERIC_FACTORY_CONSTRUCTOR(CSPService) >+ >+ >+ > // The list of components we register > static const nsModuleComponentInfo gComponents[] = { > #ifdef DEBUG > { "Frame utility", > NS_FRAME_UTIL_CID, > nsnull, > CreateNewFrameUtil }, > { "Layout debugger", >@@ -1454,17 +1529,23 @@ static const nsModuleComponentInfo gComp > nsTextServicesDocumentConstructor }, > #endif > > { "Geolocation Service", > NS_GEOLOCATION_SERVICE_CID, > "@mozilla.org/geolocation/service;1", > nsGeolocationServiceConstructor }, > >- >+ { "Content Security Policy Service", >+ CSPSERVICE_CID, >+ CSPSERVICE_CONTRACTID, >+ CSPServiceConstructor, >+ CSPServiceRegistration, >+ CSPServiceUnregistration >+ }, > > { "Focus Manager", > NS_FOCUSMANAGER_CID, > "@mozilla.org/focus-manager;1", > CreateFocusManager }, > }; > > NS_IMPL_NSGETMODULE_WITH_CTOR(nsLayoutModule, gComponents, Initialize) >diff -r 944b2cd986d9 modules/libpref/src/init/all.js >--- a/modules/libpref/src/init/all.js Fri Jul 17 16:05:04 2009 -0700 >+++ b/modules/libpref/src/init/all.js Tue Jul 21 16:46:09 2009 -0700 >@@ -919,16 +919,18 @@ pref("images.dither", "auto"); > pref("security.directory", ""); > > pref("signed.applets.codebase_principal_support", false); > pref("security.checkloaduri", true); > pref("security.xpconnect.plugin.unrestricted", true); > // security-sensitive dialogs should delay button enabling. In milliseconds. > pref("security.dialog_enable_delay", 2000); > >+pref("security.csp.enable", true); >+ > // Modifier key prefs: default to Windows settings, > // menu access key = alt, accelerator key = control. > // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 0 for none. Mac settings in macprefs.js > pref("ui.key.accelKey", 17); > pref("ui.key.menuAccessKey", 18); > pref("ui.key.generalAccessKey", -1); > > // If generalAccessKey is -1, use the following two prefs instead.
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Actions:
View
|
Diff
|
Review
Attachments on
bug 493857
:
378464
|
378466
|
382041
|
382990
|
384026
|
387098
|
387470
|
387569
|
388837
|
389817
|
390366
|
394402
|
394403
|
394589
|
395460
|
396000