Bug 1807268 - Re-enable verifyShowClipboardSuggestionsToggleTest UI test r=jajohnson
[gecko.git] / netwerk / cookie / CookieCommons.cpp
blob9b26fc4a6e6ecdd6dd6576a6fd9378fe2da3b461
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "Cookie.h"
7 #include "CookieCommons.h"
8 #include "CookieLogging.h"
9 #include "CookieService.h"
10 #include "mozilla/ConsoleReportCollector.h"
11 #include "mozilla/ContentBlockingNotifier.h"
12 #include "mozilla/ScopeExit.h"
13 #include "mozilla/StaticPrefs_network.h"
14 #include "mozilla/StorageAccess.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/nsMixedContentBlocker.h"
17 #include "mozilla/net/CookieJarSettings.h"
18 #include "mozIThirdPartyUtil.h"
19 #include "nsContentUtils.h"
20 #include "nsICookiePermission.h"
21 #include "nsICookieService.h"
22 #include "nsIEffectiveTLDService.h"
23 #include "nsIRedirectHistoryEntry.h"
24 #include "nsIWebProgressListener.h"
25 #include "nsNetUtil.h"
26 #include "nsScriptSecurityManager.h"
27 #include "ThirdPartyUtil.h"
29 namespace mozilla {
31 using dom::Document;
33 namespace net {
35 // static
36 bool CookieCommons::DomainMatches(Cookie* aCookie, const nsACString& aHost) {
37 // first, check for an exact host or domain cookie match, e.g. "google.com"
38 // or ".google.com"; second a subdomain match, e.g.
39 // host = "mail.google.com", cookie domain = ".google.com".
40 return aCookie->RawHost() == aHost ||
41 (aCookie->IsDomain() && StringEndsWith(aHost, aCookie->Host()));
44 // static
45 bool CookieCommons::PathMatches(Cookie* aCookie, const nsACString& aPath) {
46 nsCString cookiePath(aCookie->GetFilePath());
48 // if our cookie path is empty we can't really perform our prefix check, and
49 // also we can't check the last character of the cookie path, so we would
50 // never return a successful match.
51 if (cookiePath.IsEmpty()) {
52 return false;
55 // if the cookie path and the request path are identical, they match.
56 if (cookiePath.Equals(aPath)) {
57 return true;
60 // if the cookie path is a prefix of the request path, and the last character
61 // of the cookie path is %x2F ("/"), they match.
62 bool isPrefix = StringBeginsWith(aPath, cookiePath);
63 if (isPrefix && cookiePath.Last() == '/') {
64 return true;
67 // if the cookie path is a prefix of the request path, and the first character
68 // of the request path that is not included in the cookie path is a %x2F ("/")
69 // character, they match.
70 uint32_t cookiePathLen = cookiePath.Length();
71 return isPrefix && aPath[cookiePathLen] == '/';
74 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
75 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
76 // dot may be present. If aHostURI is an IP address, an alias such as
77 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
78 // be the exact host, and aRequireHostMatch will be true to indicate that
79 // substring matches should not be performed.
80 nsresult CookieCommons::GetBaseDomain(nsIEffectiveTLDService* aTLDService,
81 nsIURI* aHostURI, nsACString& aBaseDomain,
82 bool& aRequireHostMatch) {
83 // get the base domain. this will fail if the host contains a leading dot,
84 // more than one trailing dot, or is otherwise malformed.
85 nsresult rv = aTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
86 aRequireHostMatch = rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
87 rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
88 if (aRequireHostMatch) {
89 // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
90 // such as 'co.uk', or the empty string. use the host as a key in such
91 // cases.
92 rv = nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI, aBaseDomain);
94 NS_ENSURE_SUCCESS(rv, rv);
96 // aHost (and thus aBaseDomain) may be the string '.'. If so, fail.
97 if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.') {
98 return NS_ERROR_INVALID_ARG;
101 // block any URIs without a host that aren't file:// URIs.
102 if (aBaseDomain.IsEmpty() && !aHostURI->SchemeIs("file")) {
103 return NS_ERROR_INVALID_ARG;
106 return NS_OK;
109 nsresult CookieCommons::GetBaseDomain(nsIPrincipal* aPrincipal,
110 nsACString& aBaseDomain) {
111 MOZ_ASSERT(aPrincipal);
113 // for historical reasons we use ascii host for file:// URLs.
114 if (aPrincipal->SchemeIs("file")) {
115 return nsContentUtils::GetHostOrIPv6WithBrackets(aPrincipal, aBaseDomain);
118 nsresult rv = aPrincipal->GetBaseDomain(aBaseDomain);
119 if (NS_FAILED(rv)) {
120 return rv;
123 nsContentUtils::MaybeFixIPv6Host(aBaseDomain);
124 return NS_OK;
127 // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
128 // "bbc.co.uk". This is done differently than GetBaseDomain(mTLDService, ): it
129 // is assumed that aHost is already normalized, and it may contain a leading dot
130 // (indicating that it represents a domain). A trailing dot may be present.
131 // If aHost is an IP address, an alias such as 'localhost', an eTLD such as
132 // 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
133 // leading dot will be treated as an error.
134 nsresult CookieCommons::GetBaseDomainFromHost(
135 nsIEffectiveTLDService* aTLDService, const nsACString& aHost,
136 nsCString& aBaseDomain) {
137 // aHost must not be the string '.'.
138 if (aHost.Length() == 1 && aHost.Last() == '.') {
139 return NS_ERROR_INVALID_ARG;
142 // aHost may contain a leading dot; if so, strip it now.
143 bool domain = !aHost.IsEmpty() && aHost.First() == '.';
145 // get the base domain. this will fail if the host contains a leading dot,
146 // more than one trailing dot, or is otherwise malformed.
147 nsresult rv = aTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0,
148 aBaseDomain);
149 if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
150 rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
151 // aHost is either an IP address, an alias such as 'localhost', an eTLD
152 // such as 'co.uk', or the empty string. use the host as a key in such
153 // cases; however, we reject any such hosts with a leading dot, since it
154 // doesn't make sense for them to be domain cookies.
155 if (domain) {
156 return NS_ERROR_INVALID_ARG;
159 aBaseDomain = aHost;
160 return NS_OK;
162 return rv;
165 namespace {
167 void NotifyRejectionToObservers(nsIURI* aHostURI, CookieOperation aOperation) {
168 if (aOperation == OPERATION_WRITE) {
169 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
170 if (os) {
171 os->NotifyObservers(aHostURI, "cookie-rejected", nullptr);
173 } else {
174 MOZ_ASSERT(aOperation == OPERATION_READ);
178 } // namespace
180 // Notify observers that a cookie was rejected due to the users' prefs.
181 void CookieCommons::NotifyRejected(nsIURI* aHostURI, nsIChannel* aChannel,
182 uint32_t aRejectedReason,
183 CookieOperation aOperation) {
184 NotifyRejectionToObservers(aHostURI, aOperation);
186 ContentBlockingNotifier::OnDecision(
187 aChannel, ContentBlockingNotifier::BlockingDecision::eBlock,
188 aRejectedReason);
191 bool CookieCommons::CheckPathSize(const CookieStruct& aCookieData) {
192 return aCookieData.path().Length() <= kMaxBytesPerPath;
195 bool CookieCommons::CheckNameAndValueSize(const CookieStruct& aCookieData) {
196 // reject cookie if it's over the size limit, per RFC2109
197 return (aCookieData.name().Length() + aCookieData.value().Length()) <=
198 kMaxBytesPerCookie;
201 bool CookieCommons::CheckName(const CookieStruct& aCookieData) {
202 const char illegalNameCharacters[] = {
203 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
204 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
205 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x3B, 0x3D, 0x7F, 0x00};
207 const auto* start = aCookieData.name().BeginReading();
208 const auto* end = aCookieData.name().EndReading();
210 auto charFilter = [&](unsigned char c) {
211 if (StaticPrefs::network_cookie_blockUnicode() && c >= 0x80) {
212 return true;
214 return std::find(std::begin(illegalNameCharacters),
215 std::end(illegalNameCharacters),
216 c) != std::end(illegalNameCharacters);
219 return std::find_if(start, end, charFilter) == end;
222 bool CookieCommons::CheckValue(const CookieStruct& aCookieData) {
223 // reject cookie if value contains an RFC 6265 disallowed character - see
224 // https://bugzilla.mozilla.org/show_bug.cgi?id=1191423
225 // NOTE: this is not the full set of characters disallowed by 6265 - notably
226 // 0x09, 0x20, 0x22, 0x2C, and 0x5C are missing from this list.
227 const char illegalCharacters[] = {
228 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C,
229 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
230 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x3B, 0x7F, 0x00};
232 const auto* start = aCookieData.value().BeginReading();
233 const auto* end = aCookieData.value().EndReading();
235 auto charFilter = [&](unsigned char c) {
236 if (StaticPrefs::network_cookie_blockUnicode() && c >= 0x80) {
237 return true;
239 return std::find(std::begin(illegalCharacters), std::end(illegalCharacters),
240 c) != std::end(illegalCharacters);
243 return std::find_if(start, end, charFilter) == end;
246 // static
247 bool CookieCommons::CheckCookiePermission(nsIChannel* aChannel,
248 CookieStruct& aCookieData) {
249 if (!aChannel) {
250 // No channel, let's assume this is a system-principal request.
251 return true;
254 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
255 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
256 nsresult rv =
257 loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
258 if (NS_WARN_IF(NS_FAILED(rv))) {
259 return true;
262 nsIScriptSecurityManager* ssm =
263 nsScriptSecurityManager::GetScriptSecurityManager();
264 MOZ_ASSERT(ssm);
266 nsCOMPtr<nsIPrincipal> channelPrincipal;
267 rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(channelPrincipal));
268 if (NS_WARN_IF(NS_FAILED(rv))) {
269 return false;
272 return CheckCookiePermission(channelPrincipal, cookieJarSettings,
273 aCookieData);
276 // static
277 bool CookieCommons::CheckCookiePermission(
278 nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
279 CookieStruct& aCookieData) {
280 MOZ_ASSERT(aPrincipal);
281 MOZ_ASSERT(aCookieJarSettings);
283 if (!aPrincipal->GetIsContentPrincipal()) {
284 return true;
287 uint32_t cookiePermission = nsICookiePermission::ACCESS_DEFAULT;
288 nsresult rv =
289 aCookieJarSettings->CookiePermission(aPrincipal, &cookiePermission);
290 if (NS_WARN_IF(NS_FAILED(rv))) {
291 return true;
294 if (cookiePermission == nsICookiePermission::ACCESS_ALLOW) {
295 return true;
298 if (cookiePermission == nsICookiePermission::ACCESS_SESSION) {
299 aCookieData.isSession() = true;
300 return true;
303 if (cookiePermission == nsICookiePermission::ACCESS_DENY) {
304 return false;
307 return true;
310 namespace {
312 CookieStatus CookieStatusForWindow(nsPIDOMWindowInner* aWindow,
313 nsIURI* aDocumentURI) {
314 MOZ_ASSERT(aWindow);
315 MOZ_ASSERT(aDocumentURI);
317 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
318 if (thirdPartyUtil) {
319 bool isThirdParty = true;
321 nsresult rv = thirdPartyUtil->IsThirdPartyWindow(
322 aWindow->GetOuterWindow(), aDocumentURI, &isThirdParty);
323 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Third-party window check failed.");
325 if (NS_SUCCEEDED(rv) && !isThirdParty) {
326 return STATUS_ACCEPTED;
330 if (StaticPrefs::network_cookie_thirdparty_sessionOnly()) {
331 return STATUS_ACCEPT_SESSION;
334 if (StaticPrefs::network_cookie_thirdparty_nonsecureSessionOnly() &&
335 !nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aDocumentURI)) {
336 return STATUS_ACCEPT_SESSION;
339 return STATUS_ACCEPTED;
342 } // namespace
344 // static
345 already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
346 Document* aDocument, const nsACString& aCookieString,
347 int64_t currentTimeInUsec, nsIEffectiveTLDService* aTLDService,
348 mozIThirdPartyUtil* aThirdPartyUtil,
349 std::function<bool(const nsACString&, const OriginAttributes&)>&&
350 aHasExistingCookiesLambda,
351 nsIURI** aDocumentURI, nsACString& aBaseDomain, OriginAttributes& aAttrs) {
352 nsCOMPtr<nsIPrincipal> storagePrincipal =
353 aDocument->EffectiveCookiePrincipal();
354 MOZ_ASSERT(storagePrincipal);
356 nsCOMPtr<nsIURI> principalURI;
357 auto* basePrincipal = BasePrincipal::Cast(aDocument->NodePrincipal());
358 basePrincipal->GetURI(getter_AddRefs(principalURI));
359 if (NS_WARN_IF(!principalURI)) {
360 // Document's principal is not a content or null (may be system), so
361 // can't set cookies
362 return nullptr;
365 if (!CookieCommons::IsSchemeSupported(principalURI)) {
366 return nullptr;
369 nsAutoCString baseDomain;
370 bool requireHostMatch = false;
371 nsresult rv = CookieCommons::GetBaseDomain(aTLDService, principalURI,
372 baseDomain, requireHostMatch);
373 if (NS_WARN_IF(NS_FAILED(rv))) {
374 return nullptr;
377 nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
378 if (NS_WARN_IF(!innerWindow)) {
379 return nullptr;
382 // Check if limit-foreign is required.
383 uint32_t dummyRejectedReason = 0;
384 if (aDocument->CookieJarSettings()->GetLimitForeignContexts() &&
385 !aHasExistingCookiesLambda(baseDomain,
386 storagePrincipal->OriginAttributesRef()) &&
387 !ShouldAllowAccessFor(innerWindow, principalURI, &dummyRejectedReason)) {
388 return nullptr;
391 bool isForeignAndNotAddon = false;
392 if (!BasePrincipal::Cast(aDocument->NodePrincipal())->AddonPolicy()) {
393 rv = aThirdPartyUtil->IsThirdPartyWindow(
394 innerWindow->GetOuterWindow(), principalURI, &isForeignAndNotAddon);
395 if (NS_WARN_IF(NS_FAILED(rv))) {
396 isForeignAndNotAddon = true;
400 bool mustBePartitioned =
401 isForeignAndNotAddon &&
402 aDocument->CookieJarSettings()->GetCookieBehavior() ==
403 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
404 !aDocument->UsingStorageAccess();
406 // If we are here, we have been already accepted by the anti-tracking.
407 // We just need to check if we have to be in session-only mode.
408 CookieStatus cookieStatus = CookieStatusForWindow(innerWindow, principalURI);
409 MOZ_ASSERT(cookieStatus == STATUS_ACCEPTED ||
410 cookieStatus == STATUS_ACCEPT_SESSION);
412 // Console report takes care of the correct reporting at the exit of this
413 // method.
414 RefPtr<ConsoleReportCollector> crc = new ConsoleReportCollector();
415 auto scopeExit = MakeScopeExit([&] { crc->FlushConsoleReports(aDocument); });
417 nsCString cookieString(aCookieString);
419 CookieStruct cookieData;
420 MOZ_ASSERT(cookieData.creationTime() == 0, "Must be initialized to 0");
421 bool canSetCookie = false;
422 CookieService::CanSetCookie(principalURI, baseDomain, cookieData,
423 requireHostMatch, cookieStatus, cookieString,
424 false, isForeignAndNotAddon, mustBePartitioned,
425 crc, canSetCookie);
427 if (!canSetCookie) {
428 return nullptr;
431 // check permissions from site permission list.
432 if (!CookieCommons::CheckCookiePermission(aDocument->NodePrincipal(),
433 aDocument->CookieJarSettings(),
434 cookieData)) {
435 NotifyRejectionToObservers(principalURI, OPERATION_WRITE);
436 ContentBlockingNotifier::OnDecision(
437 innerWindow, ContentBlockingNotifier::BlockingDecision::eBlock,
438 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION);
439 return nullptr;
442 RefPtr<Cookie> cookie =
443 Cookie::Create(cookieData, storagePrincipal->OriginAttributesRef());
444 MOZ_ASSERT(cookie);
446 cookie->SetLastAccessed(currentTimeInUsec);
447 cookie->SetCreationTime(
448 Cookie::GenerateUniqueCreationTime(currentTimeInUsec));
450 aBaseDomain = baseDomain;
451 aAttrs = storagePrincipal->OriginAttributesRef();
452 principalURI.forget(aDocumentURI);
454 return cookie.forget();
457 // static
458 already_AddRefed<nsICookieJarSettings> CookieCommons::GetCookieJarSettings(
459 nsIChannel* aChannel) {
460 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
461 bool shouldResistFingerprinting = nsContentUtils::ShouldResistFingerprinting(
462 aChannel, RFPTarget::IsAlwaysEnabledForPrecompute);
463 if (aChannel) {
464 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
465 nsresult rv =
466 loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
467 if (NS_WARN_IF(NS_FAILED(rv))) {
468 cookieJarSettings =
469 CookieJarSettings::GetBlockingAll(shouldResistFingerprinting);
471 } else {
472 cookieJarSettings = CookieJarSettings::Create(CookieJarSettings::eRegular,
473 shouldResistFingerprinting);
476 MOZ_ASSERT(cookieJarSettings);
477 return cookieJarSettings.forget();
480 // static
481 bool CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
482 Cookie* aCookie, dom::Document* aDocument) {
483 MOZ_ASSERT(aCookie);
484 MOZ_ASSERT(aDocument);
486 int32_t sameSiteAttr = 0;
487 aCookie->GetSameSite(&sameSiteAttr);
489 if (aDocument->CookieJarSettings()->GetPartitionForeign() &&
490 StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() &&
491 !aCookie->IsPartitioned()) {
492 return false;
495 return sameSiteAttr == nsICookie::SAMESITE_NONE;
498 bool CookieCommons::IsSafeTopLevelNav(nsIChannel* aChannel) {
499 if (!aChannel) {
500 return false;
502 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
503 nsCOMPtr<nsIInterceptionInfo> interceptionInfo = loadInfo->InterceptionInfo();
504 if ((loadInfo->GetExternalContentPolicyType() !=
505 ExtContentPolicy::TYPE_DOCUMENT &&
506 loadInfo->GetExternalContentPolicyType() !=
507 ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) &&
508 !interceptionInfo) {
509 return false;
512 if (interceptionInfo &&
513 interceptionInfo->GetExtContentPolicyType() !=
514 ExtContentPolicy::TYPE_DOCUMENT &&
515 interceptionInfo->GetExtContentPolicyType() !=
516 ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD &&
517 interceptionInfo->GetExtContentPolicyType() !=
518 ExtContentPolicy::TYPE_INVALID) {
519 return false;
522 return NS_IsSafeMethodNav(aChannel);
525 // This function determines if two schemes are equal in the context of
526 // "Schemeful SameSite cookies".
528 // Two schemes are considered equal:
529 // - if the "network.cookie.sameSite.schemeful" pref is set to false.
530 // OR
531 // - if one of the schemes is not http or https.
532 // OR
533 // - if both schemes are equal AND both are either http or https.
534 bool IsSameSiteSchemeEqual(const nsACString& aFirstScheme,
535 const nsACString& aSecondScheme) {
536 if (!StaticPrefs::network_cookie_sameSite_schemeful()) {
537 return true;
540 auto isSchemeHttpOrHttps = [](const nsACString& scheme) -> bool {
541 return scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https");
544 if (!isSchemeHttpOrHttps(aFirstScheme) ||
545 !isSchemeHttpOrHttps(aSecondScheme)) {
546 return true;
549 return aFirstScheme.Equals(aSecondScheme);
552 bool CookieCommons::IsSameSiteForeign(nsIChannel* aChannel, nsIURI* aHostURI,
553 bool* aHadCrossSiteRedirects) {
554 *aHadCrossSiteRedirects = false;
556 if (!aChannel) {
557 return false;
559 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
560 // Do not treat loads triggered by web extensions as foreign
561 nsCOMPtr<nsIURI> channelURI;
562 NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
564 nsCOMPtr<nsIInterceptionInfo> interceptionInfo = loadInfo->InterceptionInfo();
566 RefPtr<BasePrincipal> triggeringPrincipal;
567 ExtContentPolicy contentPolicyType;
568 if (interceptionInfo && interceptionInfo->TriggeringPrincipal()) {
569 triggeringPrincipal =
570 BasePrincipal::Cast(interceptionInfo->TriggeringPrincipal());
571 contentPolicyType = interceptionInfo->GetExtContentPolicyType();
572 } else {
573 triggeringPrincipal = BasePrincipal::Cast(loadInfo->TriggeringPrincipal());
574 contentPolicyType = loadInfo->GetExternalContentPolicyType();
576 if (triggeringPrincipal->AddonPolicy() &&
577 triggeringPrincipal->AddonAllowsLoad(channelURI)) {
578 return false;
581 const nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>>& redirectChain(
582 interceptionInfo && interceptionInfo->TriggeringPrincipal()
583 ? interceptionInfo->RedirectChain()
584 : loadInfo->RedirectChain());
586 nsAutoCString hostScheme, otherScheme;
587 aHostURI->GetScheme(hostScheme);
589 bool isForeign = true;
590 nsresult rv;
591 if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
592 contentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) {
593 // for loads of TYPE_DOCUMENT we query the hostURI from the
594 // triggeringPrincipal which returns the URI of the document that caused the
595 // navigation.
596 rv = triggeringPrincipal->IsThirdPartyChannel(aChannel, &isForeign);
598 triggeringPrincipal->GetScheme(otherScheme);
599 } else {
600 // If the load is caused by FetchEvent.request or NavigationPreload request,
601 // check the original InterceptedHttpChannel is a third-party channel or
602 // not.
603 if (interceptionInfo && interceptionInfo->TriggeringPrincipal()) {
604 isForeign = interceptionInfo->FromThirdParty();
605 if (isForeign) {
606 return true;
609 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
610 do_GetService(THIRDPARTYUTIL_CONTRACTID);
611 if (!thirdPartyUtil) {
612 return true;
614 rv = thirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
616 channelURI->GetScheme(otherScheme);
618 // if we are dealing with a cross origin request, we can return here
619 // because we already know the request is 'foreign'.
620 if (NS_FAILED(rv) || isForeign) {
621 return true;
624 if (!IsSameSiteSchemeEqual(otherScheme, hostScheme)) {
625 // If the two schemes are not of the same http(s) scheme then we
626 // consider the request as foreign.
627 return true;
630 // for loads of TYPE_SUBDOCUMENT we have to perform an additional test,
631 // because a cross-origin iframe might perform a navigation to a same-origin
632 // iframe which would send same-site cookies. Hence, if the iframe navigation
633 // was triggered by a cross-origin triggeringPrincipal, we treat the load as
634 // foreign.
635 if (contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
636 rv = triggeringPrincipal->IsThirdPartyChannel(aChannel, &isForeign);
637 if (NS_FAILED(rv) || isForeign) {
638 return true;
642 // for the purpose of same-site cookies we have to treat any cross-origin
643 // redirects as foreign. E.g. cross-site to same-site redirect is a problem
644 // with regards to CSRF.
646 nsCOMPtr<nsIPrincipal> redirectPrincipal;
647 for (nsIRedirectHistoryEntry* entry : redirectChain) {
648 entry->GetPrincipal(getter_AddRefs(redirectPrincipal));
649 if (redirectPrincipal) {
650 rv = redirectPrincipal->IsThirdPartyChannel(aChannel, &isForeign);
651 // if at any point we encounter a cross-origin redirect we can return.
652 if (NS_FAILED(rv) || isForeign) {
653 *aHadCrossSiteRedirects = isForeign;
654 return true;
657 nsAutoCString redirectScheme;
658 redirectPrincipal->GetScheme(redirectScheme);
659 if (!IsSameSiteSchemeEqual(redirectScheme, hostScheme)) {
660 // If the two schemes are not of the same http(s) scheme then we
661 // consider the request as foreign.
662 *aHadCrossSiteRedirects = true;
663 return true;
667 return isForeign;
670 // static
671 nsICookie::schemeType CookieCommons::URIToSchemeType(nsIURI* aURI) {
672 MOZ_ASSERT(aURI);
674 nsAutoCString scheme;
675 nsresult rv = aURI->GetScheme(scheme);
676 if (NS_WARN_IF(NS_FAILED(rv))) {
677 return nsICookie::SCHEME_UNSET;
680 return SchemeToSchemeType(scheme);
683 // static
684 nsICookie::schemeType CookieCommons::PrincipalToSchemeType(
685 nsIPrincipal* aPrincipal) {
686 MOZ_ASSERT(aPrincipal);
688 nsAutoCString scheme;
689 nsresult rv = aPrincipal->GetScheme(scheme);
690 if (NS_WARN_IF(NS_FAILED(rv))) {
691 return nsICookie::SCHEME_UNSET;
694 return SchemeToSchemeType(scheme);
697 // static
698 nsICookie::schemeType CookieCommons::SchemeToSchemeType(
699 const nsACString& aScheme) {
700 MOZ_ASSERT(IsSchemeSupported(aScheme));
702 if (aScheme.Equals("https")) {
703 return nsICookie::SCHEME_HTTPS;
706 if (aScheme.Equals("http")) {
707 return nsICookie::SCHEME_HTTP;
710 if (aScheme.Equals("file")) {
711 return nsICookie::SCHEME_FILE;
714 MOZ_CRASH("Unsupported scheme type");
717 // static
718 bool CookieCommons::IsSchemeSupported(nsIPrincipal* aPrincipal) {
719 MOZ_ASSERT(aPrincipal);
721 nsAutoCString scheme;
722 nsresult rv = aPrincipal->GetScheme(scheme);
723 if (NS_WARN_IF(NS_FAILED(rv))) {
724 return false;
727 return IsSchemeSupported(scheme);
730 // static
731 bool CookieCommons::IsSchemeSupported(nsIURI* aURI) {
732 MOZ_ASSERT(aURI);
734 nsAutoCString scheme;
735 nsresult rv = aURI->GetScheme(scheme);
736 if (NS_WARN_IF(NS_FAILED(rv))) {
737 return false;
740 return IsSchemeSupported(scheme);
743 // static
744 bool CookieCommons::IsSchemeSupported(const nsACString& aScheme) {
745 return aScheme.Equals("https") || aScheme.Equals("http") ||
746 aScheme.Equals("file");
749 } // namespace net
750 } // namespace mozilla