Bug 1026520 - CSP: Inline report sending into allows - csp changes (r=dveditz)
authorChristoph Kerschbaumer <mozilla@christophkerschbaumer.com>
Thu, 17 Sep 2015 22:34:16 -0700
changeset 295793 8f860449c1a96be345671ca3b7b92beff4fa7511
parent 295791 1ab076078dc357724b011db374d035e7a1563b21
child 295794 19eb4586448f4a9c5272a057218f63956ac8b8ca
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdveditz
bugs1026520
milestone43.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 1026520 - CSP: Inline report sending into allows - csp changes (r=dveditz)
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/security/nsCSPContext.cpp
dom/security/nsCSPContext.h
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -15,17 +15,17 @@ 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;
 
-[scriptable, uuid(b622b0f8-ee51-4f7a-8c23-b3bce20e752e)]
+[scriptable, uuid(fe07ab08-21ba-470c-8b89-78d0e7298c68)]
 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.
    *
@@ -96,97 +96,48 @@ interface nsIContentSecurityPolicy : nsI
    * @param aPolicy
    *        String representation of the policy (e.g., header value)
    * @param reportOnly
    *        Should this policy affect content, script and style processing or
    *        just send reports if it is violated?
    */
   void appendPolicy(in AString policyString, in boolean reportOnly);
 
-  /**
-   * Whether this policy allows in-page script.
-   * @param shouldReportViolations
-   *     Whether or not the use of inline script should be reported.
-   *     This function always returns "true" for report-only policies, but when
-   *     any policy (report-only or otherwise) is violated,
-   *     shouldReportViolations is true as well.
+  /*
+   * Whether this policy allows inline script or style.
+   * @param aContentPolicyType Either TYPE_SCRIPT or TYPE_STYLESHEET
+   * @param aNonce The nonce string to check against the policy
+   * @param aContent  The content of the inline resource to hash
+   *        (and compare to the hashes listed in the policy)
+   * @param aLineNumber The line number of the inline resource
+   *        (used for reporting)
    * @return
-   *     Whether or not the effects of the inline script should be allowed
-   *     (block the compilation if false).
+   *     Whether or not the effects of the inline style should be allowed
+   *     (block the rules if false).
    */
