Bug 1254194: Allow iterating over and inspecting sources of parsed CSP directives. r?ckerschb
MozReview-Commit-ID: G8b86UvSv0y
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -16,16 +16,22 @@ interface nsIURI;
* nsIContentSecurityPolicy
* 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.
*/
typedef unsigned short CSPDirective;
+%{C++
+class nsCSPPolicy;
+%}
+
+[ptr] native CSPPolicyPtr(const nsCSPPolicy);
+
[scriptable, builtinclass, uuid(b3c4c0ae-bd5e-4cad-87e0-8d210dbb3f9f)]
interface nsIContentSecurityPolicy : nsISerializable
{
/**
* Directives supported by Content Security Policy. These are enums for
* the CSPDirective type.
* The NO_DIRECTIVE entry is used for checking default permissions and
* returning failure when asking CSP which directive to check.
@@ -56,16 +62,23 @@ interface nsIContentSecurityPolicy : nsI
/**
* Accessor method for a read-only string version of the policy at a given
* index.
*/
AString getPolicy(in unsigned long index);
/**
+ * Accessor method for a read-only pointer the policy object at a given
+ * index. Returns a null pointer if the index is larger than the current
+ * policy count.
+ */
+ [noscript,notxpcom,nostdcall] CSPPolicyPtr GetPolicy(in unsigned long index);
+
+ /**
* Returns the number of policies attached to this CSP instance. Useful with
* getPolicy().
*/
readonly attribute unsigned long policyCount;
/**
* Returns whether this policy uses the directive upgrade-insecure-requests.
* Please note that upgrade-insecure-reqeusts also applies if the parent or
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -283,16 +283,25 @@ nsCSPContext::GetPolicy(uint32_t aIndex,
{
if (aIndex >= mPolicies.Length()) {
return NS_ERROR_ILLEGAL_VALUE;
}
mPolicies[aIndex]->toString(outStr);
return NS_OK;
}
+const nsCSPPolicy*
+nsCSPContext::GetPolicy(uint32_t aIndex)
+{
+ if (aIndex >= mPolicies.Length()) {
+ return nullptr;
+ }
+ return mPolicies[aIndex];
+}
+
NS_IMETHODIMP
nsCSPContext::GetPolicyCount(uint32_t *outPolicyCount)
{
*outPolicyCount = mPolicies.Length();
return NS_OK;
}
NS_IMETHODIMP
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -370,16 +370,22 @@ nsCSPSchemeSrc::permits(nsIURI* aUri, co
nsAutoCString spec;
aUri->GetSpec(spec);
CSPUTILSLOG(("nsCSPSchemeSrc::permits, aUri: %s", spec.get()));
}
MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure);
}
+bool
+nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return aVisitor->visitSchemeSrc(*this);
+}
+
void
nsCSPSchemeSrc::toString(nsAString& outStr) const
{
outStr.Append(mScheme);
outStr.AppendASCII(":");
}
/* ===== nsCSPHostSrc ======================== */
@@ -529,16 +535,22 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
return false;
}
}
// At the end: scheme, host, path, and port match -> allow the load.
return true;
}
+bool
+nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return aVisitor->visitHostSrc(*this);
+}
+
void
nsCSPHostSrc::toString(nsAString& outStr) const
{
// If mHost is a single "*", we append the wildcard and return.
if (mHost.EqualsASCII("*") &&
mScheme.IsEmpty() &&
mPort.IsEmpty()) {
outStr.Append(mHost);
@@ -606,16 +618,22 @@ nsCSPKeywordSrc::allows(enum CSPKeyword
if (mInvalidated) {
NS_ASSERTION(mKeyword == CSP_UNSAFE_INLINE,
"should only invalidate unsafe-inline within script-src");
return false;
}
return mKeyword == aKeyword;
}
+bool
+nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return aVisitor->visitKeywordSrc(*this);
+}
+
void
nsCSPKeywordSrc::toString(nsAString& outStr) const
{
if (mInvalidated) {
MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE,
"can only ignore 'unsafe-inline' within toString()");
return;
}
@@ -662,16 +680,22 @@ nsCSPNonceSrc::allows(enum CSPKeyword aK
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
if (aKeyword != CSP_NONCE) {
return false;
}
return mNonce.Equals(aHashOrNonce);
}
+bool
+nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return aVisitor->visitNonceSrc(*this);
+}
+
void
nsCSPNonceSrc::toString(nsAString& outStr) const
{
outStr.AppendASCII(CSP_EnumToKeyword(CSP_NONCE));
outStr.Append(mNonce);
outStr.AppendASCII("'");
}
@@ -719,16 +743,22 @@ nsCSPHashSrc::allows(enum CSPKeyword aKe
// The NSS Base64 encoder automatically adds linebreaks "\r\n" every 64
// characters. We need to remove these so we can properly validate longer
// (SHA-512) base64-encoded hashes
hash.StripChars("\r\n");
return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
}
+bool
+nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return aVisitor->visitHashSrc(*this);
+}
+
void
nsCSPHashSrc::toString(nsAString& outStr) const
{
outStr.AppendASCII("'");
outStr.Append(mAlgorithm);
outStr.AppendASCII("-");
outStr.Append(mHash);
outStr.AppendASCII("'");
@@ -740,16 +770,22 @@ nsCSPReportURI::nsCSPReportURI(nsIURI *a
:mReportURI(aURI)
{
}
nsCSPReportURI::~nsCSPReportURI()
{
}
+bool
+nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const
+{
+ return false;
+}
+
void
nsCSPReportURI::toString(nsAString& outStr) const
{
nsAutoCString spec;
nsresult rv = mReportURI->GetSpec(spec);
if (NS_FAILED(rv)) {
return;
}
@@ -944,16 +980,27 @@ nsCSPDirective::getReportURIs(nsTArray<n
nsString tmpReportURI;
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
tmpReportURI.Truncate();
mSrcs[i]->toString(tmpReportURI);
outReportURIs.AppendElement(tmpReportURI);
}
}
+bool
+nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const
+{
+ for (uint32_t i = 0; i < mSrcs.Length(); i++) {
+ if (!mSrcs[i]->visit(aVisitor)) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool nsCSPDirective::equals(CSPDirective aDirective) const
{
return (mDirective == aDirective);
}
/* =============== nsCSPChildSrcDirective ============= */
nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
@@ -1251,8 +1298,19 @@ nsCSPPolicy::getReportURIs(nsTArray<nsSt
{
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
mDirectives[i]->getReportURIs(outReportURIs);
return;
}
}
}
+
+bool
+nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const
+{
+ for (uint32_t i = 0; i < mDirectives.Length(); i++) {
+ if (mDirectives[i]->equals(aDir)) {
+ return mDirectives[i]->visitSrcs(aVisitor);
+ }
+ }
+ return false;
+}
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -172,128 +172,182 @@ class nsCSPHostSrc;
nsCSPHostSrc* CSP_CreateHostSrcFromURI(nsIURI* aURI);
bool CSP_IsValidDirective(const nsAString& aDir);
bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
+class nsCSPSrcVisitor;
/* =============== nsCSPSrc ================== */
class nsCSPBaseSrc {
public:
nsCSPBaseSrc();
virtual ~nsCSPBaseSrc();
virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
bool aReportOnly, bool aUpgradeInsecure) const;
virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
+ virtual bool visit(nsCSPSrcVisitor* aVisitor) const = 0;
virtual void toString(nsAString& outStr) const = 0;
};
/* =============== nsCSPSchemeSrc ============ */
class nsCSPSchemeSrc : public nsCSPBaseSrc {
public:
explicit nsCSPSchemeSrc(const nsAString& aScheme);
virtual ~nsCSPSchemeSrc();
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
bool aReportOnly, bool aUpgradeInsecure) const;
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
void toString(nsAString& outStr) const;
+ inline void getScheme(nsAString& outStr) const
+ { outStr.Assign(mScheme); };
+
private:
nsString mScheme;
};
/* =============== nsCSPHostSrc ============== */
class nsCSPHostSrc : public nsCSPBaseSrc {
public:
explicit nsCSPHostSrc(const nsAString& aHost);
virtual ~nsCSPHostSrc();
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
bool aReportOnly, bool aUpgradeInsecure) const;
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
void toString(nsAString& outStr) const;
void setScheme(const nsAString& aScheme);
void setPort(const nsAString& aPort);
void appendPath(const nsAString &aPath);
+ inline void getScheme(nsAString& outStr) const
+ { outStr.Assign(mScheme); };
+
+ inline void getHost(nsAString& outStr) const
+ { outStr.Assign(mHost); };
+
+ inline void getPort(nsAString& outStr) const
+ { outStr.Assign(mPort); };
+
+ inline void getPath(nsAString& outStr) const
+ { outStr.Assign(mPath); };
+
private:
nsString mScheme;
nsString mHost;
nsString mPort;
nsString mPath;
};
/* =============== nsCSPKeywordSrc ============ */
class nsCSPKeywordSrc : public nsCSPBaseSrc {
public:
explicit nsCSPKeywordSrc(CSPKeyword aKeyword);
virtual ~nsCSPKeywordSrc();
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
void toString(nsAString& outStr) const;
void invalidate();
+ inline CSPKeyword getKeyword() const
+ { return mKeyword; };
+
private:
CSPKeyword mKeyword;
// invalidate 'unsafe-inline' if nonce- or hash-source specified
bool mInvalidated;
};
/* =============== nsCSPNonceSource =========== */
class nsCSPNonceSrc : public nsCSPBaseSrc {
public:
explicit nsCSPNonceSrc(const nsAString& aNonce);
virtual ~nsCSPNonceSrc();
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
bool aReportOnly, bool aUpgradeInsecure) const;
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
void toString(nsAString& outStr) const;
+ inline void getNonce(nsAString& outStr) const
+ { outStr.Assign(mNonce); };
+
private:
nsString mNonce;
};
/* =============== nsCSPHashSource ============ */
class nsCSPHashSrc : public nsCSPBaseSrc {
public:
nsCSPHashSrc(const nsAString& algo, const nsAString& hash);
virtual ~nsCSPHashSrc();
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
void toString(nsAString& outStr) const;
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
+
+ inline void getAlgorithm(nsAString& outStr) const
+ { outStr.Assign(mAlgorithm); };
+
+ inline void getHash(nsAString& outStr) const
+ { outStr.Assign(mHash); };
private:
nsString mAlgorithm;
nsString mHash;
};
/* =============== nsCSPReportURI ============ */
class nsCSPReportURI : public nsCSPBaseSrc {
public:
explicit nsCSPReportURI(nsIURI* aURI);
virtual ~nsCSPReportURI();
+ bool visit(nsCSPSrcVisitor* aVisitor) const;
void toString(nsAString& outStr) const;
private:
nsCOMPtr<nsIURI> mReportURI;
};
+/* =============== nsCSPSrcVisitor ================== */
+
+class nsCSPSrcVisitor {
+ public:
+ virtual bool visitSchemeSrc(const nsCSPSchemeSrc& src) = 0;
+
+ virtual bool visitHostSrc(const nsCSPHostSrc& src) = 0;
+
+ virtual bool visitKeywordSrc(const nsCSPKeywordSrc& src) = 0;
+
+ virtual bool visitNonceSrc(const nsCSPNonceSrc& src) = 0;
+
+ virtual bool visitHashSrc(const nsCSPHashSrc& src) = 0;
+
+ protected:
+ nsCSPSrcVisitor() {};
+ virtual ~nsCSPSrcVisitor() {};
+};
+
/* =============== nsCSPDirective ============= */
class nsCSPDirective {
public:
explicit nsCSPDirective(CSPDirective aDirective);
virtual ~nsCSPDirective();
virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
@@ -309,16 +363,18 @@ class nsCSPDirective {
inline bool isDefaultDirective() const
{ return mDirective == nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; }
virtual bool equals(CSPDirective aDirective) const;
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
+ bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;
+
private:
CSPDirective mDirective;
nsTArray<nsCSPBaseSrc*> mSrcs;
};
/* =============== nsCSPChildSrcDirective ============= */
/*
@@ -471,16 +527,18 @@ class nsCSPPolicy {
void getDirectiveStringForContentType(nsContentPolicyType aContentType,
nsAString& outDirective) const;
void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;
inline uint32_t getNumDirectives() const
{ return mDirectives.Length(); }
+ bool visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const;
+
private:
nsUpgradeInsecureDirective* mUpgradeInsecDir;
nsTArray<nsCSPDirective*> mDirectives;
bool mReportOnly;
nsString mReferrerPolicy;
};
#endif /* nsCSPUtils_h___ */