From 9847e9f759a62ac9718cd3a24bf8d1a99b523d65 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Fri, 9 May 2025 22:25:06 +0200 Subject: [PATCH] Issue #2736 - Part 13: Override page CSP for loads by expanded principals. Per the CSP specification, content injected by extensions is meant to be exempt from page CSP. This patch takes care of the most common case of content injected by extension content scripts, which always have expanded principals which inherit from the page principal. To make this easier, de-virtualize BasePrincipal::Kind(), using CTOR initializers instead. --- caps/BasePrincipal.cpp | 3 ++- caps/BasePrincipal.h | 28 +++++++++++++++++++--------- caps/nsNullPrincipal.h | 6 +++--- caps/nsPrincipal.cpp | 6 ++++-- caps/nsPrincipal.h | 4 ---- caps/nsSystemPrincipal.h | 6 +++--- dom/security/nsCSPService.cpp | 16 +++++++++++----- 7 files changed, 42 insertions(+), 27 deletions(-) diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index 78b09bb249..c59ca5e37c 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -343,7 +343,8 @@ OriginAttributes::IsFirstPartyEnabled() return sFirstPartyIsolation; } -BasePrincipal::BasePrincipal() +BasePrincipal::BasePrincipal(PrincipalKind aKind) + : mKind(aKind) {} BasePrincipal::~BasePrincipal() diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h index dd60d33746..1fa95b786e 100644 --- a/caps/BasePrincipal.h +++ b/caps/BasePrincipal.h @@ -251,7 +251,14 @@ public: class BasePrincipal : public nsJSPrincipals { public: - BasePrincipal(); + enum PrincipalKind { + eNullPrincipal, + eCodebasePrincipal, + eExpandedPrincipal, + eSystemPrincipal + }; + + explicit BasePrincipal(PrincipalKind aKind); enum DocumentDomainConsideration { DontConsiderDocumentDomain, ConsiderDocumentDomain}; bool Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration); @@ -301,17 +308,19 @@ public: uint32_t PrivateBrowsingId() const { return mOriginAttributes.mPrivateBrowsingId; } bool IsInIsolatedMozBrowserElement() const { return mOriginAttributes.mInIsolatedMozBrowser; } - enum PrincipalKind { - eNullPrincipal, - eCodebasePrincipal, - eExpandedPrincipal, - eSystemPrincipal - }; - - virtual PrincipalKind Kind() = 0; + PrincipalKind Kind() const { return mKind; } already_AddRefed CloneStrippingUserContextIdAndFirstPartyDomain(); + /** + * Returns true if this principal's CSP should override a document's CSP for + * loads that it triggers. Currently true only for expanded principals which + * subsume the document principal. + */ + bool OverridesCSP(nsIPrincipal* aDocumentPrincipal) { + return mKind == eExpandedPrincipal && Subsumes(aDocumentPrincipal, DontConsiderDocumentDomain); + } + protected: virtual ~BasePrincipal(); @@ -333,6 +342,7 @@ protected: nsCOMPtr mCSP; nsCOMPtr mPreloadCSP; PrincipalOriginAttributes mOriginAttributes; + PrincipalKind mKind; }; } // namespace mozilla diff --git a/caps/nsNullPrincipal.h b/caps/nsNullPrincipal.h index b294747730..01800cf4ce 100644 --- a/caps/nsNullPrincipal.h +++ b/caps/nsNullPrincipal.h @@ -36,7 +36,9 @@ public: // This should only be used by deserialization, and the factory constructor. // Other consumers should use the Create and CreateWithInheritedAttributes // methods. - nsNullPrincipal() {} + nsNullPrincipal() + : BasePrincipal(eNullPrincipal) + {} NS_DECL_NSISERIALIZABLE @@ -60,8 +62,6 @@ public: virtual nsresult GetScriptLocation(nsACString &aStr) override; - PrincipalKind Kind() override { return eNullPrincipal; } - protected: virtual ~nsNullPrincipal() {} diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp index 169ee0cb8c..00ac01ecbe 100644 --- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -64,10 +64,11 @@ nsPrincipal::InitializeStatics() } nsPrincipal::nsPrincipal() - : mCodebaseImmutable(false) + : BasePrincipal(eCodebasePrincipal) + , mCodebaseImmutable(false) , mDomainImmutable(false) , mInitialized(false) -{ } +{} nsPrincipal::~nsPrincipal() { @@ -514,6 +515,7 @@ struct OriginComparator nsExpandedPrincipal::nsExpandedPrincipal(nsTArray> &aWhiteList, const PrincipalOriginAttributes& aAttrs) + : BasePrincipal(eExpandedPrincipal) { // We force the principals to be sorted by origin so that nsExpandedPrincipal // origins can have a canonical form. diff --git a/caps/nsPrincipal.h b/caps/nsPrincipal.h index c122952c70..7acbf2520f 100644 --- a/caps/nsPrincipal.h +++ b/caps/nsPrincipal.h @@ -46,8 +46,6 @@ public: */ static void InitializeStatics(); - PrincipalKind Kind() override { return eCodebasePrincipal; } - nsCOMPtr mDomain; nsCOMPtr mCodebase; // If mCodebaseImmutable is true, mCodebase is non-null and immutable @@ -82,8 +80,6 @@ public: virtual nsresult GetScriptLocation(nsACString &aStr) override; nsresult GetOriginInternal(nsACString& aOrigin) override; - PrincipalKind Kind() override { return eExpandedPrincipal; } - protected: virtual ~nsExpandedPrincipal(); diff --git a/caps/nsSystemPrincipal.h b/caps/nsSystemPrincipal.h index 5faec50cfd..35c64d8cbd 100644 --- a/caps/nsSystemPrincipal.h +++ b/caps/nsSystemPrincipal.h @@ -36,7 +36,9 @@ public: NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override; nsresult GetOriginInternal(nsACString& aOrigin) override; - nsSystemPrincipal() {} + nsSystemPrincipal() + : BasePrincipal(eSystemPrincipal) + {} virtual nsresult GetScriptLocation(nsACString &aStr) override; @@ -52,8 +54,6 @@ protected: { return true; } - - PrincipalKind Kind() override { return eSystemPrincipal; } }; #endif // nsSystemPrincipal_h__ diff --git a/dom/security/nsCSPService.cpp b/dom/security/nsCSPService.cpp index ec32889acf..eab7f48478 100644 --- a/dom/security/nsCSPService.cpp +++ b/dom/security/nsCSPService.cpp @@ -140,12 +140,18 @@ CSPService::ShouldLoad(uint32_t aContentType, return NS_OK; } - // query the principal of the document; if no document is passed, then - // fall back to using the requestPrincipal (e.g. service workers do not - // pass a document). + // Find a principal to retrieve the CSP from. If we don't have a context node + // (because, for instance, the load originates in a service worker), or the + // requesting principal's CSP overrides our document CSP, use the request + // principal. Otherwise, use the document principal. nsCOMPtr node(do_QueryInterface(aRequestContext)); - nsCOMPtr principal = node ? node->NodePrincipal() - : aRequestPrincipal; + nsCOMPtr principal; + if (!node || (aRequestPrincipal && + BasePrincipal::Cast(aRequestPrincipal)->OverridesCSP(node->NodePrincipal()))) { + principal = aRequestPrincipal; + } else { + principal = node->NodePrincipal(); + } if (!principal) { // if we can't query a principal, then there is nothing to do. return NS_OK;