Bug 1339004 - Do DocGroup labeling in dom/security. r=smaug, r=ckerschb draft
authorThomas Nguyen <tnguyen@mozilla.com>
Wed, 29 Mar 2017 10:20:32 +0800
changeset 555835 251ffc96be35d9e64f2be68f4af9dbea50c312be
parent 555725 b043233ec04f06768d59dcdfb9e928142280f3cc
child 622721 0ae9c894f045ced60c53fbad8dadfc21f9162fc3
push id52364
push userbmo:tnguyen@mozilla.com
push dateWed, 05 Apr 2017 02:08:45 +0000
reviewerssmaug, ckerschb
bugs1339004
milestone55.0a1
Bug 1339004 - Do DocGroup labeling in dom/security. r=smaug, r=ckerschb MozReview-Commit-ID: 3QoH8P4J85I
dom/base/nsContentPolicy.cpp
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/security/nsCSPContext.cpp
dom/security/nsCSPContext.h
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -20,16 +20,18 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindow.h"
 #include "nsIContent.h"
 #include "nsILoadContext.h"
 #include "nsCOMArray.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
+#include "nsIContentSecurityPolicy.h"
+#include "mozilla/dom/TabGroup.h"
 
 using mozilla::LogLevel;
 
 NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy)
 
 static mozilla::LazyLogModule gConPolLog("nsContentPolicy");
 
 nsresult
@@ -124,16 +126,32 @@ nsContentPolicy::CheckPolicy(CPMethod   
 
     /* 
      * Enumerate mPolicies and ask each of them, taking the logical AND of
      * their permissions.
      */
     nsresult rv;
     nsCOMArray<nsIContentPolicy> entries;
     mPolicies.GetEntries(entries);
+
+    nsCOMPtr<nsPIDOMWindowOuter> window;
+    if (nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext)) {
+        window = node->OwnerDoc()->GetWindow();
+    } else {
+        window = do_QueryInterface(requestingContext);
+    }
+
+    if (requestPrincipal) {
+        nsCOMPtr<nsIContentSecurityPolicy> csp;
+        requestPrincipal->GetCsp(getter_AddRefs(csp));
+        if (csp && window) {
+            csp->EnsureEventTarget(window->EventTargetFor(TaskCategory::Other));
+        }
+    }
+
     int32_t count = entries.Count();
     for (int32_t i = 0; i < count; i++) {
         /* check the appropriate policy */
         // Send internal content policy type to CSP and mixed content blocker
         nsContentPolicyType type = externalType;
         if (mixedContentBlocker == entries[i] || cspService == entries[i]) {
           type = contentType;
         }
@@ -145,22 +163,16 @@ nsContentPolicy::CheckPolicy(CPMethod   
         if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
             /* policy says no, no point continuing to check */
             return NS_OK;
         }
     }
 
     nsCOMPtr<nsIDOMElement> topFrameElement;
     bool isTopLevel = true;
