Bug 1254194: Allow iterating over and inspecting sources of parsed CSP directives. r?ckerschb draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 14 Apr 2016 16:41:47 -0700
changeset 351732 35dae970c0bd48820f19eaecd75abea9d84ac4d0
parent 351731 95206fc37f0d79070efc062ce1dab68bc64b854e
child 351733 c737f5009c93f2fc32aaae999142e5e10c1ff496
push id15524
push usermaglione.k@gmail.com
push dateFri, 15 Apr 2016 01:09:46 +0000
reviewersckerschb
bugs1254194
milestone48.0a1
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:
+    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___ */