Bug 529697 - (CSP 1.1) Implement form-action directive [1/4], r=geekboy
authorFrancois Marier <francois@mozilla.com>
Tue, 18 Nov 2014 01:12:00 +0100
changeset 242695 199fcbb1b0187dc5ea98a4b701553bce14bd2b8e
parent 242652 ab85b334225452d907b69cb88d3cc5352ff8b530
child 242696 5a9dc311e76f5b6dcbcd4e141a6dd205bfbee2bb
push id660
push userraliiev@mozilla.com
push dateWed, 18 Feb 2015 20:30:48 +0000
treeherdermozilla-release@49e493494178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeekboy
bugs529697
milestone36.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 529697 - (CSP 1.1) Implement form-action directive [1/4], r=geekboy Teach CSP about the form-action directive from CSP Level 2.
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
@@ -202,16 +202,25 @@ interface nsIContentSecurityPolicy : nsI
    *
    * @return
    *    Whether or not the provided URI is allowed to be used as the
    *    document's base URI. (block the setting if false).
    */
   boolean permitsBaseURI(in nsIURI aURI);
 
   /**
+   * Whether this policy allows submitting HTML forms to a given URI.
+   *
+   * @return
+   *    Whether or not the provided URI is allowed to be used as a
+   *    form's action URI.
+   */
+  boolean permitsFormAction(in nsIURI aURI);
+
+  /**
    * Delegate method called by the service when sub-elements of the protected
    * document are being loaded.  Given a bit of information about the request,
    * decides whether or not the policy is satisfied.
    *
    * Calls to this may trigger violation reports when queried, so
    * this value should not be cached.
    */
   short shouldLoad(in nsContentPolicyType aContentType,
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -1097,17 +1097,17 @@ nsCSPContext::PermitsBaseURI(nsIURI* aUR
 
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (!mPolicies[i]->permitsBaseURI(aURI)) {
       // policy is violated, report to caller if not report-only
       if (!mPolicies[i]->getReportOnlyFlag()) {
         *outPermitsBaseURI = false;
       }
       nsAutoString violatedDirective;
-      mPolicies[i]->getDirectiveStringForBaseURI(violatedDirective);
+      mPolicies[i]->getDirectiveAsString(CSP_BASE_URI, violatedDirective);
       this->AsyncReportViolation(aURI,
                                  nullptr,       /* originalURI in case of redirect */
                                  violatedDirective,
                                  i,             /* policy index        */
                                  EmptyString(), /* no observer subject */
                                  EmptyString(), /* no source file      */
                                  EmptyString(), /* no script sample    */
                                  0);            /* no line number      */
@@ -1122,16 +1122,58 @@ nsCSPContext::PermitsBaseURI(nsIURI* aUR
                   spec.get(),
                   *outPermitsBaseURI ? "allow" : "deny"));
   }
 #endif
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCSPContext::PermitsFormAction(nsIURI* aURI, bool* outPermitsFormAction)
+{
+  // Can't perform check without aURI
+  if (!aURI) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *outPermitsFormAction = true;
+
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    if (!mPolicies[i]->permitsFormAction(aURI)) {
+      // policy is violated, report to caller if not report-only
+      if (!mPolicies[i]->getReportOnlyFlag()) {
+        *outPermitsFormAction = false;
+      }
+      nsAutoString violatedDirective;
+      mPolicies[i]->getDirectiveAsString(CSP_FORM_ACTION, violatedDirective);
+      this->AsyncReportViolation(aURI,
+                                 mSelfURI,
+                                 violatedDirective,
+                                 i,             /* policy index        */
+                                 EmptyString(), /* no observer subject */
+                                 EmptyString(), /* no source file      */
+                                 EmptyString(), /* no script sample    */
+                                 0);            /* no line number      */
+    }
+  }
+
+#ifdef PR_LOGGING
+  {
+    nsAutoCString spec;
+    aURI->GetSpec(spec);
+    CSPCONTEXTLOG(("nsCSPContext::PermitsFormAction, aUri: %s, isAllowed: %s",
+                  spec.get(),
+                  *outPermitsFormAction ? "allow" : "deny"));
+  }
+#endif
+
+  return NS_OK;
+}
+
 /* ========== CSPViolationReportListener implementation ========== */
 
 NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports);
 
 CSPViolationReportListener::CSPViolationReportListener()
 {
 }
 
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -868,16 +868,40 @@ nsCSPPolicy::permitsBaseURI(nsIURI* aUri
 
   // base-uri is only enforced if explicitly defined in the
   // policy - do *not* consult default-src, see:
   // http://www.w3.org/TR/CSP11/#directive-default-src
   return true;
 }
 
 bool