-    nsCOMPtr<nsPIDOMWindowOuter> window;
-    if (nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext)) {
-        window = node->OwnerDoc()->GetWindow();
-    } else {
-        window = do_QueryInterface(requestingContext);
-    }
 
     if (window) {
         nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
         nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
         if (loadContext) {
           loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
         }
 
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -4,16 +4,17 @@
 
 #include "nsISerializable.idl"
 #include "nsIContentPolicy.idl"
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIDocShell;
 interface nsIDOMDocument;
+interface nsIEventTarget;
 interface nsIPrincipal;
 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.
@@ -215,16 +216,20 @@ interface nsIContentSecurityPolicy : nsI
    * Called after the CSP object is created to fill in appropriate request
    * context. Either use
    *  * aDocument (preferred), or if no document is available, then provide
    *  * aPrincipal
    */
   void setRequestContext(in nsIDOMDocument aDocument,
                          in nsIPrincipal aPrincipal);
 
+  /**
+   *  Ensure we have a nsIEventTarget to use to label CSPReportSenderRunnable
+   */
+  [noscript] void ensureEventTarget(in nsIEventTarget aEventTarget);
 
   /*
    * Checks if a CSP requires Subresource Integrity (SRI)
    * for a given nsContentPolicyType.
    */
   bool requireSRIForType(in nsContentPolicyType aContentType);
 
   /**
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -39,16 +39,19 @@
 #include "nsStringStream.h"
 #include "mozilla/Logging.h"
 #include "mozilla/dom/CSPReportBinding.h"
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "nsINetworkInterceptController.h"
 #include "nsSandboxFlags.h"
 #include "nsIScriptElement.h"
+#include "nsIEventTarget.h"
+#include "mozilla/dom/DocGroup.h"
+#include "nsXULAppAPI.h"
 
 using namespace mozilla;
 
 static LogModule*
 GetCspContextLog()
 {
   static LazyLogModule gCspContextPRLog("CSPContext");
   return gCspContextPRLog;
@@ -660,30 +663,45 @@ nsCSPContext::SetRequestContext(nsIDOMDo
     // the innerWindowID is not available for CSPs delivered through the
     // header at the time setReqeustContext is called - let's queue up
     // console messages until it becomes available, see flushConsoleMessages
     mQueueUpMessages = !mInnerWindowID;
     mCallingChannelLoadGroup = doc->GetDocumentLoadGroup();
 
     // set the flag on the document for CSP telemetry
     doc->SetHasCSP(true);
+    mEventTarget = doc->EventTargetFor(TaskCategory::Other);
   }
   else {
     CSPCONTEXTLOG(("No Document in SetRequestContext; can not query loadgroup; sending reports may fail."));
     mLoadingPrincipal = aPrincipal;
     mLoadingPrincipal->GetURI(getter_AddRefs(mSelfURI));
     // if no document is available, then it also does not make sense to queue console messages
     // sending messages to the browser conolse instead of the web console in that case.
     mQueueUpMessages = false;
   }
 
   NS_ASSERTION(mSelfURI, "mSelfURI not available, can not translate 'self' into actual URI");
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCSPContext::EnsureEventTarget(nsIEventTarget* aEventTarget)
+{
+  NS_ENSURE_ARG(aEventTarget);
+  // Don't bother if we did have a valid event target (if the csp object is
+  // tied to a document in SetRequestContext)
+  if (mEventTarget) {
+    return NS_OK;
+  }
+
+  mEventTarget = aEventTarget;
+  return NS_OK;
+}
+
 struct ConsoleMsgQueueElem {
   nsXPIDLString mMsg;
   nsString      mSourceName;
   nsString      mSourceLine;
   uint32_t      mLineNumber;
   uint32_t      mColumnNumber;
   uint32_t      mSeverityFlag;
 };
@@ -1159,27 +1177,40 @@ nsCSPContext::AsyncReportViolation(nsISu
                                    uint32_t aViolatedPolicyIndex,
                                    const nsAString& aObserverSubject,
                                    const nsAString& aSourceFile,
                                    const nsAString& aScriptSample,
                                    uint32_t aLineNum)
 {
   NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
 
-  NS_DispatchToMainThread(new CSPReportSenderRunnable(aBlockedContentSource,
-                                                      aOriginalURI,
-                                                      aViolatedPolicyIndex,
-                                                      mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(),
-                                                      aViolatedDirective,
-                                                      aObserverSubject,
-                                                      aSourceFile,
-                                                      aScriptSample,
-                                                      aLineNum,
-                                                      this));
-   return NS_OK;
+  nsCOMPtr<nsIRunnable> task =
+    new CSPReportSenderRunnable(aBlockedContentSource,
+                                aOriginalURI,
+                                aViolatedPolicyIndex,
+                                mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(),
+                                aViolatedDirective,
+                                aObserverSubject,
+                                aSourceFile,
+                                aScriptSample,
+                                aLineNum,
+                                this);
+
+  if (XRE_IsContentProcess()) {
+    if (mEventTarget) {
+      if (nsCOMPtr<nsINamed> named = do_QueryInterface(task)) {
+        named->SetName("CSPReportSenderRunnable");
+      }
+      mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+      return NS_OK;
+    }
+  }
+
+  NS_DispatchToMainThread(task.forget());
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::RequireSRIForType(nsContentPolicyType aContentType, bool* outRequiresSRIForType)
 {
   *outRequiresSRIForType = false;
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (mPolicies[i]->hasDirective(REQUIRE_SRI_FOR)) {
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -21,16 +21,17 @@
 
 #define NS_CSPCONTEXT_CONTRACTID "@mozilla.org/cspcontext;1"
  // 09d9ed1a-e5d4-4004-bfe0-27ceb923d9ac
 #define NS_CSPCONTEXT_CID \
 { 0x09d9ed1a, 0xe5d4, 0x4004, \
   { 0xbf, 0xe0, 0x27, 0xce, 0xb9, 0x23, 0xd9, 0xac } }
 
 class nsINetworkInterceptController;
+class nsIEventTarget;
 struct ConsoleMsgQueueElem;
 
 class nsCSPContext : public nsIContentSecurityPolicy
 {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSICONTENTSECURITYPOLICY
     NS_DECL_NSISERIALIZABLE
@@ -116,16 +117,17 @@ class nsCSPContext : public nsIContentSe
     // to avoid memory leaks. Within the destructor of the principal we explicitly
     // set mLoadingPrincipal to null.
     nsIPrincipal*                              mLoadingPrincipal;
 
     // helper members used to queue up web console messages till
     // the windowID becomes available. see flushConsoleMessages()
     nsTArray<ConsoleMsgQueueElem>              mConsoleMsgQueue;
     bool                                       mQueueUpMessages;
+    nsCOMPtr<nsIEventTarget>                   mEventTarget;
 };
 
 // Class that listens to violation report transmission and logs errors.
 class CSPViolationReportListener : public nsIStreamListener
 {
   public:
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIREQUESTOBSERVER
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1149,22 +1149,27 @@ private:
       // However, we must still override the principal since the nsIPrincipal
       // URL may be different due to same-origin redirects.  Unfortunately this
       // URL must exactly match the final worker script URL in order to
       // properly set the referrer header on fetch/xhr requests.  If bug 1340694
       // is ever fixed this can be removed.
       rv = mWorkerPrivate->SetPrincipalFromChannel(channel);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      nsCOMPtr<nsIContentSecurityPolicy> csp = mWorkerPrivate->GetCSP();
       // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
       // should get it from the HTTP headers on the worker script.
-      if (!mWorkerPrivate->GetCSP() && CSPService::sCSPEnabled) {
-        rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
-                                                    tCspROHeaderValue);
-        NS_ENSURE_SUCCESS(rv, rv);
+      if (CSPService::sCSPEnabled) {
+        if (!csp) {
+          rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
+                                                      tCspROHeaderValue);
+          NS_ENSURE_SUCCESS(rv, rv);
+        } else {
+          csp->EnsureEventTarget(mWorkerPrivate->MainThreadEventTarget());
+        }
       }
 
       mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue);
 
       WorkerPrivate* parent = mWorkerPrivate->GetParent();
       if (parent) {
         // XHR Params Allowed
         mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2636,32 +2636,48 @@ WorkerPrivateParent<Derived>::GetDocumen
     }
     parent = parent->GetParent();
   }
   // couldn't query a document, give up and return nullptr
   return nullptr;
 }
 
 template <class Derived>
+void
+WorkerPrivateParent<Derived>::SetCSP(nsIContentSecurityPolicy* aCSP)
+{
+  AssertIsOnMainThread();
+  if (!aCSP) {
+    return;
+  }
+  WorkerPrivate* self = ParentAsWorkerPrivate();
+  aCSP->EnsureEventTarget(self->mMainThreadEventTarget);
+  mLoadInfo.mCSP = aCSP;
+}
+
+template <class Derived>
 nsresult
 WorkerPrivateParent<Derived>::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
                                                      const nsACString& aCSPReportOnlyHeaderValue)
 {
   AssertIsOnMainThread();
   MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
 
   NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
   if (!csp) {
     return NS_OK;
   }
 
+  WorkerPrivate* self = ParentAsWorkerPrivate();
+  csp->EnsureEventTarget(self->mMainThreadEventTarget);
+
   // If there's a CSP header, apply it.
   if (!cspHeaderValue.IsEmpty()) {
     rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   // If there's a report-only CSP header, apply it.
   if (!cspROHeaderValue.IsEmpty()) {
     rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -698,21 +698,17 @@ public:
   nsIContentSecurityPolicy*
   GetCSP() const
   {
     AssertIsOnMainThread();
     return mLoadInfo.mCSP;
   }
 
   void
-  SetCSP(nsIContentSecurityPolicy* aCSP)
-  {
-    AssertIsOnMainThread();
-    mLoadInfo.mCSP = aCSP;
-  }
+  SetCSP(nsIContentSecurityPolicy* aCSP);
 
   nsresult
   SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
                          const nsACString& aCSPReportOnlyHeaderValue);
 
   void
   SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue);