bug 836922 - (CSP) remove intersectWith once multiple policies are supported. r=grobinson

This commit is contained in:
Sid Stamm 2013-09-12 09:25:33 -07:00
parent 533fe16a0b
commit 11eccea08a
2 changed files with 4 additions and 336 deletions

View File

@ -465,8 +465,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp, reportOnly) {
return CSPRep.fromString("default-src 'none'");
}
// return a fully-open policy to be intersected with the contents of the
// policy-uri when it returns
// return a fully-open policy to be used until the contents of the
// policy-uri come back.
return CSPRep.fromString("default-src *");
}
@ -723,8 +723,8 @@ CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp, reportOnl
return CSPRep.fromStringSpecCompliant("default-src 'none'");
}
// return a fully-open policy to be intersected with the contents of the
// policy-uri when it returns
// return a fully-open policy to be used until the contents of the
// policy-uri come back
return CSPRep.fromStringSpecCompliant("default-src *");
}
@ -843,120 +843,6 @@ CSPRep.prototype = {
return this._specCompliant;
},
/**
* Intersects with another CSPRep, deciding the subset policy
* that should be enforced, and returning a new instance.
* This assumes that either both CSPReps are specCompliant or they are both
* not.
* @param aCSPRep
* a CSPRep instance to use as "other" CSP
* @returns
* a new CSPRep instance of the intersection
*/
intersectWith:
function cspsd_intersectWith(aCSPRep) {
var newRep = new CSPRep();
let DIRS = aCSPRep._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW :
CSPRep.SRC_DIRECTIVES_OLD;
// one or more of the two CSPReps may not have any given directive. In
// these cases, we need to pick "all" or "none" based on the type of CSPRep
// (spec compliant or not).
let thisHasDefault = this._directives.hasOwnProperty(DIRS.DEFAULT_SRC),
thatHasDefault = aCSPRep._directives.hasOwnProperty(DIRS.DEFAULT_SRC);
for (var dir in DIRS) {
let dirv = DIRS[dir];
let thisHasDir = this._directives.hasOwnProperty(dirv),
thatHasDir = aCSPRep._directives.hasOwnProperty(dirv);
// if both specific src directives are absent, skip this (new policy will
// rely on default-src)
if (!thisHasDir && !thatHasDir) {
continue;
}
// frame-ancestors is a special case; it doesn't fall back to
// default-src, so only add it to newRep if one or both of the policies
// have it.
if (dirv === DIRS.FRAME_ANCESTORS) {
if (thisHasDir && thatHasDir) {
// both have frame-ancestors, intersect them.
newRep._directives[dirv] =
aCSPRep._directives[dirv].intersectWith(this._directives[dirv]);
} else if (thisHasDir || thatHasDir) {
// one or the other has frame-ancestors, copy it.
newRep._directives[dirv] =
( thisHasDir ? this : aCSPRep )._directives[dirv].clone();
}
}
else if (aCSPRep._specCompliant) {
// CSP 1.0 doesn't require default-src, so an intersection only makes
// sense if there is a default-src or both policies have the directive.
if (!thisHasDir && !thisHasDefault) {
// only aCSPRep has a relevant directive
newRep._directives[dirv] = aCSPRep._directives[dirv].clone();
}
else if (!thatHasDir && !thatHasDefault) {
// only "this" has a relevant directive
newRep._directives[dirv] = this._directives[dirv].clone();
}
else {
// both policies have a relevant directive (may be default-src)
var isect1 = thisHasDir ?
this._directives[dirv] :
this._directives[DIRS.DEFAULT_SRC];
var isect2 = thatHasDir ?
aCSPRep._directives[dirv] :
aCSPRep._directives[DIRS.DEFAULT_SRC];
newRep._directives[dirv] = isect1.intersectWith(isect2);
}
}
else {
// pre-1.0 CSP requires a default-src, so we can assume it's here
// (since the parser created one).
var isect1 = thisHasDir ?
this._directives[dirv] :
this._directives[DIRS.DEFAULT_SRC];
var isect2 = thatHasDir ?
aCSPRep._directives[dirv] :
aCSPRep._directives[DIRS.DEFAULT_SRC];
newRep._directives[dirv] = isect1.intersectWith(isect2);
}
}
// REPORT_URI
var reportURIDir = CSPRep.URI_DIRECTIVES.REPORT_URI;
if (this._directives[reportURIDir] && aCSPRep._directives[reportURIDir]) {
newRep._directives[reportURIDir] =
this._directives[reportURIDir].concat(aCSPRep._directives[reportURIDir]);
}
else if (this._directives[reportURIDir]) {
// blank concat makes a copy of the string.
newRep._directives[reportURIDir] = this._directives[reportURIDir].concat();
}
else if (aCSPRep._directives[reportURIDir]) {
// blank concat makes a copy of the string.
newRep._directives[reportURIDir] = aCSPRep._directives[reportURIDir].concat();
}
newRep._allowEval = this.allowsEvalInScripts
&& aCSPRep.allowsEvalInScripts;
newRep._allowInlineScripts = this.allowsInlineScripts
&& aCSPRep.allowsInlineScripts;
newRep._allowInlineStyles = this.allowsInlineStyles
&& aCSPRep.allowsInlineStyles;
newRep._innerWindowID = this._innerWindowID ?
this._innerWindowID : aCSPRep._innerWindowID;
return newRep;
},
/**
* Returns true if "eval" is enabled through the "eval" keyword.
*/
@ -1199,62 +1085,6 @@ CSPSourceList.prototype = {
}
}
return false;
},
/**
* Intersects with another CSPSourceList, deciding the subset directive
* that should be enforced, and returning a new instance.
* @param that
* the other CSPSourceList to intersect "this" with
* @returns
* a new instance of a CSPSourceList representing the intersection
*/
intersectWith:
function cspsd_intersectWith(that) {
var newCSPSrcList = null;
if (!that) return this.clone();
if (this.isNone() || that.isNone())
newCSPSrcList = CSPSourceList.fromString("'none'", this._CSPRep);
if (this.isAll()) newCSPSrcList = that.clone();
if (that.isAll()) newCSPSrcList = this.clone();
if (!newCSPSrcList) {
// the shortcuts didn't apply, must do intersection the hard way.
// -- find only common sources
// XXX (sid): we should figure out a better algorithm for this.
// This is horribly inefficient.
var isrcs = [];
for (var i in this._sources) {
for (var j in that._sources) {
var s = that._sources[j].intersectWith(this._sources[i]);
if (s) {
isrcs.push(s);
}
}
}
// Next, remove duplicates
dup: for (var i = 0; i < isrcs.length; i++) {
for (var j = 0; j < i; j++) {
if (isrcs[i].equals(isrcs[j])) {
isrcs.splice(i, 1);
i--;
continue dup;
}
}
}
newCSPSrcList = new CSPSourceList();
newCSPSrcList._sources = isrcs;
}
if ((!newCSPSrcList._CSPRep) && that._CSPRep) {
newCSPSrcList._CSPRep = that._CSPRep;
}
return newCSPSrcList;
}
}
@ -1668,93 +1498,6 @@ CSPSource.prototype = {
return true;
},
/**
* Determines the intersection of two sources.
* Returns a null object if intersection generates no
* hosts that satisfy it.
* @param that
* the other CSPSource to intersect "this" with
* @returns
* a new instance of a CSPSource representing the intersection
*/
intersectWith:
function(that) {
var newSource = new CSPSource();
// 'self' is not part of the intersection. Intersect the raw values from
// the source, self must be set by someone creating this source.
// When intersecting, we take the more specific of the two: if one scheme,
// host or port is undefined, the other is taken. (This is contrary to
// when "permits" is called -- there, the value of 'self' is looked at
// when a scheme, host or port is undefined.)
// port
if (!this.port)
newSource._port = that.port;
else if (!that.port)
newSource._port = this.port;
else if (this.port === "*")
newSource._port = that.port;
else if (that.port === "*")
newSource._port = this.port;
else if (that.port === this.port)
newSource._port = this.port;
else {
let msg = CSPLocalizer.getFormatStr("notIntersectPort",
[this.toString(), that.toString()]);
cspError(this._CSPRep, msg);
return null;
}
// scheme
if (!this.scheme)
newSource._scheme = that.scheme;
else if (!that.scheme)
newSource._scheme = this.scheme;
if (this.scheme === "*")
newSource._scheme = that.scheme;
else if (that.scheme === "*")
newSource._scheme = this.scheme;
else if (that.scheme === this.scheme)
newSource._scheme = this.scheme;
else {
var msg = CSPLocalizer.getFormatStr("notIntersectScheme",
[this.toString(), that.toString()]);
cspError(this._CSPRep, msg);
return null;
}
// NOTE: Both sources must have a host, if they don't, something funny is
// going on. The fromString() factory method should have set the host to
// * if there's no host specified in the input. Regardless, if a host is
// not present either the scheme is hostless or any host should be allowed.
// This means we can use the other source's host as the more restrictive
// host expression, or if neither are present, we can use "*", but the
// error should still be reported.
// host
if (this.host && that.host) {
newSource._host = this.host.intersectWith(that.host);
} else if (this.host) {
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
[that.toString()]);
cspError(this._CSPRep, msg);
newSource._host = this.host.clone();
} else if (that.host) {
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
[this.toString()]);
cspError(this._CSPRep, msg);
newSource._host = that.host.clone();
} else {
let msg = CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts",
[this.toString(), that.toString()]);
cspError(this._CSPRep, msg);
newSource._host = CSPHost.fromString("*");
}
return newSource;
},
/**
* Compares one CSPSource to another.
*
@ -1903,36 +1646,6 @@ CSPHost.prototype = {
return true;
},
/**
* Determines the intersection of two Hosts.
* Basically, they must be the same, or one must have a wildcard.
* @param that
* the other CSPHost to intersect "this" with
* @returns
* a new instance of a CSPHost representing the intersection
* (or null, if they can't be intersected)
*/
intersectWith:
function(that) {
if (!(this.permits(that) || that.permits(this))) {
// host definitions cannot co-exist without a more general host
// ... one must be a subset of the other, or intersection makes no sense.
return null;
}
// pick the more specific one, if both are same length.
if (this._segments.length == that._segments.length) {
// *.a vs b.a : b.a
return (this._segments[0] === "*") ? that.clone() : this.clone();
}
// different lengths...
// *.b.a vs *.a : *.b.a
// *.b.a vs d.c.b.a : d.c.b.a
return (this._segments.length > that._segments.length) ?
this.clone() : that.clone();
},
/**
* Compares one CSPHost to another.
*

View File

@ -141,15 +141,6 @@ test(
do_check_true( h2.permits("a.b.c")); //"CSPHost a.b.c should allow string a.b.c"
});
test(
function test_CSPHost_intersectWith() {
var h = CSPHost.fromString("*.b.c");
//"*.a.b.c ^ *.b.c should be *.a.b.c"
do_check_eq("*.a.b.c", h.intersectWith(CSPHost.fromString("*.a.b.c")).toString());
//"*.b.c ^ *.d.e should not work (null)"
do_check_eq(null, h.intersectWith(CSPHost.fromString("*.d.e")));
});
///////////////////// Test the Source object //////////////////////
@ -317,42 +308,6 @@ test(
do_check_false(wildcardHostSourceList.permits("http://barbaz.com"));
});
test(
function test_CSPSourceList_intersect() {
// for this test, 'self' values are irrelevant
// policy a /\ policy b intersects policies, not context (where 'self'
// values come into play)
var nullSourceList = CSPSourceList.fromString("'none'");
var simpleSourceList = CSPSourceList.fromString("http://a.com");
var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88");
var singleFooSourceList = CSPSourceList.fromString("https://foo.com");
var allSourceList = CSPSourceList.fromString("*");
//"Intersection of one source with 'none' source list should be none.");
do_check_true(nullSourceList.intersectWith(simpleSourceList).isNone());
//"Intersection of two sources with 'none' source list should be none.");
do_check_true(nullSourceList.intersectWith(doubleSourceList).isNone());
//"Intersection of '*' with 'none' source list should be none.");
do_check_true(nullSourceList.intersectWith(allSourceList).isNone());
//"Intersection of one source with '*' source list should be one source.");
do_check_equivalent(allSourceList.intersectWith(simpleSourceList),
simpleSourceList);
//"Intersection of two sources with '*' source list should be two sources.");
do_check_equivalent(allSourceList.intersectWith(doubleSourceList),
doubleSourceList);
//"Non-overlapping source lists should intersect to 'none'");
do_check_true(simpleSourceList.intersectWith(doubleSourceList).isNone());
//"subset and superset should intersect to subset.");
do_check_equivalent(singleFooSourceList,
doubleSourceList.intersectWith(singleFooSourceList));
//TODO: write more tests?
});
///////////////////// Test the Whole CSP rep object //////////////////////
test(