Bug 1441336 - Use addon permissions for PerformanceTiming properties
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
--- 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;
};