Bug 1254194: Allow iterating over and inspecting sources of parsed CSP directives. r=ckerschb
authorKris Maglione <maglione.k@gmail.com>
Sat, 23 Apr 2016 20:42:43 -0700
changeset 332570 f9c7140ccd2e86b8ad75e54db5ae7d3c21c66819
parent 332569 66ce70f225330c89731d4c8f7a916a27a7ca6b3f
child 332571 58fc5fb221e0eb4dfb23779215873fb870e980c4
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb
bugs1254194
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1254194: Allow iterating over and inspecting sources of parsed CSP directives. r=ckerschb MozReview-Commit-ID: G8b86UvSv0y
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/security/nsCSPContext.cpp
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
--- 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:
+    explicit 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___ */