+nsCSPPolicy::permitsFormAction(nsIURI* aUri) const
+{
+#ifdef PR_LOGGING
+  {
+    nsAutoCString spec;
+    aUri->GetSpec(spec);
+    CSPUTILSLOG(("nsCSPPolicy::permitsFormAction, aUri: %s", spec.get()));
+  }
+#endif
+
+  // Try to find a form-action directive
+  for (uint32_t i = 0; i < mDirectives.Length(); i++) {
+    if (mDirectives[i]->equals(CSP_FORM_ACTION)) {
+      return mDirectives[i]->permits(aUri);
+    }
+  }
+
+  // form-action is only enforced if explicitly defined in the
+  // policy - do *not* consult default-src, see:
+  // http://www.w3.org/TR/CSP2/#directive-default-src
+  return true;
+}
+
+bool
 nsCSPPolicy::allows(nsContentPolicyType aContentType,
                     enum CSPKeyword aKeyword,
                     const nsAString& aHashOrNonce) const
 {
   CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
               CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
 
   nsCSPDirective* defaultDir = nullptr;
@@ -970,20 +994,20 @@ nsCSPPolicy::getDirectiveStringForConten
     defaultDir->toString(outDirective);
     return;
   }
   NS_ASSERTION(false, "Can not query directive string for contentType!");
   outDirective.AppendASCII("couldNotQueryViolatedDirective");
 }
 
 void
-nsCSPPolicy::getDirectiveStringForBaseURI(nsAString& outDirective) const
+nsCSPPolicy::getDirectiveAsString(enum CSPDirective aDir, nsAString& outDirective) const
 {
   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
-    if (mDirectives[i]->equals(CSP_BASE_URI)) {
+    if (mDirectives[i]->equals(aDir)) {
       mDirectives[i]->toString(outDirective);
       return;
     }
   }
 }
 
 void
 nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -67,16 +67,17 @@ enum CSPDirective {
   CSP_MEDIA_SRC,
   CSP_FRAME_SRC,
   CSP_FONT_SRC,
   CSP_CONNECT_SRC,
   CSP_REPORT_URI,
   CSP_FRAME_ANCESTORS,
   CSP_REFLECTED_XSS,
   CSP_BASE_URI,
+  CSP_FORM_ACTION,
   // CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
   // because we use it to calculate the size for the char* array.
   CSP_LAST_DIRECTIVE_VALUE
 };
 
 static const char* CSPStrDirectives[] = {
   "default-src",     // CSP_DEFAULT_SRC = 0
   "script-src",      // CSP_SCRIPT_SRC
@@ -85,17 +86,18 @@ static const char* CSPStrDirectives[] = 
   "img-src",         // CSP_IMG_SRC
   "media-src",       // CSP_MEDIA_SRC
   "frame-src",       // CSP_FRAME_SRC
   "font-src",        // CSP_FONT_SRC
   "connect-src",     // CSP_CONNECT_SRC
   "report-uri",      // CSP_REPORT_URI
   "frame-ancestors", // CSP_FRAME_ANCESTORS
   "reflected-xss",   // CSP_REFLECTED_XSS
-  "base-uri"         // CSP_BASE_URI
+  "base-uri",        // CSP_BASE_URI
+  "form-action"      // CSP_FORM_ACTION
 };
 
 inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
 {
   // Make sure all elements in enum CSPDirective got added to CSPStrDirectives.
   static_assert((sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]) ==
                 static_cast<uint32_t>(CSP_LAST_DIRECTIVE_VALUE)),
                 "CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
@@ -328,16 +330,17 @@ class nsCSPPolicy {
     virtual ~nsCSPPolicy();
 
     bool permits(nsContentPolicyType aContentType,
                  nsIURI* aUri,
                  const nsAString& aNonce,
                  bool aWasRedirected,
                  nsAString& outViolatedDirective) const;
     bool permitsBaseURI(nsIURI* aUri) const;
+    bool permitsFormAction(nsIURI* aUri) const;
     bool allows(nsContentPolicyType aContentType,
                 enum CSPKeyword aKeyword,
                 const nsAString& aHashOrNonce) const;
     bool allows(nsContentPolicyType aContentType,
                 enum CSPKeyword aKeyword) const;
     void toString(nsAString& outStr) const;
 
     inline void addDirective(nsCSPDirective* aDir)
@@ -351,17 +354,17 @@ class nsCSPPolicy {
     inline bool getReportOnlyFlag() const
       { return mReportOnly; }
 
     void getReportURIs(nsTArray<nsString> &outReportURIs) const;
 
     void getDirectiveStringForContentType(nsContentPolicyType aContentType,
                                           nsAString& outDirective) const;
 
-    void getDirectiveStringForBaseURI(nsAString& outDirective) const;
+    void getDirectiveAsString(enum CSPDirective aDir, nsAString& outDirective) const;
 
     inline uint32_t getNumDirectives() const
       { return mDirectives.Length(); }
 
   private:
     nsTArray<nsCSPDirective*> mDirectives;
     bool                      mReportOnly;
 };