mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 836922 - support mulitiple CSP policies at the same time. r=jst,grobinson
This commit is contained in:
parent
c17399e56b
commit
3004080c1a
@ -10,10 +10,12 @@ interface nsIDocShell;
|
||||
|
||||
/**
|
||||
* nsIContentSecurityPolicy
|
||||
* Describes an XPCOM component used to model an enforce CSPs.
|
||||
* Describes an XPCOM component used to model and enforce CSPs. Instances of
|
||||
* this class may have multiple policies within them, but there should only be
|
||||
* one of these per document/principal.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(230b126d-afc3-4588-9794-3e135594d626)]
|
||||
[scriptable, uuid(e5020ec3-1437-46f5-b4eb-8b60766d02c0)]
|
||||
interface nsIContentSecurityPolicy : nsISupports
|
||||
{
|
||||
|
||||
@ -25,61 +27,87 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
attribute boolean isInitialized;
|
||||
|
||||
/**
|
||||
* When set to true, content load-blocking and fail-closed are disabled: CSP
|
||||
* will ONLY send reports, and not modify behavior.
|
||||
* Accessor method for a read-only string version of the policy at a given
|
||||
* index.
|
||||
*/
|
||||
attribute boolean reportOnlyMode;
|
||||
AString getPolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* A read-only string version of the policy for debugging.
|
||||
* Returns the number of policies attached to this CSP instance. Useful with
|
||||
* getPolicy().
|
||||
*/
|
||||
readonly attribute AString policy;
|
||||
attribute long policyCount;
|
||||
|
||||
/**
|
||||
* Remove a policy associated with this CSP context.
|
||||
* @throws NS_ERROR_FAILURE if the index is out of bounds or invalid.
|
||||
*/
|
||||
void removePolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Parse and install a CSP policy.
|
||||
* @param aPolicy
|
||||
* String representation of the policy (e.g., header value)
|
||||
* @param selfURI
|
||||
* the URI of the protected document/principal
|
||||
* @param reportOnly
|
||||
* Should this policy affect content, script and style processing or
|
||||
* just send reports if it is violated?
|
||||
* @param specCompliant
|
||||
* Whether or not the policy conforms to the W3C specification.
|
||||
* If this is false, that indicates this policy is from the older
|
||||
* implementation with different semantics and directive names.
|
||||
*/
|
||||
void appendPolicy(in AString policyString, in nsIURI selfURI,
|
||||
in boolean reportOnly, in boolean specCompliant);
|
||||
|
||||
/**
|
||||
* Whether this policy allows in-page script.
|
||||
* @param shouldReportViolation
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of inline script should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* any policy (report-only or otherwise) is violated,
|
||||
* shouldReportViolations is true as well.
|
||||
* @return
|
||||
* Whether or not the effects of the inline script should be allowed
|
||||
* (block the compilation if false).
|
||||
*/
|
||||
boolean getAllowsInlineScript(out boolean shouldReportViolation);
|
||||
boolean getAllowsInlineScript(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* whether this policy allows eval and eval-like functions
|
||||
* such as setTimeout("code string", time).
|
||||
* @param shouldReportViolation
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of eval should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* This function returns "true" when violating report-only policies, but
|
||||
* when any policy (report-only or otherwise) is violated,
|
||||
* shouldReportViolations is true as well.
|
||||
* @return
|
||||
* Whether or not the effects of the eval call should be allowed
|
||||
* (block the call if false).
|
||||
*/
|
||||
boolean getAllowsEval(out boolean shouldReportViolation);
|
||||
boolean getAllowsEval(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* Whether this policy allows in-page styles.
|
||||
* This includes <style> tags with text content and style="" attributes in
|
||||
* HTML elements.
|
||||
* @param shouldReportViolation
|
||||
* Whether or not the use of eval should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of inline style should be reported.
|
||||
* If there are report-only policies, this function may return true
|
||||
* (don't block), but one or more policy may still want to send
|
||||
* violation reports so shouldReportViolations will be true even if the
|
||||
* inline style should be permitted.
|
||||
* @return
|
||||
* Whether or not the effects of the eval call should be allowed
|
||||
* (block the call if false).
|
||||
* Whether or not the effects of the inline style should be allowed
|
||||
* (block the rules if false).
|
||||
*/
|
||||
boolean getAllowsInlineStyle(out boolean shouldReportViolation);
|
||||
boolean getAllowsInlineStyle(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
* For each violated policy (of type violationType), log policy violation on
|
||||
* the Error Console and send a report to report-uris present in the violated
|
||||
* policies.
|
||||
*
|
||||
* @param violationType
|
||||
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
|
||||
@ -99,47 +127,24 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
const unsigned short VIOLATION_TYPE_EVAL = 2;
|
||||
const unsigned short VIOLATION_TYPE_INLINE_STYLE = 3;
|
||||
|
||||
/**
|
||||
* Manually 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.
|
||||
* @param scriptSample
|
||||
* a sample of the violating inline script
|
||||
* @param lineNum
|
||||
* source line number of the violation (if available)
|
||||
* @return
|
||||
* nothing.
|
||||
*/
|
||||
void sendReports(in AString blockedURI,
|
||||
in AString violatedDirective,
|
||||
in AString scriptSample,
|
||||
in int32_t lineNum);
|
||||
|
||||
/**
|
||||
* 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, in boolean specCompliant);
|
||||
|
||||
/**
|
||||
* Verifies ancestry as permitted by the policy.
|
||||
*
|
||||
* Calls to this may trigger violation reports when queried, so
|
||||
* this value should not be cached.
|
||||
* NOTE: Calls to this may trigger violation reports when queried, so this
|
||||
* value should not be cached.
|
||||
*
|
||||
* @param docShell
|
||||
* containing the protected resource
|
||||
* @return
|
||||
* true if the frame's ancestors are all permitted by policy
|
||||
* true if the frame's ancestors are all allowed by policy (except for
|
||||
* report-only policies, which will send reports and then return true
|
||||
* here when violated).
|
||||
*/
|
||||
boolean permitsAncestry(in nsIDocShell docShell);
|
||||
|
||||
@ -151,11 +156,11 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
* Calls to this may trigger violation reports when queried, so
|
||||
* this value should not be cached.
|
||||
*/
|
||||
short shouldLoad(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeTypeGuess,
|
||||
short shouldLoad(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeTypeGuess,
|
||||
in nsISupports aExtra);
|
||||
|
||||
/**
|
||||
@ -163,10 +168,10 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
* 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,
|
||||
short shouldProcess(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeType,
|
||||
in nsISupports aExtra);
|
||||
|
||||
|
@ -108,7 +108,7 @@ this.CSPdebug = function CSPdebug(aMsg) {
|
||||
}
|
||||
|
||||
// Callback to resume a request once the policy-uri has been fetched
|
||||
function CSPPolicyURIListener(policyURI, docRequest, csp) {
|
||||
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
|
||||
@ -116,6 +116,7 @@ function CSPPolicyURIListener(policyURI, docRequest, csp) {
|
||||
this._wrapper = null; // nsIScriptableInputStream
|
||||
this._docURI = docRequest.QueryInterface(Ci.nsIChannel)
|
||||
.URI; // parent document URI (to be used as 'self')
|
||||
this._reportOnly = reportOnly;
|
||||
}
|
||||
|
||||
CSPPolicyURIListener.prototype = {
|
||||
@ -147,8 +148,8 @@ CSPPolicyURIListener.prototype = {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
// send the policy we received back to the parent document's CSP
|
||||
// for parsing
|
||||
this._csp.refinePolicy(this._policy, this._docURI,
|
||||
this._csp._specCompliant);
|
||||
this._csp.appendPolicy(this._policy, this._docURI,
|
||||
this._reportOnly, this._csp._specCompliant);
|
||||
}
|
||||
else {
|
||||
// problem fetching policy so fail closed
|
||||
@ -177,6 +178,7 @@ this.CSPRep = function CSPRep(aSpecCompliant) {
|
||||
|
||||
this._allowEval = false;
|
||||
this._allowInlineScripts = false;
|
||||
this._reportOnlyMode = false;
|
||||
|
||||
// don't auto-populate _directives, so it is easier to find bugs
|
||||
this._directives = {};
|
||||
@ -244,12 +246,14 @@ CSPRep.ALLOW_DIRECTIVE = "allow";
|
||||
* @returns
|
||||
* an instance of CSPRep
|
||||
*/
|
||||
CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
CSPRep.fromString = function(aStr, self, docRequest, csp, reportOnly) {
|
||||
if (typeof reportOnly === 'undefined') reportOnly = false;
|
||||
var SD = CSPRep.SRC_DIRECTIVES_OLD;
|
||||
var UD = CSPRep.URI_DIRECTIVES;
|
||||
var aCSPR = new CSPRep();
|
||||
aCSPR._originalText = aStr;
|
||||
aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
|
||||
aCSPR._reportOnlyMode = reportOnly;
|
||||
|
||||
var selfUri = null;
|
||||
if (self instanceof Ci.nsIURI) {
|
||||
@ -451,7 +455,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
// policy-uri can't be abused for CSRF
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
|
||||
chan.loadGroup = docRequest.loadGroup;
|
||||
chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
|
||||
chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp, reportOnly), null);
|
||||
}
|
||||
catch (e) {
|
||||
// resume the document request and apply most restrictive policy
|
||||
@ -499,12 +503,15 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
*/
|
||||
// When we deprecate our original CSP implementation, we rename this to
|
||||
// CSPRep.fromString and remove the existing CSPRep.fromString above.
|
||||
CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp) {
|
||||
CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp, reportOnly) {
|
||||
if (typeof reportOnly === 'undefined') reportOnly = false;
|
||||
|
||||
var SD = CSPRep.SRC_DIRECTIVES_NEW;
|
||||
var UD = CSPRep.URI_DIRECTIVES;
|
||||
var aCSPR = new CSPRep(true);
|
||||
aCSPR._originalText = aStr;
|
||||
aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
|
||||
aCSPR._reportOnlyMode = reportOnly;
|
||||
|
||||
var selfUri = null;
|
||||
if (self instanceof Ci.nsIURI) {
|
||||
|
@ -44,21 +44,15 @@ Cu.import("resource://gre/modules/CSPUtils.jsm");
|
||||
function ContentSecurityPolicy() {
|
||||
CSPdebug("CSP CREATED");
|
||||
this._isInitialized = false;
|
||||
this._reportOnlyMode = false;
|
||||
|
||||
this._policy = CSPRep.fromString("default-src *");
|
||||
|
||||
// default options "wide open" since this policy will be intersected soon
|
||||
this._policy._allowInlineScripts = true;
|
||||
this._policy._allowInlineStyles = true;
|
||||
this._policy._allowEval = true;
|
||||
this._policies = [];
|
||||
|
||||
this._request = "";
|
||||
this._requestOrigin = "";
|
||||
this._requestPrincipal = "";
|
||||
this._referrer = "";
|
||||
this._docRequest = null;
|
||||
CSPdebug("CSP POLICY INITED TO 'default-src *'");
|
||||
CSPdebug("CSP object initialized, no policies to enforce yet");
|
||||
|
||||
this._cache = { };
|
||||
}
|
||||
@ -135,45 +129,68 @@ ContentSecurityPolicy.prototype = {
|
||||
this._isInitialized = foo;
|
||||
},
|
||||
|
||||
get policy () {
|
||||
return this._policy.toString();
|
||||
},
|
||||
|
||||
getAllowsInlineScript: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsInlineScripts;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsInlineScripts;
|
||||
},
|
||||
|
||||
getAllowsEval: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsEvalInScripts;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsEvalInScripts;
|
||||
},
|
||||
|
||||
getAllowsInlineStyle: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsInlineStyles;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsInlineStyles;
|
||||
_getPolicyInternal: function(index) {
|
||||
if (index < 0 || index >= this._policies.length) {
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
return this._policies[index];
|
||||
},
|
||||
|
||||
_buildViolatedDirectiveString:
|
||||
function(aDirectiveName) {
|
||||
function(aDirectiveName, aPolicy) {
|
||||
var SD = CSPRep.SRC_DIRECTIVES_NEW;
|
||||
var cspContext = (SD[aDirectiveName] in this._policy._directives) ? SD[aDirectiveName] : SD.DEFAULT_SRC;
|
||||
var directive = this._policy._directives[cspContext];
|
||||
var cspContext = (SD[aDirectiveName] in aPolicy._directives) ? SD[aDirectiveName] : SD.DEFAULT_SRC;
|
||||
var directive = aPolicy._directives[cspContext];
|
||||
return cspContext + ' ' + directive.toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
* 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;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -185,51 +202,44 @@ ContentSecurityPolicy.prototype = {
|
||||
* source line number of the violation (if available)
|
||||
*/
|
||||
logViolationDetails:
|
||||
function(aViolationType, aSourceFile, aScriptSample, aLineNum) {
|
||||
// allowsInlineScript and allowsEval both return true when report-only mode
|
||||
// is enabled, resulting in a call to this function. Therefore we need to
|
||||
// check that the policy was in fact violated before logging any violations
|
||||
switch (aViolationType) {
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE:
|
||||
if (!this._policy.allowsInlineStyles) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC');
|
||||
this._asyncReportViolation('self', null, violatedDirective, INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
}
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
|
||||
if (!this._policy.allowsInlineScripts) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC');
|
||||
this._asyncReportViolation('self', null, violatedDirective, INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT,
|
||||
function(aViolationType, aSourceFile, aScriptSample, aLineNum, violatedPolicyIndex) {
|
||||
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_EVAL:
|
||||
if (!this._policy.allowsEvalInScripts) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC');
|
||||
this._asyncReportViolation('self', null, violatedDirective, EVAL_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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
set reportOnlyMode(val) {
|
||||
this._reportOnlyMode = val;
|
||||
},
|
||||
|
||||
get reportOnlyMode () {
|
||||
return this._reportOnlyMode;
|
||||
},
|
||||
|
||||
/*
|
||||
// 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.
|
||||
*/
|
||||
@ -265,26 +275,29 @@ ContentSecurityPolicy.prototype = {
|
||||
/* ........ 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.
|
||||
* Adds a new policy to our list of policies for this CSP context.
|
||||
* @returns the count of policies.
|
||||
*/
|
||||
refinePolicy:
|
||||
function csp_refinePolicy(aPolicy, selfURI, aSpecCompliant) {
|
||||
CSPdebug("REFINE POLICY: " + aPolicy);
|
||||
CSPdebug(" SELF: " + selfURI.asciiSpec);
|
||||
appendPolicy:
|
||||
function csp_appendPolicy(aPolicy, selfURI, aReportOnly, aSpecCompliant) {
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("APPENDING POLICY: " + aPolicy);
|
||||
CSPdebug(" SELF: " + selfURI.asciiSpec);
|
||||
CSPdebug("CSP 1.0 COMPLIANT : " + aSpecCompliant);
|
||||
#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 merging is done
|
||||
this._isInitialized = false;
|
||||
// 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
|
||||
@ -294,29 +307,37 @@ ContentSecurityPolicy.prototype = {
|
||||
// If we want to be CSP 1.0 spec compliant, use the new parser.
|
||||
// The old one will be deprecated in the future and will be
|
||||
// removed at that time.
|
||||
var newpolicy;
|
||||
if (aSpecCompliant) {
|
||||
newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this);
|
||||
this,
|
||||
aReportOnly);
|
||||
} else {
|
||||
newpolicy = CSPRep.fromString(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this);
|
||||
this,
|
||||
aReportOnly);
|
||||
}
|
||||
|
||||
// (2) Intersect the currently installed CSPRep object with the new one
|
||||
var intersect = this._policy.intersectWith(newpolicy);
|
||||
newpolicy._specCompliant = !!aSpecCompliant;
|
||||
newpolicy._isInitialized = true;
|
||||
this._policies.push(newpolicy);
|
||||
this._cache = {}; // reset cache since effective policy changes
|
||||
},
|
||||
|
||||
// (3) Save the result
|
||||
this._policy = intersect;
|
||||
|
||||
this._policy._specCompliant = !!aSpecCompliant;
|
||||
|
||||
this._isInitialized = true;
|
||||
this._cache = {};
|
||||
/**
|
||||
* 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 = {}; // reset cache since effective policy changes
|
||||
},
|
||||
|
||||
/**
|
||||
@ -324,17 +345,29 @@ ContentSecurityPolicy.prototype = {
|
||||
*/
|
||||
sendReports:
|
||||
function(blockedUri, originalUri, violatedDirective,
|
||||
aSourceFile, aScriptSample, aLineNum) {
|
||||
var uriString = this._policy.getReportURIs();
|
||||
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
|
||||
let clone = blockedUri.clone();
|
||||
clone.path = '';
|
||||
blocked = clone.asciiSpec;
|
||||
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;
|
||||
@ -400,7 +433,7 @@ ContentSecurityPolicy.prototype = {
|
||||
|
||||
// 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(this._policy);
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink(policy);
|
||||
if (this._docRequest) {
|
||||
chan.loadGroup = this._docRequest.loadGroup;
|
||||
}
|
||||
@ -435,8 +468,8 @@ ContentSecurityPolicy.prototype = {
|
||||
} catch(e) {
|
||||
// it's possible that the URI was invalid, just log a
|
||||
// warning and skip over that.
|
||||
this._policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
|
||||
this._policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
|
||||
policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
|
||||
policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,8 +479,9 @@ ContentSecurityPolicy.prototype = {
|
||||
* Logs a meaningful CSP warning to the developer console.
|
||||
*/
|
||||
logToConsole:
|
||||
function(blockedUri, originalUri, violatedDirective,
|
||||
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");
|
||||
@ -465,24 +499,37 @@ ContentSecurityPolicy.prototype = {
|
||||
} else {
|
||||
violationMessage = CSPLocalizer.getFormatStr("CSPViolation", [violatedDirective]);
|
||||
}
|
||||
this._policy.log(WARN_FLAG, violationMessage,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
policy.log(WARN_FLAG, violationMessage,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposed Method to analyze docShell for approved frame ancestry.
|
||||
* Also sends violation reports if necessary.
|
||||
* 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.
|
||||
* 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++) {
|
||||
if (!this._permitsAncestryInternal(docShell, this._policies[i], i)) {
|
||||
permitted = false;
|
||||
}
|
||||
}
|
||||
return permitted;
|
||||
},
|
||||
|
||||
_permitsAncestryInternal:
|
||||
function(docShell, policy, policyIndex) {
|
||||
if (!docShell) { return false; }
|
||||
CSPdebug(" in permitsAncestry(), docShell = " + docShell);
|
||||
|
||||
// walk up this docShell tree until we hit chrome
|
||||
var dst = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
@ -504,7 +551,9 @@ ContentSecurityPolicy.prototype = {
|
||||
ancestor.userPass = '';
|
||||
} catch (ex) {}
|
||||
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug(" found frame ancestor " + ancestor.asciiSpec);
|
||||
#endif
|
||||
ancestors.push(ancestor);
|
||||
}
|
||||
}
|
||||
@ -515,17 +564,16 @@ ContentSecurityPolicy.prototype = {
|
||||
let cspContext = CSPRep.SRC_DIRECTIVES_NEW.FRAME_ANCESTORS;
|
||||
for (let i in ancestors) {
|
||||
let ancestor = ancestors[i];
|
||||
if (!this._policy.permits(ancestor, cspContext)) {
|
||||
if (!policy.permits(ancestor, cspContext)) {
|
||||
// report the frame-ancestor violation
|
||||
let directive = this._policy._directives[cspContext];
|
||||
let violatedPolicy = (directive._isImplicit
|
||||
? 'default-src' : 'frame-ancestors ')
|
||||
+ directive.toString();
|
||||
let directive = policy._directives[cspContext];
|
||||
let violatedPolicy = 'frame-ancestors ' + directive.toString();
|
||||
|
||||
this._asyncReportViolation(ancestors[i], null, violatedPolicy);
|
||||
this._asyncReportViolation(ancestors[i], null, violatedPolicy,
|
||||
policyIndex);
|
||||
|
||||
// need to lie if we are testing in report-only mode
|
||||
return this._reportOnlyMode;
|
||||
return policy._reportOnlyMode;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -581,63 +629,82 @@ ContentSecurityPolicy.prototype = {
|
||||
|
||||
let cp = Ci.nsIContentPolicy;
|
||||
|
||||
// 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];
|
||||
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("policy is " + (this._policy._specCompliant ?
|
||||
"1.0 compliant" : "pre-1.0"));
|
||||
CSPdebug("policy is " + (policy._specCompliant ?
|
||||
"1.0 compliant" : "pre-1.0"));
|
||||
CSPdebug("policy is " + (policy._reportOnlyMode ?
|
||||
"report-only" : "blocking"));
|
||||
#endif
|
||||
|
||||
if (aContentType == cp.TYPE_XMLHTTPREQUEST && this._policy._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT];
|
||||
} else if (aContentType == cp.TYPE_WEBSOCKET && this._policy._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT];
|
||||
} else {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
|
||||
}
|
||||
|
||||
CSPdebug("shouldLoad cspContext = " + cspContext);
|
||||
|
||||
// if the mapping is null, there's no policy, let it through.
|
||||
if (!cspContext) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
}
|
||||
|
||||
// otherwise, honor the translation
|
||||
// var source = aContentLocation.scheme + "://" + aContentLocation.hostPort;
|
||||
var res = this._policy.permits(aContentLocation, cspContext)
|
||||
? 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);
|
||||
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 (this._policy._directives.hasOwnProperty(cspContext)) {
|
||||
directive = this._policy._directives[cspContext];
|
||||
violatedPolicy = cspContext + ' ' + directive.toString();
|
||||
} else if (this._policy._directives.hasOwnProperty("default-src")) {
|
||||
directive = this._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);
|
||||
} catch(e) {
|
||||
CSPdebug('---------------- ERROR: ' + e);
|
||||
if (aContentType == cp.TYPE_XMLHTTPREQUEST && this._policies[policyIndex]._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT];
|
||||
} else if (aContentType == cp.TYPE_WEBSOCKET && this._policies[policyIndex]._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT];
|
||||
} else {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
|
||||
}
|
||||
}
|
||||
|
||||
let ret = (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("shouldLoad cspContext = " + cspContext);
|
||||
#endif
|
||||
|
||||
// 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 = policy.permits(aContentLocation, cspContext)
|
||||
? cp.ACCEPT : cp.REJECT_SERVER;
|
||||
// record whether the thing should be blocked or just reported.
|
||||
policyAllowsLoadArray.push(res == cp.ACCEPT || policy._reportOnlyMode);
|
||||
|
||||
// frame-ancestors is taken care of early on (as this document is loaded)
|
||||
|
||||
// If the result is *NOT* ACCEPT, then send report
|
||||
if (res != Ci.nsIContentPolicy.ACCEPT) {
|
||||
CSPdebug("blocking request for " + aContentLocation.asciiSpec);
|
||||
try {
|
||||
let directive = "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);
|
||||
|
||||
if (key) {
|
||||
this._cache[key] = ret;
|
||||
}
|
||||
@ -669,6 +736,9 @@ ContentSecurityPolicy.prototype = {
|
||||
* 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.
|
||||
@ -680,7 +750,8 @@ ContentSecurityPolicy.prototype = {
|
||||
* source line number of the violation (if available)
|
||||
*/
|
||||
_asyncReportViolation:
|
||||
function(aBlockedContentSource, aOriginalUri, aViolatedDirective, aObserverSubject,
|
||||
function(aBlockedContentSource, aOriginalUri, aViolatedDirective,
|
||||
aViolatedPolicyIndex, aObserverSubject,
|
||||
aSourceFile, aScriptSample, aLineNum) {
|
||||
// if optional observerSubject isn't specified, default to the source of
|
||||
// the violation.
|
||||
@ -704,10 +775,11 @@ ContentSecurityPolicy.prototype = {
|
||||
CSP_VIOLATION_TOPIC,
|
||||
aViolatedDirective);
|
||||
reportSender.sendReports(aBlockedContentSource, aOriginalUri,
|
||||
aViolatedDirective,
|
||||
aViolatedDirective, aViolatedPolicyIndex,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
reportSender.logToConsole(aBlockedContentSource, aOriginalUri,
|
||||
aViolatedDirective, aSourceFile, aScriptSample,
|
||||
aViolatedDirective, aViolatedPolicyIndex,
|
||||
aSourceFile, aScriptSample,
|
||||
aLineNum, aObserverSubject);
|
||||
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
@ -60,88 +60,97 @@ CSPService::ShouldLoad(uint32_t aContentType,
|
||||
nsIPrincipal *aRequestPrincipal,
|
||||
int16_t *aDecision)
|
||||
{
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString location;
|
||||
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 (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// shortcut for about: chrome: and resource: and javascript: uris since
|
||||
// they're not subject to CSP content policy checks.
|
||||
bool schemeMatch = false;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// 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)
|
||||
if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
|
||||
aContentType == nsIContentPolicy::TYPE_REFRESH ||
|
||||
aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// find the principal of the document that initiated this request and see
|
||||
// if it has a CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Document has CSP: %s",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
// (don't pass aExtra, we use that slot for redirects)
|
||||
csp->ShouldLoad(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
nullptr,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
{
|
||||
nsAutoCString location;
|
||||
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 (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// shortcut for about: chrome: and resource: and javascript: uris since
|
||||
// they're not subject to CSP content policy checks.
|
||||
bool schemeMatch = false;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// 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)
|
||||
if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
|
||||
aContentType == nsIContentPolicy::TYPE_REFRESH ||
|
||||
aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// find the principal of the document that initiated this request and see
|
||||
// if it has a CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
int numPolicies = 0;
|
||||
nsresult rv = csp->GetPolicyCount(&numPolicies);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (int i=0; i<numPolicies; i++) {
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(i, policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Document has CSP[%d]: %s", i,
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
// (don't pass aExtra, we use that slot for redirects)
|
||||
csp->ShouldLoad(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
nullptr,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -154,52 +163,60 @@ CSPService::ShouldProcess(uint32_t aContentType,
|
||||
nsIPrincipal *aRequestPrincipal,
|
||||
int16_t *aDecision)
|
||||
{
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// default decision is to accept the item
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
// default decision is to accept the item
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// find the nsDocument that initiated this request and see if it has a
|
||||
// CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("shouldProcess - document has policy: %s",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
csp->ShouldProcess(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
aExtra,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// find the nsDocument that initiated this request and see if it has a
|
||||
// CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
int numPolicies = 0;
|
||||
nsresult rv = csp->GetPolicyCount(&numPolicies);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (int i=0; i<numPolicies; i++) {
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(i, policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("shouldProcess - document has policy[%d]: %s", i,
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
csp->ShouldProcess(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
aExtra,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIChannelEventSink implementation */
|
||||
|
@ -2458,6 +2458,30 @@ nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
|
||||
}
|
||||
}
|
||||
|
||||
static nsresult
|
||||
AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue,
|
||||
nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant)
|
||||
{
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsresult rv = NS_OK;
|
||||
nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
{
|
||||
@ -2502,15 +2526,23 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
bool specCompliantEnabled =
|
||||
Preferences::GetBool("security.csp.speccompliant");
|
||||
|
||||
// If spec compliant pref isn't set, pretend we never got these headers.
|
||||
if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) &&
|
||||
!specCompliantEnabled) {
|
||||
// If spec compliant pref isn't set, pretend we never got
|
||||
// these headers.
|
||||
if (!specCompliantEnabled) {
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Got spec compliant CSP headers but pref was not set"));
|
||||
cspHeaderValue.Truncate();
|
||||
cspROHeaderValue.Truncate();
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Got spec compliant CSP headers but pref was not set"));
|
||||
cspHeaderValue.Truncate();
|
||||
cspROHeaderValue.Truncate();
|
||||
}
|
||||
|
||||
// If the old header is present, warn that it will be deprecated.
|
||||
if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
|
||||
|
||||
// Also, if the new headers AND the old headers were present, warn
|
||||
// that the old headers will be ignored.
|
||||
if (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2571,19 +2603,13 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
|
||||
// used as a "self" identifier for the CSP.
|
||||
nsCOMPtr<nsIURI> chanURI;
|
||||
aChannel->GetURI(getter_AddRefs(chanURI));
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
aChannel->GetURI(getter_AddRefs(selfURI));
|
||||
|
||||
// Store the request context for violation reports
|
||||
csp->ScanRequestData(httpChannel);
|
||||
|
||||
// The CSP is refined in the following order:
|
||||
// 1. Default app CSP, if applicable
|
||||
// 2. App manifest CSP, if provided
|
||||
// 3. HTTP header CSP, if provided
|
||||
// Note that since each application of refinePolicy is a set intersection,
|
||||
// the order in which multiple CSP's are refined does not matter.
|
||||
|
||||
// ----- if the doc is an app and we want a default CSP, apply it.
|
||||
if (applyAppDefaultCSP) {
|
||||
nsAdoptingString appCSP;
|
||||
if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) {
|
||||
@ -2594,92 +2620,43 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
|
||||
}
|
||||
|
||||
if (appCSP)
|
||||
if (appCSP) {
|
||||
// Use the 1.0 CSP parser for apps if the pref to do so is set.
|
||||
csp->RefinePolicy(appCSP, chanURI, specCompliantEnabled);
|
||||
csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- if the doc is an app and specifies a CSP in its manifest, apply it.
|
||||
if (applyAppManifestCSP) {
|
||||
// Use the 1.0 CSP parser for apps if the pref to do so is set.
|
||||
csp->RefinePolicy(appManifestCSP, chanURI, specCompliantEnabled);
|
||||
csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled);
|
||||
}
|
||||
|
||||
// While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
|
||||
// take priority. If any spec-compliant headers are present, the x- headers
|
||||
// are ignored, and the spec compliant parser is used.
|
||||
bool cspSpecCompliant = (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty());
|
||||
|
||||
// If the old header is present, warn that it will be deprecated.
|
||||
if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
|
||||
|
||||
// Also, if the new headers AND the old headers were present, warn
|
||||
// that the old headers will be ignored.
|
||||
if (cspSpecCompliant) {
|
||||
mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
|
||||
}
|
||||
}
|
||||
// can coexist with x- headers. If both exist, they're both enforced, but
|
||||
// there's a warning posted in the web console that the x-headers are going
|
||||
// away.
|
||||
|
||||
// ----- if there's a full-strength CSP header, apply it.
|
||||
bool applyCSPFromHeader =
|
||||
(( cspSpecCompliant && !cspHeaderValue.IsEmpty()) ||
|
||||
(!cspSpecCompliant && !cspOldHeaderValue.IsEmpty()));
|
||||
|
||||
if (applyCSPFromHeader) {
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspSpecCompliant ?
|
||||
cspHeaderValue :
|
||||
cspOldHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
csp->RefinePolicy(policy, chanURI, cspSpecCompliant);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!cspOldHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- if there's a report-only CSP header, apply it
|
||||
if (( cspSpecCompliant && !cspROHeaderValue.IsEmpty()) ||
|
||||
(!cspSpecCompliant && !cspOldROHeaderValue.IsEmpty())) {
|
||||
// post a warning and skip report-only CSP when both read only and regular
|
||||
// CSP policies are present since CSP only allows one policy and it can't
|
||||
// be partially report-only.
|
||||
if (applyAppDefaultCSP || applyCSPFromHeader) {
|
||||
mCSPWebConsoleErrorQueue.Add("ReportOnlyCSPIgnored");
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Skipped report-only CSP init for document %p because another, enforced policy is set", this));
|
||||
#endif
|
||||
} else {
|
||||
// we can apply the report-only policy because there's no other CSP
|
||||
// applied.
|
||||
csp->SetReportOnlyMode(true);
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspSpecCompliant ?
|
||||
cspROHeaderValue :
|
||||
cspOldROHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
csp->RefinePolicy(policy, chanURI, cspSpecCompliant);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP (report-only) refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// ----- if there's a report-only CSP header, apply it.
|
||||
if (!cspOldROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!cspROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- Enforce frame-ancestor policy on any applied policies
|
||||
|
@ -67,7 +67,7 @@ listener.prototype = {
|
||||
// nsIContentSecurityPolicy instance. The problem is, this cspr_str is a
|
||||
// string and not a policy due to the way it's exposed from
|
||||
// nsIContentSecurityPolicy, so we have to re-parse it.
|
||||
let cspr_str = this._csp.policy;
|
||||
let cspr_str = this._csp.getPolicy(0);
|
||||
let cspr = CSPRep.fromString(cspr_str, mkuri(DOCUMENT_URI));
|
||||
|
||||
// and in reparsing it, we lose the 'self' relationships, so need to also
|
||||
|
@ -79,10 +79,8 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
|
||||
csp.scanRequestData(selfchan);
|
||||
|
||||
// Load up the policy
|
||||
csp.refinePolicy(policy, selfuri, false);
|
||||
|
||||
// set as report-only if that's the case
|
||||
if (useReportOnlyPolicy) csp.reportOnlyMode = true;
|
||||
csp.appendPolicy(policy, selfuri, useReportOnlyPolicy, false);
|
||||
|
||||
// prime the report server
|
||||
var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
|
||||
@ -100,7 +98,7 @@ function run_test() {
|
||||
// test that inline script violations cause a report.
|
||||
makeTest(0, {"blocked-uri": "self"}, false,
|
||||
function(csp) {
|
||||
let inlineOK = true, oReportViolation = {};
|
||||
let inlineOK = true, oReportViolation = {'value': false};
|
||||
inlineOK = csp.getAllowsInlineScript(oReportViolation);
|
||||
|
||||
// this is not a report only policy, so it better block inline scripts
|
||||
@ -108,17 +106,19 @@ function run_test() {
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
0);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
0);
|
||||
}
|
||||
});
|
||||
|
||||
// test that eval violations cause a report.
|
||||
makeTest(1, {"blocked-uri": "self"}, false,
|
||||
function(csp) {
|
||||
let evalOK = true, oReportViolation = {};
|
||||
let evalOK = true, oReportViolation = {'value': false};
|
||||
evalOK = csp.getAllowsEval(oReportViolation);
|
||||
|
||||
// this is not a report only policy, so it better block eval
|
||||
@ -126,11 +126,13 @@ function run_test() {
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
1);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
1);
|
||||
}
|
||||
});
|
||||
|
||||
makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false,
|
||||
@ -144,25 +146,28 @@ function run_test() {
|
||||
// test that inline script violations cause a report in report-only policy
|
||||
makeTest(3, {"blocked-uri": "self"}, true,
|
||||
function(csp) {
|
||||
let inlineOK = true, oReportViolation = {};
|
||||
let inlineOK = true, oReportViolation = {'value': false};
|
||||
inlineOK = csp.getAllowsInlineScript(oReportViolation);
|
||||
|
||||
// this is a report only policy, so it better allow inline scripts
|
||||
do_check_true(inlineOK);
|
||||
// ... but still cause reports to go out
|
||||
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
3);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
3);
|
||||
}
|
||||
});
|
||||
|
||||
// test that eval violations cause a report in report-only policy
|
||||
makeTest(4, {"blocked-uri": "self"}, true,
|
||||
function(csp) {
|
||||
let evalOK = true, oReportViolation = {};
|
||||
let evalOK = true, oReportViolation = {'value': false};
|
||||
evalOK = csp.getAllowsEval(oReportViolation);
|
||||
|
||||
// this is a report only policy, so it better allow eval
|
||||
@ -170,10 +175,12 @@ function run_test() {
|
||||
// ... but still cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
4);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -680,7 +680,7 @@ test(function test_FrameAncestor_ignores_userpass_bug779918() {
|
||||
function testPermits(aChildUri, aParentUri, aContentType) {
|
||||
let cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
cspObj.refinePolicy(testPolicy, aChildUri, false);
|
||||
cspObj.appendPolicy(testPolicy, aChildUri, false, false);
|
||||
let docshellparent = Cc["@mozilla.org/docshell;1"]
|
||||
.createInstance(Ci.nsIDocShell);
|
||||
let docshellchild = Cc["@mozilla.org/docshell;1"]
|
||||
@ -909,56 +909,6 @@ test(
|
||||
do_check_false(p_none.permits("http://bar.com"));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_bug783497_refinePolicyIssues() {
|
||||
|
||||
const firstPolicy = "allow 'self'; img-src 'self'; script-src 'self'; options 'bogus-option'";
|
||||
const secondPolicy = "default-src 'none'; script-src 'self'";
|
||||
var cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
var selfURI = URI("http://self.com/");
|
||||
|
||||
function testPermits(aUri, aContentType) {
|
||||
return cspObj.shouldLoad(aContentType, aUri, null, null, null, null)
|
||||
== Ci.nsIContentPolicy.ACCEPT;
|
||||
};
|
||||
|
||||
// everything is allowed by the default policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the first policy
|
||||
cspObj.refinePolicy(firstPolicy, selfURI, false);
|
||||
|
||||
// script-src and img-src are limited to self after the first policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the second policy
|
||||
cspObj.refinePolicy(secondPolicy, selfURI, false);
|
||||
|
||||
// script-src is self and img-src is none after the merge
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_bug764937_defaultSrcMissing() {
|
||||
@ -974,7 +924,7 @@ test(
|
||||
};
|
||||
|
||||
const policy = "script-src 'self'";
|
||||
cspObjSpecCompliant.refinePolicy(policy, selfURI, true);
|
||||
cspObjSpecCompliant.appendPolicy(policy, selfURI, false, true);
|
||||
|
||||
// Spec-Compliant policy default-src defaults to *.
|
||||
// This means all images are allowed, and only 'self'
|
||||
@ -992,7 +942,7 @@ test(
|
||||
URI("http://bar.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
|
||||
cspObjOld.refinePolicy(policy, selfURI, false);
|
||||
cspObjOld.appendPolicy(policy, selfURI, false, false);
|
||||
|
||||
// non-Spec-Compliant policy default-src defaults to 'none'
|
||||
// This means all images are blocked, and so are all scripts (because the
|
||||
|
@ -254,7 +254,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (reportViolation) {
|
||||
// TODO : FIX DATA in violation report.
|
||||
// TODO : need actual script sample in violation report.
|
||||
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
|
||||
|
||||
// Get the calling location.
|
||||
@ -268,9 +268,9 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
}
|
||||
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
NS_ConvertUTF8toUTF16(aFileName),
|
||||
scriptSample,
|
||||
lineNum);
|
||||
NS_ConvertUTF8toUTF16(aFileName),
|
||||
scriptSample,
|
||||
lineNum);
|
||||
}
|
||||
|
||||
if (!allowsEval) {
|
||||
|
@ -674,8 +674,10 @@ public:
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum);
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<LogViolationDetailsResponseRunnable> response =
|
||||
|
@ -464,7 +464,7 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
|
||||
|
||||
if (csp) {
|
||||
bool inlineOK = true;
|
||||
bool reportViolation = false;
|
||||
bool reportViolation;
|
||||
rv = csp->GetAllowsInlineStyle(&reportViolation, &inlineOK);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (aRv)
|
||||
@ -485,9 +485,9 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
|
||||
}
|
||||
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE,
|
||||
NS_ConvertUTF8toUTF16(asciiSpec),
|
||||
aStyleText,
|
||||
aLineNumber);
|
||||
NS_ConvertUTF8toUTF16(asciiSpec),
|
||||
aStyleText,
|
||||
aLineNumber);
|
||||
}
|
||||
|
||||
if (!inlineOK) {
|
||||
|
Loading…
Reference in New Issue
Block a user