bug 570505 - fix csp to avoid creating strings from URIs and un-hose test_bug558431. r=dveditz

This commit is contained in:
Tanvi Vyas 2012-04-17 06:16:05 -07:00
Родитель d71e3a7466
Коммит 68b566d19a
4 изменённых файлов: 116 добавлений и 62 удалений

Просмотреть файл

@ -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);