зеркало из https://github.com/mozilla/gecko-dev.git
bug 570505 - fix csp to avoid creating strings from URIs and un-hose test_bug558431. r=dveditz
This commit is contained in:
Родитель
d71e3a7466
Коммит
68b566d19a
|
@ -87,7 +87,7 @@ function CSPWarning(aMsg, aSource, aScriptSample, aLineNum) {
|
|||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptError);
|
||||
consoleMsg.init('CSP: ' + aMsg, aSource, aScriptSample, aLineNum, 0,
|
||||
consoleMsg.init(textMessage, aSource, aScriptSample, aLineNum, 0,
|
||||
Components.interfaces.nsIScriptError.warningFlag,
|
||||
"Content Security Policy");
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
|
@ -100,7 +100,7 @@ function CSPError(aMsg) {
|
|||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptError);
|
||||
consoleMsg.init('CSP: ' + aMsg, null, null, 0, 0,
|
||||
consoleMsg.init(textMessage, null, null, 0, 0,
|
||||
Components.interfaces.nsIScriptError.errorFlag,
|
||||
"Content Security Policy");
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
|
@ -213,7 +213,7 @@ CSPRep.ALLOW_DIRECTIVE = "allow";
|
|||
* @param aStr
|
||||
* string rep of a CSP
|
||||
* @param self (optional)
|
||||
* string or CSPSource representing the "self" source
|
||||
* URI representing the "self" source
|
||||
* @param docRequest (optional)
|
||||
* request for the parent document which may need to be suspended
|
||||
* while the policy-uri is asynchronously fetched
|
||||
|
@ -231,8 +231,6 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
var selfUri = null;
|
||||
if (self instanceof Components.interfaces.nsIURI)
|
||||
selfUri = self.clone();
|
||||
else if (self)
|
||||
selfUri = gIoService.newURI(self, null, null);
|
||||
|
||||
var dirs = aStr.split(";");
|
||||
|
||||
|
@ -452,8 +450,8 @@ CSPRep.prototype = {
|
|||
var dirs = [];
|
||||
|
||||
if (this._allowEval || this._allowInlineScripts) {
|
||||
dirs.push("options " + (this._allowEval ? "eval-script" : "")
|
||||
+ (this._allowInlineScripts ? "inline-script" : ""));
|
||||
dirs.push("options" + (this._allowEval ? " eval-script" : "")
|
||||
+ (this._allowInlineScripts ? " inline-script" : ""));
|
||||
}
|
||||
for (var i in this._directives) {
|
||||
if (this._directives[i]) {
|
||||
|
@ -604,7 +602,7 @@ function CSPSourceList() {
|
|||
* @param aStr
|
||||
* string rep of a CSP Source List
|
||||
* @param self (optional)
|
||||
* string or CSPSource representing the "self" source
|
||||
* URI or CSPSource representing the "self" source
|
||||
* @param enforceSelfChecks (optional)
|
||||
* if present, and "true", will check to be sure "self" has the
|
||||
* appropriate values to inherit when they are omitted from the source.
|
||||
|
@ -618,6 +616,12 @@ CSPSourceList.fromString = function(aStr, self, enforceSelfChecks) {
|
|||
// <source-list> ::= <source>
|
||||
// | <source-list>" "<source>
|
||||
|
||||
/* If self parameter is passed, convert to CSPSource,
|
||||
unless it is already a CSPSource. */
|
||||
if(self && !(self instanceof CSPSource)) {
|
||||
self = CSPSource.create(self);
|
||||
}
|
||||
|
||||
var slObj = new CSPSourceList();
|
||||
if (aStr === "'none'")
|
||||
return slObj;
|
||||
|
@ -810,6 +814,15 @@ function CSPSource() {
|
|||
* - nsURI
|
||||
* - string
|
||||
* - CSPSource (clone)
|
||||
* @param aData
|
||||
* string, nsURI, or CSPSource
|
||||
* @param self (optional)
|
||||
* if present, string, URI, or CSPSource representing the "self" resource
|
||||
* @param enforceSelfChecks (optional)
|
||||
* if present, and "true", will check to be sure "self" has the
|
||||
* appropriate values to inherit when they are omitted from the source.
|
||||
* @returns
|
||||
* an instance of CSPSource
|
||||
*/
|
||||
CSPSource.create = function(aData, self, enforceSelfChecks) {
|
||||
if (typeof aData === 'string')
|
||||
|
@ -912,7 +925,7 @@ CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
|
|||
* @param aStr
|
||||
* string rep of a CSP Source
|
||||
* @param self (optional)
|
||||
* string or CSPSource representing the "self" source
|
||||
* string, URI, or CSPSource representing the "self" source
|
||||
* @param enforceSelfChecks (optional)
|
||||
* if present, and "true", will check to be sure "self" has the
|
||||
* appropriate values to inherit when they are omitted from aURI.
|
||||
|
|
|
@ -239,8 +239,10 @@ ContentSecurityPolicy.prototype = {
|
|||
|
||||
// If there is a policy-uri, fetch the policy, then re-call this function.
|
||||
// (1) parse and create a CSPRep object
|
||||
// Note that we pass the full URI since when it's parsed as 'self' to construct a
|
||||
// CSPSource only the scheme, host, and port are kept.
|
||||
var newpolicy = CSPRep.fromString(aPolicy,
|
||||
selfURI.scheme + "://" + selfURI.hostPort,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this);
|
||||
|
||||
|
|
|
@ -10,14 +10,20 @@ const POLICY_URI_RELATIVE = "/policy";
|
|||
const DOCUMENT_URI = "http://localhost:" + POLICY_PORT + "/document";
|
||||
const CSP_DOC_BODY = "CSP doc content";
|
||||
const SD = CSPRep.SRC_DIRECTIVES;
|
||||
const MAX_TESTS = 2;
|
||||
var TESTS_COMPLETED = 0;
|
||||
|
||||
var cspr, cspr_static;
|
||||
// this will get populated by run_tests()
|
||||
var TESTS = [];
|
||||
|
||||
// helper to make URIs
|
||||
function mkuri(foo) {
|
||||
return Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(foo, null, null);
|
||||
}
|
||||
|
||||
// helper to use .equals on stuff
|
||||
function do_check_equivalent(foo, bar, stack) {
|
||||
if (!stack)
|
||||
if (!stack)
|
||||
stack = Components.stack.caller;
|
||||
|
||||
var text = foo + ".equals(" + bar + ")";
|
||||
|
@ -30,8 +36,10 @@ function do_check_equivalent(foo, bar, stack) {
|
|||
do_throw(text, stack);
|
||||
}
|
||||
|
||||
function listener() {
|
||||
function listener(csp, cspr_static) {
|
||||
this.buffer = "";
|
||||
this._csp = csp;
|
||||
this._cspr_static = cspr_static;
|
||||
}
|
||||
|
||||
listener.prototype = {
|
||||
|
@ -49,20 +57,30 @@ listener.prototype = {
|
|||
// make sure that we have the full document content, guaranteeing that
|
||||
// the document channel has been resumed, before we do the comparisons
|
||||
if (this.buffer == CSP_DOC_BODY) {
|
||||
// "policy-uri failed to load"
|
||||
|
||||
// need to re-grab cspr since it may have changed inside the document's
|
||||
// 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 = CSPRep.fromString(cspr_str, mkuri(DOCUMENT_URI));
|
||||
|
||||
// and in reparsing it, we lose the 'self' relationships, so need to also
|
||||
// reparse the static one (or find a way to resolve 'self' in the parsed
|
||||
// policy when doing comparisons).
|
||||
let cspr_static_str = this._cspr_static.toString();
|
||||
let cspr_static_reparse = CSPRep.fromString(cspr_static_str, mkuri(DOCUMENT_URI));
|
||||
|
||||
// not null, and one policy .equals the other one
|
||||
do_check_neq(null, cspr);
|
||||
do_check_true(cspr.equals(cspr_static_reparse));
|
||||
|
||||
// other directives inherit self
|
||||
for (var i in SD) {
|
||||
do_check_equivalent(cspr._directives[SD[i]],
|
||||
cspr_static._directives[SD[i]]);
|
||||
}
|
||||
|
||||
do_test_finished();
|
||||
TESTS_COMPLETED++;
|
||||
// final teardown
|
||||
if (TESTS_COMPLETED == MAX_TESTS) {
|
||||
httpserv.stop(function(){});
|
||||
if (TESTS.length == 0) {
|
||||
httpserv.stop(do_test_finished);
|
||||
} else {
|
||||
do_test_finished();
|
||||
(TESTS.shift())();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,12 +91,11 @@ function run_test() {
|
|||
httpserv.registerPathHandler("/document", csp_doc_response);
|
||||
httpserv.registerPathHandler("/policy", csp_policy_response);
|
||||
httpserv.start(POLICY_PORT);
|
||||
TESTS = [ test_CSPRep_fromPolicyURI, test_CSPRep_fromRelativePolicyURI ];
|
||||
|
||||
var tests = [ test_CSPRep_fromPolicyURI, test_CSPRep_fromRelativePolicyURI];
|
||||
for (var i = 0 ; i < tests.length ; i++) {
|
||||
tests[i]();
|
||||
do_test_pending();
|
||||
}
|
||||
// when this triggers the "onStopRequest" callback, it'll
|
||||
// go to the next test.
|
||||
(TESTS.shift())();
|
||||
}
|
||||
|
||||
function makeChan(url) {
|
||||
|
@ -101,27 +118,41 @@ function csp_policy_response(metadata, response) {
|
|||
|
||||
///////////////////// TEST POLICY_URI //////////////////////
|
||||
function test_CSPRep_fromPolicyURI() {
|
||||
var csp = Components.classes["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance[Components.interfaces.nsIContentSecurityPolicy];
|
||||
do_test_pending();
|
||||
let csp = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
// once the policy-uri is returned we will compare our static CSPRep with one
|
||||
// we generated from the content we got back from the network to make sure
|
||||
// they are equivalent
|
||||
cspr_static = CSPRep.fromString(POLICY_FROM_URI, DOCUMENT_URI);
|
||||
let cspr_static = CSPRep.fromString(POLICY_FROM_URI, mkuri(DOCUMENT_URI));
|
||||
|
||||
// simulates the request for the parent document
|
||||
var docChan = makeChan(DOCUMENT_URI);
|
||||
docChan.asyncOpen(new listener(), null);
|
||||
cspr = CSPRep.fromString("policy-uri " + POLICY_URI, DOCUMENT_URI, docChan, csp);
|
||||
docChan.asyncOpen(new listener(csp, cspr_static), null);
|
||||
|
||||
// the resulting policy here can be discarded, since it's going to be
|
||||
// "allow *"; when the policy-uri fetching call-back happens, the *real*
|
||||
// policy will be in csp.policy
|
||||
CSPRep.fromString("policy-uri " + POLICY_URI,
|
||||
mkuri(DOCUMENT_URI), docChan, csp);
|
||||
}
|
||||
|
||||
function test_CSPRep_fromRelativePolicyURI() {
|
||||
var csp = Components.classes["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance[Components.interfaces.nsIContentSecurityPolicy];
|
||||
do_test_pending();
|
||||
let csp = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
// once the policy-uri is returned we will compare our static CSPRep with one
|
||||
// we generated from the content we got back from the network to make sure
|
||||
// they are equivalent
|
||||
cspr_static = CSPRep.fromString(POLICY_FROM_URI, DOCUMENT_URI);
|
||||
let cspr_static = CSPRep.fromString(POLICY_FROM_URI, mkuri(DOCUMENT_URI));
|
||||
|
||||
// simulates the request for the parent document
|
||||
var docChan = makeChan(DOCUMENT_URI);
|
||||
docChan.asyncOpen(new listener(), null);
|
||||
cspr = CSPRep.fromString("policy-uri " + POLICY_URI_RELATIVE, DOCUMENT_URI, docChan, csp);
|
||||
docChan.asyncOpen(new listener(csp, cspr_static), null);
|
||||
|
||||
// the resulting policy here can be discarded, since it's going to be
|
||||
// "allow *"; when the policy-uri fetching call-back happens, the *real*
|
||||
// policy will be in csp.policy
|
||||
CSPRep.fromString("policy-uri " + POLICY_URI_RELATIVE,
|
||||
mkuri(DOCUMENT_URI), docChan, csp);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,14 @@ const POLICY_PORT = 9000;
|
|||
const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy";
|
||||
const POLICY_URI_RELATIVE = "/policy";
|
||||
|
||||
//converts string to nsIURI
|
||||
function URI(uriString) {
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
return ioService.newURI(uriString, null, null);
|
||||
}
|
||||
|
||||
|
||||
// helper to assert that an array has the given value somewhere.
|
||||
function do_check_in_array(arr, val, stack) {
|
||||
if (!stack)
|
||||
|
@ -272,7 +280,7 @@ test(
|
|||
function test_CSPSourceList_fromString_twohost() {
|
||||
var str = "foo.bar:21 https://ras.bar";
|
||||
var parsed = "http://foo.bar:21 https://ras.bar:443";
|
||||
var sd = CSPSourceList.fromString(str, "http://self.com:80");
|
||||
var sd = CSPSourceList.fromString(str, URI("http://self.com:80"));
|
||||
//"two-host list should parse"
|
||||
do_check_neq(null,sd);
|
||||
//"two-host list should parse to two hosts"
|
||||
|
@ -284,9 +292,9 @@ test(
|
|||
test(
|
||||
function test_CSPSourceList_permits() {
|
||||
var nullSourceList = CSPSourceList.fromString("'none'");
|
||||
var simpleSourceList = CSPSourceList.fromString("a.com", "http://self.com");
|
||||
var simpleSourceList = CSPSourceList.fromString("a.com", URI("http://self.com"));
|
||||
var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88",
|
||||
"http://self.com:88");
|
||||
URI("http://self.com:88"));
|
||||
var allSourceList = CSPSourceList.fromString("*");
|
||||
|
||||
//'none' should permit none."
|
||||
|
@ -362,7 +370,7 @@ test(
|
|||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
|
||||
// check default policy "allow *"
|
||||
cspr = CSPRep.fromString("allow *", "http://self.com:80");
|
||||
cspr = CSPRep.fromString("allow *", URI("http://self.com:80"));
|
||||
// "DEFAULT_SRC directive is missing when specified in fromString"
|
||||
do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
|
||||
|
||||
|
@ -384,7 +392,7 @@ test(
|
|||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
|
||||
// apply policy of "default-src *" (e.g. "allow *")
|
||||
cspr = CSPRep.fromString("default-src *", "http://self.com:80");
|
||||
cspr = CSPRep.fromString("default-src *", URI("http://self.com:80"));
|
||||
// "DEFAULT_SRC directive is missing when specified in fromString"
|
||||
do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
|
||||
|
||||
|
@ -399,8 +407,8 @@ test(
|
|||
|
||||
// check that |allow *| and |default-src *| are parsed equivalently and
|
||||
// result in the same set of explicit policy directives
|
||||
cspr = CSPRep.fromString("default-src *", "http://self.com:80");
|
||||
cspr_allow = CSPRep.fromString("allow *", "http://self.com:80");
|
||||
cspr = CSPRep.fromString("default-src *", URI("http://self.com:80"));
|
||||
cspr_allow = CSPRep.fromString("allow *", URI("http://self.com:80"));
|
||||
|
||||
for (var d in SD) {
|
||||
do_check_equivalent(cspr._directives[SD[d]],
|
||||
|
@ -418,7 +426,7 @@ test(
|
|||
|
||||
// check one-directive policies
|
||||
cspr = CSPRep.fromString("allow bar.com; script-src https://foo.com",
|
||||
"http://self.com");
|
||||
URI("http://self.com"));
|
||||
|
||||
for(var x in DEFAULTS) {
|
||||
//DEFAULTS[x] + " does not use default rule."
|
||||
|
@ -446,7 +454,7 @@ test(
|
|||
var polstr = "allow allow.com; "
|
||||
+ "script-src https://foo.com; "
|
||||
+ "img-src bar.com:*";
|
||||
cspr = CSPRep.fromString(polstr, "http://self.com");
|
||||
cspr = CSPRep.fromString(polstr, URI("http://self.com"));
|
||||
|
||||
for(var x in DEFAULTS) {
|
||||
do_check_true(cspr.permits("http://allow.com", DEFAULTS[x]));
|
||||
|
@ -478,7 +486,7 @@ test(function test_CSPRep_fromString_withself() {
|
|||
|
||||
// check one-directive policies
|
||||
cspr = CSPRep.fromString("allow 'self'; script-src 'self' https://*:*",
|
||||
self);
|
||||
URI(self));
|
||||
//"img-src does not enforce default rule, 'self'.
|
||||
do_check_false(cspr.permits("https://foo.com:400", SD.IMG_SRC));
|
||||
//"img-src does not allow self
|
||||
|
@ -498,7 +506,7 @@ test(function test_FrameAncestor_defaults() {
|
|||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
var self = "http://self.com:34";
|
||||
|
||||
cspr = CSPRep.fromString("allow 'none'", self);
|
||||
cspr = CSPRep.fromString("allow 'none'", URI(self));
|
||||
|
||||
//"frame-ancestors should default to * not 'allow' value"
|
||||
do_check_true(cspr.permits("https://foo.com:400", SD.FRAME_ANCESTORS));
|
||||
|
@ -507,7 +515,7 @@ test(function test_FrameAncestor_defaults() {
|
|||
do_check_true(cspr.permits("http://self.com", SD.FRAME_ANCESTORS));
|
||||
do_check_true(cspr.permits("http://subd.self.com:34", SD.FRAME_ANCESTORS));
|
||||
|
||||
cspr = CSPRep.fromString("allow 'none'; frame-ancestors 'self'", self);
|
||||
cspr = CSPRep.fromString("allow 'none'; frame-ancestors 'self'", URI(self));
|
||||
|
||||
//"frame-ancestors should only allow self"
|
||||
do_check_true(cspr.permits("http://self.com:34", SD.FRAME_ANCESTORS));
|
||||
|
@ -531,27 +539,27 @@ test(function test_CSP_ReportURI_parsing() {
|
|||
var uri_valid_relative2_expanded = self + "/" + uri_valid_relative2;
|
||||
var uri_invalid_relative = "javascript:alert(1)";
|
||||
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_absolute, self);
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_absolute, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, uri_valid_absolute);
|
||||
do_check_eq(parsedURIs.length, 1);
|
||||
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_host_absolute, self);
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_host_absolute, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, "");
|
||||
do_check_eq(parsedURIs.length, 1); // the empty string is in there.
|
||||
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_relative, self);
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_relative, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, "");
|
||||
do_check_eq(parsedURIs.length, 1);
|
||||
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative, self);
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, uri_valid_relative_expanded);
|
||||
do_check_eq(parsedURIs.length, 1);
|
||||
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative2, self);
|
||||
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative2, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
dump(parsedURIs.length);
|
||||
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
|
||||
|
@ -560,7 +568,7 @@ test(function test_CSP_ReportURI_parsing() {
|
|||
// combination!
|
||||
cspr = CSPRep.fromString("allow *; report-uri " +
|
||||
uri_valid_relative2 + " " +
|
||||
uri_valid_absolute, self);
|
||||
uri_valid_absolute, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
|
||||
do_check_in_array(parsedURIs, uri_valid_absolute);
|
||||
|
@ -569,7 +577,7 @@ test(function test_CSP_ReportURI_parsing() {
|
|||
cspr = CSPRep.fromString("allow *; report-uri " +
|
||||
uri_valid_relative2 + " " +
|
||||
uri_invalid_host_absolute + " " +
|
||||
uri_valid_absolute, self);
|
||||
uri_valid_absolute, URI(self));
|
||||
parsedURIs = cspr.getReportURIs().split(/\s+/);
|
||||
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
|
||||
do_check_in_array(parsedURIs, uri_valid_absolute);
|
||||
|
@ -631,11 +639,11 @@ test(function test_CSPRep_fromPolicyURI_failswhenmixed() {
|
|||
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);
|
||||
cspr = CSPRep.fromString("allow *; " + my_uri_policy, URI(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);
|
||||
cspr = CSPRep.fromString("img-src 'self'; " + my_uri_policy, URI(self));
|
||||
|
||||
//"Parsing should fail when 'policy-uri' is mixed with other directives"
|
||||
do_check_equivalent(cspr, closed_policy);
|
||||
|
|
Загрузка…
Ссылка в новой задаче