Bug 1890277: part 1) Add CSP parser tests for `require-trusted-types-for`. r=tschuster
[gecko.git] / caps / ExpandedPrincipal.cpp
blob7d65fec61472da613a742e9e1e3b2c8da47570ea
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ExpandedPrincipal.h"
8 #include "nsIClassInfoImpl.h"
9 #include "nsIObjectInputStream.h"
10 #include "nsReadableUtils.h"
11 #include "mozilla/Base64.h"
12 #include "mozilla/extensions/WebExtensionPolicy.h"
13 #include "mozilla/JSONWriter.h"
15 #include "js/JSON.h"
16 #include "ExpandedPrincipalJSONHandler.h"
17 #include "SubsumedPrincipalJSONHandler.h"
19 using namespace mozilla;
21 NS_IMPL_CLASSINFO(ExpandedPrincipal, nullptr, 0, NS_EXPANDEDPRINCIPAL_CID)
22 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal, nsIPrincipal,
23 nsIExpandedPrincipal)
24 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal, nsIPrincipal,
25 nsIExpandedPrincipal)
27 ExpandedPrincipal::ExpandedPrincipal(
28 nsTArray<nsCOMPtr<nsIPrincipal>>&& aPrincipals,
29 const nsACString& aOriginNoSuffix, const OriginAttributes& aAttrs)
30 : BasePrincipal(eExpandedPrincipal, aOriginNoSuffix, aAttrs),
31 mPrincipals(std::move(aPrincipals)) {}
33 ExpandedPrincipal::~ExpandedPrincipal() = default;
35 already_AddRefed<ExpandedPrincipal> ExpandedPrincipal::Create(
36 const nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList,
37 const OriginAttributes& aAttrs) {
38 nsTArray<nsCOMPtr<nsIPrincipal>> principals;
39 for (size_t i = 0; i < aAllowList.Length(); ++i) {
40 principals.AppendElement(aAllowList[i]);
43 nsAutoCString origin;
44 origin.AssignLiteral("[Expanded Principal [");
45 StringJoinAppend(
46 origin, ", "_ns, principals,
47 [](nsACString& dest, const nsCOMPtr<nsIPrincipal>& principal) {
48 nsAutoCString subOrigin;
49 DebugOnly<nsresult> rv = principal->GetOrigin(subOrigin);
50 MOZ_ASSERT(NS_SUCCEEDED(rv));
51 dest.Append(subOrigin);
52 });
53 origin.AppendLiteral("]]");
55 RefPtr<ExpandedPrincipal> ep =
56 new ExpandedPrincipal(std::move(principals), origin, aAttrs);
57 return ep.forget();
60 NS_IMETHODIMP
61 ExpandedPrincipal::GetDomain(nsIURI** aDomain) {
62 *aDomain = nullptr;
63 return NS_OK;
66 NS_IMETHODIMP
67 ExpandedPrincipal::SetDomain(nsIURI* aDomain) { return NS_OK; }
69 bool ExpandedPrincipal::SubsumesInternal(
70 nsIPrincipal* aOther,
71 BasePrincipal::DocumentDomainConsideration aConsideration) {
72 // If aOther is an ExpandedPrincipal too, we break it down into its component
73 // nsIPrincipals, and check subsumes on each one.
74 if (Cast(aOther)->Is<ExpandedPrincipal>()) {
75 auto* expanded = Cast(aOther)->As<ExpandedPrincipal>();
77 for (auto& other : expanded->AllowList()) {
78 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
79 // checks are only done between non-expanded sub-principals, and we don't
80 // need to incur the extra virtual call overhead.
81 if (!SubsumesInternal(other, aConsideration)) {
82 return false;
85 return true;
88 // We're dealing with a regular principal. One of our principals must subsume
89 // it.
90 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
91 if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
92 return true;
96 return false;
99 bool ExpandedPrincipal::MayLoadInternal(nsIURI* uri) {
100 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
101 if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
102 return true;
106 return false;
109 uint32_t ExpandedPrincipal::GetHashValue() {
110 MOZ_CRASH("extended principal should never be used as key in a hash map");
113 NS_IMETHODIMP
114 ExpandedPrincipal::GetURI(nsIURI** aURI) {
115 *aURI = nullptr;
116 return NS_OK;
119 const nsTArray<nsCOMPtr<nsIPrincipal>>& ExpandedPrincipal::AllowList() {
120 return mPrincipals;
123 NS_IMETHODIMP
124 ExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
125 return NS_ERROR_NOT_AVAILABLE;
128 NS_IMETHODIMP
129 ExpandedPrincipal::GetAddonId(nsAString& aAddonId) {
130 aAddonId.Truncate();
131 return NS_OK;
134 bool ExpandedPrincipal::AddonHasPermission(const nsAtom* aPerm) {
135 for (size_t i = 0; i < mPrincipals.Length(); ++i) {
136 if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
137 return true;
140 return false;
143 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI,
144 bool aExplicit /* = false */) {
145 for (const auto& principal : mPrincipals) {
146 if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) {
147 return true;
150 return false;
153 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy* aCSP) {
154 AssertIsOnMainThread();
155 mCSP = new nsMainThreadPtrHolder<nsIContentSecurityPolicy>(
156 "ExpandedPrincipal::mCSP", aCSP);
159 NS_IMETHODIMP
160 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
161 AssertIsOnMainThread();
162 NS_IF_ADDREF(*aCsp = mCSP);
163 return NS_OK;
166 nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) {
167 if (aRequestedURI) {
168 // If a given sub-principal subsumes the given URI, use that principal for
169 // inheritance. In general, this only happens with certain CORS modes, loads
170 // with forced principal inheritance, and creation of XML documents from
171 // XMLHttpRequests or fetch requests. For URIs that normally inherit a
172 // principal (such as data: URIs), we fall back to the last principal in the
173 // allowlist.
174 for (const auto& principal : mPrincipals) {
175 if (Cast(principal)->MayLoadInternal(aRequestedURI)) {
176 return principal;
180 return mPrincipals.LastElement();
183 nsresult ExpandedPrincipal::GetScriptLocation(nsACString& aStr) {
184 aStr.AssignLiteral("[Expanded Principal [");
185 for (size_t i = 0; i < mPrincipals.Length(); ++i) {
186 if (i != 0) {
187 aStr.AppendLiteral(", ");
190 nsAutoCString spec;
191 nsresult rv =
192 nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
193 NS_ENSURE_SUCCESS(rv, rv);
195 aStr.Append(spec);
197 aStr.AppendLiteral("]]");
198 return NS_OK;
201 //////////////////////////////////////////
202 // Methods implementing nsISerializable //
203 //////////////////////////////////////////
205 // We've had way too many issues with unversioned serializations, so
206 // explicitly version this one.
207 static const uint32_t kSerializationVersion = 1;
209 NS_IMETHODIMP
210 ExpandedPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) {
211 uint32_t version;
212 nsresult rv = aStream->Read32(&version);
213 if (version != kSerializationVersion) {
214 MOZ_ASSERT(false,
215 "We really need to add handling of the old(?) version here");
216 return NS_ERROR_UNEXPECTED;
219 uint32_t count;
220 rv = aStream->Read32(&count);
221 if (NS_FAILED(rv)) {
222 return rv;
225 nsTArray<nsCOMPtr<nsIPrincipal>> principals;
226 if (!principals.SetCapacity(count, fallible)) {
227 return NS_ERROR_OUT_OF_MEMORY;
230 for (uint32_t i = 0; i < count; ++i) {
231 nsCOMPtr<nsISupports> read;
232 rv = aStream->ReadObject(true, getter_AddRefs(read));
233 if (NS_FAILED(rv)) {
234 return rv;
237 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(read);
238 if (!principal) {
239 return NS_ERROR_UNEXPECTED;
242 principals.AppendElement(std::move(principal));
245 mPrincipal = ExpandedPrincipal::Create(principals, OriginAttributes());
246 return NS_OK;
249 nsresult ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
250 // Call GetSiteIdentifier on each of our principals and return a new
251 // ExpandedPrincipal.
253 nsTArray<nsCOMPtr<nsIPrincipal>> allowlist;
254 for (const auto& principal : mPrincipals) {
255 SiteIdentifier site;
256 nsresult rv = Cast(principal)->GetSiteIdentifier(site);
257 NS_ENSURE_SUCCESS(rv, rv);
258 allowlist.AppendElement(site.GetPrincipal());
261 RefPtr<ExpandedPrincipal> expandedPrincipal =
262 ExpandedPrincipal::Create(allowlist, OriginAttributesRef());
263 MOZ_ASSERT(expandedPrincipal, "ExpandedPrincipal::Create returned nullptr?");
265 aSite.Init(expandedPrincipal);
266 return NS_OK;
269 nsresult ExpandedPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
270 aWriter.StartArrayProperty(JSONEnumKeyString<eSpecs>(),
271 JSONWriter::CollectionStyle::SingleLineStyle);
273 for (const auto& principal : mPrincipals) {
274 aWriter.StartObjectElement(JSONWriter::CollectionStyle::SingleLineStyle);
276 nsresult rv = BasePrincipal::Cast(principal)->WriteJSONProperties(aWriter);
277 NS_ENSURE_SUCCESS(rv, rv);
279 aWriter.EndObject();
282 aWriter.EndArray();
284 nsAutoCString suffix;
285 OriginAttributesRef().CreateSuffix(suffix);
286 if (suffix.Length() > 0) {
287 WriteJSONProperty<eSuffix>(aWriter, suffix);
290 return NS_OK;
293 bool ExpandedPrincipalJSONHandler::ProcessSubsumedResult(bool aResult) {
294 if (!aResult) {
295 NS_WARNING("Failed to parse subsumed principal");
296 mState = State::Error;
297 return false;
299 return true;
302 bool ExpandedPrincipalJSONHandler::startObject() {
303 if (mSubsumedHandler.isSome()) {
304 return ProcessSubsumedResult(mSubsumedHandler->startObject());
307 switch (mState) {
308 case State::Init:
309 mState = State::StartObject;
310 break;
311 case State::StartArray:
312 mState = State::SubsumedPrincipal;
313 [[fallthrough]];
314 case State::SubsumedPrincipal:
315 mSubsumedHandler.emplace();
317 return ProcessSubsumedResult(mSubsumedHandler->startObject());
318 default:
319 NS_WARNING("Unexpected object value");
320 mState = State::Error;
321 return false;
324 return true;
327 bool ExpandedPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
328 size_t length) {
329 if (mSubsumedHandler.isSome()) {
330 return ProcessSubsumedResult(mSubsumedHandler->propertyName(name, length));
333 switch (mState) {
334 case State::StartObject:
335 case State::AfterPropertyValue: {
336 if (length != 1) {
337 NS_WARNING(
338 nsPrintfCString("Unexpected property name length: %zu", length)
339 .get());
340 mState = State::Error;
341 return false;
344 char key = char(name[0]);
345 switch (key) {
346 case ExpandedPrincipal::SpecsKey:
347 mState = State::SpecsKey;
348 break;
349 case ExpandedPrincipal::SuffixKey:
350 mState = State::SuffixKey;
351 break;
352 default:
353 NS_WARNING(
354 nsPrintfCString("Unexpected property name: '%c'", key).get());
355 mState = State::Error;
356 return false;
358 break;
360 default:
361 NS_WARNING("Unexpected property name");
362 mState = State::Error;
363 return false;
366 return true;
369 bool ExpandedPrincipalJSONHandler::endObject() {
370 if (mSubsumedHandler.isSome()) {
371 if (!ProcessSubsumedResult(mSubsumedHandler->endObject())) {
372 return false;
374 if (mSubsumedHandler->HasAccepted()) {
375 nsCOMPtr<nsIPrincipal> principal = mSubsumedHandler->mPrincipal.forget();
376 mSubsumedHandler.reset();
377 mAllowList.AppendElement(principal);
379 return true;
382 switch (mState) {
383 case State::AfterPropertyValue:
384 mPrincipal = ExpandedPrincipal::Create(mAllowList, mAttrs);
385 MOZ_ASSERT(mPrincipal);
387 mState = State::EndObject;
388 break;
389 default:
390 NS_WARNING("Unexpected end of object");
391 mState = State::Error;
392 return false;
395 return true;
398 bool ExpandedPrincipalJSONHandler::startArray() {
399 switch (mState) {
400 case State::SpecsKey:
401 mState = State::StartArray;
402 break;
403 default:
404 NS_WARNING("Unexpected array value");
405 mState = State::Error;
406 return false;
409 return true;
412 bool ExpandedPrincipalJSONHandler::endArray() {
413 switch (mState) {
414 case State::SubsumedPrincipal: {
415 mState = State::AfterPropertyValue;
416 break;
418 default:
419 NS_WARNING("Unexpected end of array");
420 mState = State::Error;
421 return false;
424 return true;
427 bool ExpandedPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
428 size_t length) {
429 if (mSubsumedHandler.isSome()) {
430 return ProcessSubsumedResult(mSubsumedHandler->stringValue(str, length));
433 switch (mState) {
434 case State::SpecsKey: {
435 nsDependentCSubstring specs(reinterpret_cast<const char*>(str), length);
437 for (const nsACString& each : specs.Split(',')) {
438 nsAutoCString result;
439 nsresult rv = Base64Decode(each, result);
440 MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode");
441 if (NS_FAILED(rv)) {
442 mState = State::Error;
443 return false;
446 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result);
447 if (!principal) {
448 mState = State::Error;
449 return false;
451 mAllowList.AppendElement(principal);
454 mState = State::AfterPropertyValue;
455 break;
457 case State::SuffixKey: {
458 nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
459 if (!mAttrs.PopulateFromSuffix(attrs)) {
460 mState = State::Error;
461 return false;
464 mState = State::AfterPropertyValue;
465 break;
467 default:
468 NS_WARNING("Unexpected string value");
469 mState = State::Error;
470 return false;
473 return true;
476 NS_IMETHODIMP
477 ExpandedPrincipal::IsThirdPartyURI(nsIURI* aURI, bool* aRes) {
478 // ExpandedPrincipal for extension content scripts consist of two principals,
479 // the document's principal and the extension's principal.
480 // To make sure that the third-party check behaves like the web page on which
481 // the content script is running, ignore the extension's principal.
483 for (const auto& principal : mPrincipals) {
484 if (!Cast(principal)->AddonPolicyCore()) {
485 return Cast(principal)->IsThirdPartyURI(aURI, aRes);
489 if (mPrincipals.IsEmpty()) {
490 *aRes = true;
491 return NS_OK;
494 return Cast(mPrincipals[0])->IsThirdPartyURI(aURI, aRes);