Bug 911547 - make nsIContentSecurityPolicy serializable and trigger read/write from nsPrincipal. r=jst,grobinson

--HG--
extra : rebase_source : 3e1846e15538729f3c94f5c1470959b5d7b31f0f
This commit is contained in:
Sid Stamm 2014-01-23 15:34:59 -08:00
Родитель 499b09923b
Коммит f6b4704ae8
6 изменённых файлов: 130 добавлений и 26 удалений

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

@ -491,9 +491,22 @@ nsPrincipal::Read(nsIObjectInputStream* aStream)
rv = aStream->ReadBoolean(&inMozBrowser);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
rv = Init(codebase, appId, inMozBrowser);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetCsp(csp);
NS_ENSURE_SUCCESS(rv, rv);
// need to link in the CSP context here (link in a reference to this
// nsIPrincipal and to the URI of the protected resource).
if (csp) {
csp->SetRequestContext(codebase, nullptr, this, nullptr);
}
SetDomain(domain);
return NS_OK;
@ -519,6 +532,13 @@ nsPrincipal::Write(nsIObjectOutputStream* aStream)
aStream->Write32(mAppId);
aStream->WriteBoolean(mInMozBrowser);
rv = NS_WriteOptionalCompoundObject(aStream, mCSP,
NS_GET_IID(nsIContentSecurityPolicy),
true);
if (NS_FAILED(rv)) {
return rv;
}
// mCodebaseImmutable and mDomainImmutable will be recomputed based
// on the deserialized URIs in Read().

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

