Bug 1441336 - Use addon permissions for PerformanceTiming properties r=bz,kmag
authorTomislav Jovanovic <tomica@gmail.com>
Wed, 04 Apr 2018 16:54:26 +0200
changeset 468966 c294177f4ec91625ef3613c13aefe0fadbae1cb8
parent 468965 27e293c662c840e6aa2bcd81d3ce4f301293f425
child 468967 0f1e4fe1eb4c9dcae751b086c0f933af63fbac33
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, kmag
bugs1441336
milestone61.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 1441336 - Use addon permissions for PerformanceTiming properties r=bz,kmag We need to side-step existing cross-origin checks in Performance Timing code when the caller is a web extension content script that otherwise has permission to access the cross-origin resource. MozReview-Commit-ID: 8IgtqZgPWgY
caps/BasePrincipal.cpp
caps/ExpandedPrincipal.cpp
caps/ExpandedPrincipal.h
dom/performance/PerformanceResourceTiming.cpp
dom/performance/PerformanceResourceTiming.h
dom/performance/PerformanceTiming.h
dom/webidl/PerformanceResourceTiming.webidl
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -459,16 +459,19 @@ BasePrincipal::CloneStrippingUserContext
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 }
 
 bool
 BasePrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */)
 {
+  if (Is<ExpandedPrincipal>()) {
+    return As<ExpandedPrincipal>()->AddonAllowsLoad(aURI, aExplicit);
+  }
   if (auto policy = AddonPolicy()) {
     return policy->CanAccessURI(aURI, aExplicit);
   }
   return false;
 }
 
 void
 BasePrincipal::FinishInit(const nsACString& aOriginNoSuffix,
--- a/caps/ExpandedPrincipal.cpp
+++ b/caps/ExpandedPrincipal.cpp
@@ -175,16 +175,27 @@ ExpandedPrincipal::AddonHasPermission(co
   for (size_t i = 0; i < mPrincipals.Length(); ++i) {
     if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
       return true;
     }
   }
   return false;
 }
 
+bool
+ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */)
+{
+  for (const auto& principal : mPrincipals) {
+    if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 nsIPrincipal*
 ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI)
 {
   if (aRequestedURI) {
     // If a given sub-principal subsumes the given URI, use that principal for
     // inheritance. In general, this only happens with certain CORS modes, loads
     // with forced principal inheritance, and creation of XML documents from
     // XMLHttpRequests or fetch requests. For URIs that normally inherit a
--- a/caps/ExpandedPrincipal.h
+++ b/caps/ExpandedPrincipal.h
@@ -32,16 +32,18 @@ public:
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   virtual bool AddonHasPermission(const nsAtom* aPerm) override;
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
 
+  bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
+
   // Returns the principal to inherit when this principal requests the given
   // URL. See BasePrincipal::PrincipalToInherit.
   nsIPrincipal* PrincipalToInherit(nsIURI* aRequestedURI = nullptr);
 
 protected:
   explicit ExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList);
 
   virtual ~ExpandedPrincipal();
--- a/dom/performance/PerformanceResourceTiming.cpp
+++ b/dom/performance/PerformanceResourceTiming.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PerformanceResourceTiming.h"
 #include "mozilla/dom/PerformanceResourceTimingBinding.h"
+#include "nsNetUtil.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming,
                                    PerformanceEntry,
                                    mPerformance)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceResourceTiming,
@@ -26,16 +27,21 @@ NS_IMPL_RELEASE_INHERITED(PerformanceRes
 PerformanceResourceTiming::PerformanceResourceTiming(UniquePtr<PerformanceTimingData>&& aPerformanceTiming,
                                                      Performance* aPerformance,
                                                      const nsAString& aName)
   : PerformanceEntry(aPerformance->GetParentObject(), aName, NS_LITERAL_STRING("resource"))
   , mTimingData(Move(aPerformanceTiming))
   , mPerformance(aPerformance)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
+  if (NS_IsMainThread()) {
+    // Used to check if an addon content script has access to this timing.
+    // We don't need it in workers, and ignore mOriginalURI if null.
+    NS_NewURI(getter_AddRefs(mOriginalURI), aName);
+  }
 }
 
 PerformanceResourceTiming::~PerformanceResourceTiming()
 {
 }
 
 DOMHighResTimeStamp
 PerformanceResourceTiming::StartTime() const
@@ -75,8 +81,40 @@ size_t
 PerformanceResourceTiming::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return PerformanceEntry::SizeOfExcludingThis(aMallocSizeOf) +
          mInitiatorType.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
          (mTimingData
             ? mTimingData->NextHopProtocol().SizeOfExcludingThisIfUnshared(aMallocSizeOf)
             : 0);
 }