-  boolean getAllowsInlineScript(out boolean shouldReportViolations);
+  boolean getAllowsInline(in nsContentPolicyType aContentPolicyType,
+                          in AString aNonce,
+                          in AString aContent,
+                          in unsigned long aLineNumber);
 
   /**
    * whether this policy allows eval and eval-like functions
    * such as setTimeout("code string", time).
    * @param shouldReportViolations
    *     Whether or not the use of eval should be reported.
    *     This function returns "true" when violating report-only policies, but
    *     when any policy (report-only or otherwise) is violated,
    *     shouldReportViolations is true as well.
    * @return
    *     Whether or not the effects of the eval call should be allowed
    *     (block the call if false).
    */
   boolean getAllowsEval(out boolean shouldReportViolations);
 
   /**
-   * Whether this policy allows in-page styles.
-   * This includes <style> tags with text content and style="" attributes in
-   * HTML elements.
-   * @param shouldReportViolations
-   *     Whether or not the use of inline style should be reported.
-   *     If there are report-only policies, this function may return true
-   *     (don't block), but one or more policy may still want to send
-   *     violation reports so shouldReportViolations will be true even if the
-   *     inline style should be permitted.
-   * @return
-   *     Whether or not the effects of the inline style should be allowed
-   *     (block the rules if false).
-   */
-  boolean getAllowsInlineStyle(out boolean shouldReportViolations);
-
-  /**
-   * Whether this policy accepts the given nonce
-   * @param aNonce
-   *     The nonce string to check against the policy
-   * @param aContentType
-   *     The type of element on which we encountered this nonce
-   * @param shouldReportViolation
-   *     Whether or not the use of an incorrect nonce should be reported.
-   *     This function always returns "true" for report-only policies, but when
-   *     the report-only policy is violated, shouldReportViolation is true as
-   *     well.
-   * @return
-   *     Whether or not this nonce is valid
-   */
-   boolean getAllowsNonce(in AString aNonce,
-                          in unsigned long aContentType,
-                          out boolean shouldReportViolation);
-
-   /**
-    * Whether this policy accepts the given inline resource based on the hash
-    * of its content.
-    * @param aContent
-    *     The content of the inline resource to hash (and compare to the
-    *     hashes listed in the policy)
-    * @param aContentType
-    *     The type of inline element (script or style)
-    * @param shouldReportViolation
-    *     Whether this inline resource should be reported as a hash-source
-    *     violation. If there are no hash-sources in the policy, this is
-    *     always false.
-    * @return
-    *     Whether or not this inline resource is whitelisted by a hash-source
-    */
-   boolean getAllowsHash(in AString aContent,
-                         in unsigned short aContentType,
-                         out boolean shouldReportViolation);
-
-  /**
    * For each violated policy (of type violationType), log policy violation on
    * the Error Console and send a report to report-uris present in the violated
    * policies.
    *
    * @param violationType
    *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    * @param sourceFile
    *     name of the source file containing the violation (if available)
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -372,115 +372,131 @@ nsCSPContext::AppendPolicy(const nsAStri
   if (policy) {
     mPolicies.AppendElement(policy);
     // reset cache since effective policy changes
     mShouldLoadCache.Clear();
   }
   return NS_OK;
 }
 
-// aNonceOrContent either holds the nonce-value or otherwise the content
-// of the element to be hashed.
 NS_IMETHODIMP
-nsCSPContext::getAllowsInternal(nsContentPolicyType aContentType,
-                                enum CSPKeyword aKeyword,
-                                const nsAString& aNonceOrContent,
-                                bool* outShouldReportViolation,
-                                bool* outIsAllowed) const
+nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
+                            bool* outAllowsEval)
 {
   *outShouldReportViolation = false;
-  *outIsAllowed = true;
-
-  // Skip things that aren't hash/nonce compatible
-  if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) {
-    if (!(aContentType == nsIContentPolicy::TYPE_SCRIPT ||
-          aContentType == nsIContentPolicy::TYPE_STYLESHEET)) {
-      *outIsAllowed = false;
-      return NS_OK;
-    }
-  }
+  *outAllowsEval = true;
 
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
-    if (!mPolicies[i]->allows(aContentType,
-                              aKeyword,
-                              aNonceOrContent)) {
+    if (!mPolicies[i]->allows(nsIContentPolicy::TYPE_SCRIPT,
+                              CSP_UNSAFE_EVAL, EmptyString())) {
       // policy is violated: must report the violation and allow the inline
       // script if the policy is report-only.
       *outShouldReportViolation = true;
       if (!mPolicies[i]->getReportOnlyFlag()) {
-        *outIsAllowed = false;
+        *outAllowsEval = false;
       }
     }
   }
-  CSPCONTEXTLOG(("nsCSPContext::getAllowsInternal, aContentType: %d, aKeyword: %s, aNonceOrContent: %s, isAllowed: %s",
-                aContentType,
-                aKeyword == CSP_HASH ? "hash" : CSP_EnumToKeyword(aKeyword),
-                NS_ConvertUTF16toUTF8(aNonceOrContent).get(),
-                *outIsAllowed ? "load" : "deny"));
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCSPContext::GetAllowsInlineScript(bool* outShouldReportViolation,
-                                    bool* outAllowsInlineScript)
+// Helper function to report inline violations
+void
+nsCSPContext::reportInlineViolation(nsContentPolicyType aContentType,
+                                    const nsAString& aNonce,
+                                    const nsAString& aContent,
+                                    const nsAString& aViolatedDirective,
+                                    uint32_t aViolatedPolicyIndex, // TODO, use report only flag for that
+                                    uint32_t aLineNumber)
 {
-  return getAllowsInternal(nsIContentPolicy::TYPE_SCRIPT,
-                           CSP_UNSAFE_INLINE,
-                           EmptyString(),
-                           outShouldReportViolation,
-                           outAllowsInlineScript);
-}
+  nsString observerSubject;
+  // if the nonce is non empty, then we report the nonce error, otherwise
+  // let's report the hash error; no need to report the unsafe-inline error
+  // anymore.
+  if (!aNonce.IsEmpty()) {
+    observerSubject = (aContentType == nsIContentPolicy::TYPE_SCRIPT)
+                      ? NS_LITERAL_STRING(SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC)
+                      : NS_LITERAL_STRING(STYLE_NONCE_VIOLATION_OBSERVER_TOPIC);
+  }
+  else {
+    observerSubject = (aContentType == nsIContentPolicy::TYPE_SCRIPT)
+                      ? NS_LITERAL_STRING(SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC)
+                      : NS_LITERAL_STRING(STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
+  }
 
-NS_IMETHODIMP
-nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
-                            bool* outAllowsEval)
-{
-  return getAllowsInternal(nsIContentPolicy::TYPE_SCRIPT,
-                           CSP_UNSAFE_EVAL,
-                           EmptyString(),
-                           outShouldReportViolation,
-                           outAllowsEval);
+  nsCOMPtr<nsISupportsCString> selfICString(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+  if (selfICString) {
+    selfICString->SetData(nsDependentCString("self"));
+  }
+  nsCOMPtr<nsISupports> selfISupports(do_QueryInterface(selfICString));
+
+  // use selfURI as the sourceFile
+  nsAutoCString sourceFile;
+  mSelfURI->GetSpec(sourceFile);
+
+  nsAutoString codeSample(aContent);
+  // cap the length of the script sample at 40 chars
+  if (codeSample.Length() > 40) {
+    codeSample.Truncate(40);
+    codeSample.AppendLiteral("...");
+  }
+  AsyncReportViolation(selfISupports,                      // aBlockedContentSource
+                       mSelfURI,                           // aOriginalURI
+                       aViolatedDirective,                 // aViolatedDirective
+                       aViolatedPolicyIndex,               // aViolatedPolicyIndex
+                       observerSubject,                    // aObserverSubject
+                       NS_ConvertUTF8toUTF16(sourceFile),  // aSourceFile
+                       codeSample,                         // aScriptSample
+                       aLineNumber);                       // aLineNum
 }
 
 NS_IMETHODIMP
-nsCSPContext::GetAllowsInlineStyle(bool* outShouldReportViolation,
-                                   bool* outAllowsInlineStyle)
+nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
+                              const nsAString& aNonce,
+                              const nsAString& aContent,
+                              uint32_t aLineNumber,
+                              bool* outAllowsInline)
 {
-  return getAllowsInternal(nsIContentPolicy::TYPE_STYLESHEET,
-                           CSP_UNSAFE_INLINE,
-                           EmptyString(),
-                           outShouldReportViolation,
-                           outAllowsInlineStyle);
+  *outAllowsInline = true;
+
+  MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
+             "We should only see external content policy types here.");
+
+  if (aContentType != nsIContentPolicy::TYPE_SCRIPT &&
+      aContentType != nsIContentPolicy::TYPE_STYLESHEET) {
+    MOZ_ASSERT(false, "can only allow inline for script or style");
+    return NS_OK;
+  }
+
+  // always iterate all policies, otherwise we might not send out all reports
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    bool allowed =
+      mPolicies[i]->allows(aContentType, CSP_UNSAFE_INLINE, EmptyString()) ||
+      mPolicies[i]->allows(aContentType, CSP_NONCE, aNonce) ||
+      mPolicies[i]->allows(aContentType, CSP_HASH, aContent);
+
+    if (!allowed) {
+      // policy is violoated: deny the load unless policy is report only and
+      // report the violation.
+      if (!mPolicies[i]->getReportOnlyFlag()) {
+        *outAllowsInline = false;
+      }
+      nsAutoString violatedDirective;
+      mPolicies[i]->getDirectiveStringForContentType(aContentType, violatedDirective);
+      reportInlineViolation(aContentType,
+                            aNonce,
+                            aContent,
+                            violatedDirective,
+                            i,
+                            aLineNumber);
+    }
+  }
+  return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCSPContext::GetAllowsNonce(const nsAString& aNonce,
-                             uint32_t aContentType,
-                             bool* outShouldReportViolation,
-                             bool* outAllowsNonce)
-{
-  return getAllowsInternal(aContentType,
-                           CSP_NONCE,
-                           aNonce,
-                           outShouldReportViolation,
-                           outAllowsNonce);
-}
-
-NS_IMETHODIMP
-nsCSPContext::GetAllowsHash(const nsAString& aContent,
-                            uint16_t aContentType,
-                            bool* outShouldReportViolation,
-                            bool* outAllowsHash)
-{
-  return getAllowsInternal(aContentType,
-                           CSP_HASH,
-                           aContent,
-                           outShouldReportViolation,
-                           outAllowsHash);
-}
 
 /**
  * Reduces some code repetition for the various logging situations in
  * LogViolationDetails.
  *
  * Call-sites for the eval/inline checks recieve two return values: allows
  * and violates.  Based on those, they must choose whether to call
  * LogViolationDetails or not.  Policies that are report-only allow the
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -53,32 +53,34 @@ class nsCSPContext : public nsIContentSe
                                   const nsAString& aViolatedDirective,
                                   uint32_t aViolatedPolicyIndex,
                                   const nsAString& aObserverSubject,
                                   const nsAString& aSourceFile,
                                   const nsAString& aScriptSample,
                                   uint32_t aLineNum);
 
   private:
-    NS_IMETHODIMP getAllowsInternal(nsContentPolicyType aContentType,
-                                    enum CSPKeyword aKeyword,
-                                    const nsAString& aNonceOrContent,
-                                    bool* outShouldReportViolations,
-                                    bool* outIsAllowed) const;
-
     bool permitsInternal(CSPDirective aDir,
                          nsIURI* aContentLocation,
                          nsIURI* aOriginalURI,
                          const nsAString& aNonce,
                          bool aWasRedirected,
                          bool aIsPreload,
                          bool aSpecific,
                          bool aSendViolationReports,
                          bool aSendContentLocationInViolationReports);
 
+    // helper to report inline script/style violations
+    void reportInlineViolation(nsContentPolicyType aContentType,
+                               const nsAString& aNonce,
+                               const nsAString& aContent,
+                               const nsAString& aViolatedDirective,
+                               uint32_t aViolatedPolicyIndex,
+                               uint32_t aLineNumber);
+
     nsCOMPtr<nsIURI>                           mReferrer;
     uint64_t                                   mInnerWindowID; // used for web console logging
     nsTArray<nsCSPPolicy*>                     mPolicies;
     nsCOMPtr<nsIURI>                           mSelfURI;
     nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
     nsCOMPtr<nsILoadGroup>                     mCallingChannelLoadGroup;
     nsWeakPtr                                  mLoadingContext;
 };