@ -2,11 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsISerializable.idl"
interface nsIURI;
interface nsIChannel;
interface nsIDocShell;
interface nsIPrincipal;
/**
* nsIContentSecurityPolicy
@ -15,8 +16,8 @@ interface nsIDocShell;
* one of these per document/principal.
*/
[scriptable, uuid(2e7875a3-8cb5-4ebb-905b-af0a90dae594)]
interface nsIContentSecurityPolicy : nsISupports
[scriptable, uuid(8b91f829-b1bf-4327-8ece-4000aa823394)]
interface nsIContentSecurityPolicy : nsISerializable
{
/**
@ -180,10 +181,18 @@ interface nsIContentSecurityPolicy : nsISupports
const unsigned short VIOLATION_TYPE_HASH_STYLE = 7;
/**
* 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.
* Called after the CSP object is created to fill in appropriate request
* context and give it a reference to its owning principal for violation
* report generation.
* This will use whatever data is available, choosing earlier arguments first
* if multiple are available. Either way, it will attempt to obtain the URI,
* referrer and the principal from whatever is available. If the channel is
* available, it'll also store that for processing policy-uri directives.
*/
void scanRequestData(in nsIChannel aChannel);
void setRequestContext(in nsIURI selfURI,
in nsIURI referrer,
in nsIPrincipal documentPrincipal,
in nsIChannel aChannel);
/**
* Verifies ancestry as permitted by the policy.

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

@ -11,6 +11,10 @@
* that ContentSecurityPolicy has to do.
*/
// these identifiers are also defined in contentSecurityPolicy.manifest.
const CSP_CLASS_ID = Components.ID("{d1680bb4-1ac0-4772-9437-1188375e44f2}");
const CSP_CONTRACT_ID = "@mozilla.org/contentsecuritypolicy;1";
/* :::::::: Constants and Helpers ::::::::::::::: */
const Cc = Components.classes;
@ -123,8 +127,17 @@ function ContentSecurityPolicy() {
}
ContentSecurityPolicy.prototype = {
classID: Components.ID("{d1680bb4-1ac0-4772-9437-1188375e44f2}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentSecurityPolicy]),
classID: CSP_CLASS_ID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentSecurityPolicy,
Ci.nsISerializable,
Ci.nsISupports]),
// Class info is required to be able to serialize
classInfo: XPCOMUtils.generateCI({classID: CSP_CLASS_ID,
contractID: CSP_CONTRACT_ID,
interfaces: [Ci.nsIContentSecurityPolicy,
Ci.nsISerializable],
flags: Ci.nsIClassInfo.MAIN_THREAD_ONLY}),
get isInitialized() {
return this._isInitialized;
@ -343,28 +356,45 @@ ContentSecurityPolicy.prototype = {
/**
* Given an nsIHttpChannel, fill out the appropriate data.
*/
scanRequestData:
function(aChannel) {
if (!aChannel)
setRequestContext:
function(aSelfURI, aReferrerURI, aPrincipal, aChannel) {
// this requires either a self URI or a http channel.
if (!aSelfURI && !aChannel)
return;
// Save the docRequest for fetching a policy-uri
this._weakDocRequest = Cu.getWeakReference(aChannel);
if (aChannel) {
// Save the docRequest for fetching a policy-uri
this._weakDocRequest = Cu.getWeakReference(aChannel);
}
// save the document URI (minus <fragment>) and referrer for reporting
let uri = aChannel.URI.cloneIgnoringRef();
let uri = aSelfURI ? aSelfURI.cloneIgnoringRef() : aChannel.URI.cloneIgnoringRef();
try { // GetUserPass throws for some protocols without userPass
uri.userPass = '';
} catch (ex) {}
this._request = uri.asciiSpec;
this._requestOrigin = uri;
//store a reference to the principal, that can later be used in shouldLoad
this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getChannelPrincipal(aChannel));
// store a reference to the principal, that can later be used in shouldLoad
if (aPrincipal) {
this._weakRequestPrincipal = Cu.getWeakReference(aPrincipal);
} else if (aChannel) {
this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getChannelPrincipal(aChannel));
} else {
CSPdebug("No principal or channel for document context; violation reports may not work.");
}
if (aChannel instanceof Ci.nsIHttpChannel && aChannel.referrer) {
// pick one: referrerURI, channel's referrer, or null (first available)
let ref = null;
if (aReferrerURI)
ref = aReferrerURI;
else if (aChannel instanceof Ci.nsIHttpChannel)
ref = aChannel.referrer;
if (ref) {
let referrer = aChannel.referrer.cloneIgnoringRef();
try { // GetUserPass throws for some protocols without userPass
referrer.userPass = '';
@ -383,7 +413,7 @@ ContentSecurityPolicy.prototype = {
function csp_appendPolicy(aPolicy, selfURI, aReportOnly, aSpecCompliant) {
#ifndef MOZ_B2G
CSPdebug("APPENDING POLICY: " + aPolicy);
CSPdebug(" SELF: " + selfURI.asciiSpec);
CSPdebug(" SELF: " + (selfURI ? selfURI.asciiSpec : " null"));
CSPdebug("CSP 1.0 COMPLIANT : " + aSpecCompliant);
#endif
@ -905,6 +935,51 @@ ContentSecurityPolicy.prototype = {
}, Ci.nsIThread.DISPATCH_NORMAL);
},
/* ........ nsISerializable Methods: .............. */
read:
function(aStream) {
this._requestOrigin = aStream.readObject(true);
this._requestOrigin.QueryInterface(Ci.nsIURI);
for (let pCount = aStream.read32(); pCount > 0; pCount--) {
let polStr = aStream.readString();
let reportOnly = aStream.readBoolean();
let specCompliant = aStream.readBoolean();
// don't need self info because when the policy is turned back into a
// string, 'self' is replaced with the explicit source expression.
this.appendPolicy(polStr, null, reportOnly, specCompliant);
}
// NOTE: the document instance that's deserializing this object (via its
// principal) should hook itself into this._principal manually. If they
// don't, the CSP reports will likely be blocked by nsMixedContentBlocker.
},
write:
function(aStream) {
// need to serialize the context: request origin and such. They are
// used when sending reports. Since _request and _requestOrigin are just
// different representations of the same thing, only save _requestOrigin
// (an nsIURI).
aStream.writeCompoundObject(this._requestOrigin, Ci.nsIURI, true);
// we can't serialize a reference to the principal that triggered this
// instance to serialize, so when this is deserialized by the principal the
// caller must hook it up manually by calling setRequestContext on this
// instance with the appropriate nsIChannel.
// Finally, serialize all the policies.
aStream.write32(this._policies.length);
for each (var policy in this._policies) {
aStream.writeWStringZ(policy.toString());
aStream.writeBoolean(policy._reportOnlyMode);
aStream.writeBoolean(policy._specCompliant);
}
},
};
// The POST of the violation report (if it happens) should not follow

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

@ -2691,7 +2691,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
aChannel->GetURI(getter_AddRefs(selfURI));
// Store the request context for violation reports
csp->ScanRequestData(aChannel);
csp->SetRequestContext(nullptr, nullptr, nullptr, aChannel);
// ----- if the doc is an app and we want a default CSP, apply it.
if (applyAppDefaultCSP) {

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

@ -12,7 +12,7 @@
<script type="application/javascript;version=1.8">
"use strict";
// Ensure that `scanRequestData` doesn't throw with app:// URLs
// Ensure that `setRequestContext` doesn't throw with app:// URLs
const csp = SpecialPowers.Cc["@mozilla.org/contentsecuritypolicy;1"]
.createInstance(SpecialPowers.Ci.nsIContentSecurityPolicy);
@ -44,10 +44,10 @@
let appchan = SpecialPowers.Services.io.newChannel(gManifestURL, null, null);
try {
csp.scanRequestData(appchan);
ok(true, "scanRequestData hasn't thown");
csp.setRequestContext(null, null, null, appchan);
ok(true, "setRequestContext hasn't thown");
} catch(e) {
ok(false, "scanRequestData throws");
ok(false, "setRequestContext throws");
}
cleanup()

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

@ -76,7 +76,7 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
dump("Created test " + id + " : " + policy + "\n\n");
// make the reports seem authentic by "binding" them to a channel.
csp.scanRequestData(selfchan);
csp.setRequestContext(selfuri, null, null, selfchan);
// Load up the policy
// set as report-only if that's the case