+
+bool
+PerformanceResourceTiming::TimingAllowedForCaller(Maybe<nsIPrincipal*>& aCaller) const
+{
+  if (!mTimingData) {
+    return false;
+  }
+
+  if (mTimingData->TimingAllowed()) {
+    return true;
+  }
+
+  // Check if the addon has permission to access the cross-origin resource.
+  return mOriginalURI && aCaller.isSome() &&
+      BasePrincipal::Cast(aCaller.value())->AddonAllowsLoad(mOriginalURI);
+}
+
+bool
+PerformanceResourceTiming::ReportRedirectForCaller(Maybe<nsIPrincipal*>& aCaller) const
+{
+  if (!mTimingData) {
+    return false;
+  }
+
+  if (mTimingData->ShouldReportCrossOriginRedirect()) {
+    return true;
+  }
+
+  // Only report cross-origin redirect if the addon has <all_urls> permission.
+  return aCaller.isSome() &&
+      BasePrincipal::Cast(aCaller.value())->AddonHasPermission(nsGkAtoms::all_urlsPermission);
+}
--- a/dom/performance/PerformanceResourceTiming.h
+++ b/dom/performance/PerformanceResourceTiming.h
@@ -65,111 +65,129 @@ public:
   }
 
   DOMHighResTimeStamp FetchStart() const {
     return mTimingData
         ? mTimingData->FetchStartHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp RedirectStart() const {
+  DOMHighResTimeStamp RedirectStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
     // We have to check if all the redirect URIs had the same origin (since
-    // there is no check in RedirectEndHighRes())
-    return mTimingData && mTimingData->ShouldReportCrossOriginRedirect()
+    // there is no check in RedirectStartHighRes())
+    return ReportRedirectForCaller(aSubjectPrincipal)
         ? mTimingData->RedirectStartHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp RedirectEnd() const {
+  DOMHighResTimeStamp RedirectEnd(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
     // We have to check if all the redirect URIs had the same origin (since
     // there is no check in RedirectEndHighRes())
-    return mTimingData && mTimingData->ShouldReportCrossOriginRedirect()
+    return ReportRedirectForCaller(aSubjectPrincipal)
         ? mTimingData->RedirectEndHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp DomainLookupStart() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp DomainLookupStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->DomainLookupStartHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp DomainLookupEnd() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp DomainLookupEnd(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->DomainLookupEndHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp ConnectStart() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp ConnectStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->ConnectStartHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp ConnectEnd() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp ConnectEnd(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->ConnectEndHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp RequestStart() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp RequestStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->RequestStartHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp ResponseStart() const {
-    return mTimingData && mTimingData->TimingAllowed()
+  DOMHighResTimeStamp ResponseStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const {
+    return TimingAllowedForCaller(aSubjectPrincipal)
         ? mTimingData->ResponseStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp ResponseEnd() const {
     return mTimingData
         ? mTimingData->ResponseEndHighRes(mPerformance)
         : 0;
   }
 
-  DOMHighResTimeStamp SecureConnectionStart() const
+  DOMHighResTimeStamp SecureConnectionStart(Maybe<nsIPrincipal*>& aSubjectPrincipal) const
   {
-    return mTimingData && mTimingData->TimingAllowed()
-        ?  mTimingData->SecureConnectionStartHighRes(mPerformance)
+    return TimingAllowedForCaller(aSubjectPrincipal)
+        ? mTimingData->SecureConnectionStartHighRes(mPerformance)
         : 0;
   }
 
   virtual const PerformanceResourceTiming* ToResourceTiming() const override
   {
     return this;
   }
 
-  uint64_t TransferSize() const
+  uint64_t TransferSize(Maybe<nsIPrincipal*>& aSubjectPrincipal) const
   {
-    return mTimingData ? mTimingData->TransferSize() : 0;
+    return TimingAllowedForCaller(aSubjectPrincipal)
+        ? mTimingData->TransferSize()
+        : 0;
   }
 
-  uint64_t EncodedBodySize() const
+  uint64_t EncodedBodySize(Maybe<nsIPrincipal*>& aSubjectPrincipal) const
   {
-    return mTimingData ? mTimingData->EncodedBodySize() : 0;
+    return TimingAllowedForCaller(aSubjectPrincipal)
+        ? mTimingData->EncodedBodySize()
+        : 0;
   }
 
-  uint64_t DecodedBodySize() const
+  uint64_t DecodedBodySize(Maybe<nsIPrincipal*>& aSubjectPrincipal) const
   {
-    return mTimingData ? mTimingData->DecodedBodySize() : 0;
+    return TimingAllowedForCaller(aSubjectPrincipal)
+        ? mTimingData->DecodedBodySize()
+        : 0;
   }
 
   size_t
   SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
 protected:
   virtual ~PerformanceResourceTiming();
 
   size_t
   SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
+  // Check if caller has access to cross-origin timings, either by the rules
+  // from the spec, or based on addon permissions.
+  bool
+  TimingAllowedForCaller(Maybe<nsIPrincipal*>& aCaller) const;
+
+  // Check if cross-origin redirects should be reported to the caller.
+  bool
+  ReportRedirectForCaller(Maybe<nsIPrincipal*>& aCaller) const;
+
   nsString mInitiatorType;
   UniquePtr<PerformanceTimingData> mTimingData;
   RefPtr<Performance> mPerformance;
+
+  // The same initial requested URI as the `name` attribute.
+  nsCOMPtr<nsIURI> mOriginalURI;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_PerformanceResourceTiming_h___ */
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -49,27 +49,27 @@ public:
 
   const nsString& NextHopProtocol() const
   {
     return mNextHopProtocol;
   }
 
   uint64_t TransferSize() const
   {
-    return mTimingAllowed ? mTransferSize : 0;
+    return mTransferSize;
   }
 
   uint64_t EncodedBodySize() const
   {
-    return mTimingAllowed ? mEncodedBodySize : 0;
+    return mEncodedBodySize;
   }
 
   uint64_t DecodedBodySize() const
   {
-    return mTimingAllowed ? mDecodedBodySize : 0;
+    return mDecodedBodySize;
   }
 
   /**
    * @param   aStamp
    *          The TimeStamp recorded for a specific event. This TimeStamp can
    *          be null.
    * @return  the duration of an event with a given TimeStamp, relative to the
    *          navigationStart TimeStamp (the moment the user landed on the
--- a/dom/webidl/PerformanceResourceTiming.webidl
+++ b/dom/webidl/PerformanceResourceTiming.webidl
@@ -12,26 +12,42 @@
 
 [Exposed=(Window,Worker)]
 interface PerformanceResourceTiming : PerformanceEntry
 {
   readonly attribute DOMString initiatorType;
   readonly attribute DOMString nextHopProtocol;
 
   readonly attribute DOMHighResTimeStamp workerStart;
+
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp redirectStart;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp redirectEnd;
+
   readonly attribute DOMHighResTimeStamp fetchStart;
+
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp domainLookupStart;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp domainLookupEnd;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp connectStart;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp connectEnd;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp secureConnectionStart;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp requestStart;
+  [NeedsSubjectPrincipal]
   readonly attribute DOMHighResTimeStamp responseStart;
+
   readonly attribute DOMHighResTimeStamp responseEnd;
 
+  [NeedsSubjectPrincipal]
   readonly attribute unsigned long long transferSize;
+  [NeedsSubjectPrincipal]
   readonly attribute unsigned long long encodedBodySize;
+  [NeedsSubjectPrincipal]
   readonly attribute unsigned long long decodedBodySize;
 
   jsonifier;
 };