Bug 1910886 - Reduce duplicated code between CookieService and CookieServiceChild - part 2 - Unify nsICookieService.setCookieStringFromDocument implementations, r=edgul,cookie-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D218313
This commit is contained in:
Andrea Marchesini 2024-08-09 08:37:13 +00:00
Родитель f2b80c9707
Коммит 64d766f610
14 изменённых файлов: 238 добавлений и 264 удалений

Просмотреть файл

@ -269,6 +269,7 @@
#include "mozilla/net/Cookie.h"
#include "mozilla/net/CookieCommons.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/net/CookieParser.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/RequestContextService.h"
#include "nsAboutProtocolUtils.h"
@ -6787,7 +6788,7 @@ void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
UTF_8_ENCODING->DecodeWithoutBOMHandling(cookieString, aCookie);
}
void Document::SetCookie(const nsAString& aCookie, ErrorResult& aRv) {
void Document::SetCookie(const nsAString& aCookieString, ErrorResult& aRv) {
if (mDisableCookieAccess) {
return;
}
@ -6827,19 +6828,71 @@ void Document::SetCookie(const nsAString& aCookie, ErrorResult& aRv) {
return;
}
NS_ConvertUTF16toUTF8 cookie(aCookie);
nsresult rv = service->SetCookieStringFromDocument(this, cookie);
NS_ConvertUTF16toUTF8 cookieString(aCookieString);
// No warning messages here.
if (NS_FAILED(rv)) {
nsCOMPtr<nsIURI> documentURI;
nsAutoCString baseDomain;
OriginAttributes attrs;
int64_t currentTimeInUsec = PR_Now();
auto* basePrincipal = BasePrincipal::Cast(NodePrincipal());
basePrincipal->GetURI(getter_AddRefs(documentURI));
if (NS_WARN_IF(!documentURI)) {
// Document's principal is not a content or null (may be system), so
// can't set cookies
return;
}
// Console report takes care of the correct reporting at the exit of this
// method.
RefPtr<ConsoleReportCollector> crc = new ConsoleReportCollector();
auto scopeExit = MakeScopeExit([&] { crc->FlushConsoleReports(this); });
CookieParser cookieParser(crc, documentURI);
ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
if (!thirdPartyUtil) {
return;
}
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
return;
}
RefPtr<Cookie> cookie = CookieCommons::CreateCookieFromDocument(
cookieParser, this, cookieString, currentTimeInUsec, tldService,
thirdPartyUtil, baseDomain, attrs);
if (!cookie) {
return;
}
bool thirdParty = true;
nsPIDOMWindowInner* innerWindow = GetInnerWindow();
// in gtests we don't have a window, let's consider those requests as 3rd
// party.
if (innerWindow) {
Unused << thirdPartyUtil->IsThirdPartyWindow(innerWindow->GetOuterWindow(),
nullptr, &thirdParty);
}
if (thirdParty &&
!CookieCommons::ShouldIncludeCrossSiteCookieForDocument(cookie, this)) {
return;
}
// add the cookie to the list. AddCookieFromDocument() takes care of logging.
service->AddCookieFromDocument(cookieParser, baseDomain, attrs, *cookie,
currentTimeInUsec, documentURI, thirdParty,
this);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(ToSupports(this), "document-set-cookie",
nsString(aCookie).get());
nsString(aCookieString).get());
}
}

Просмотреть файл

@ -368,8 +368,6 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
CookieParser& aCookieParser, Document* aDocument,
const nsACString& aCookieString, int64_t currentTimeInUsec,
nsIEffectiveTLDService* aTLDService, mozIThirdPartyUtil* aThirdPartyUtil,
std::function<bool(const nsACString&, const OriginAttributes&)>&&
aHasExistingCookiesLambda,
nsACString& aBaseDomain, OriginAttributes& aAttrs) {
if (!CookieCommons::IsSchemeSupported(aCookieParser.HostURI())) {
return nullptr;
@ -447,11 +445,17 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
: aDocument->EffectiveCookiePrincipal();
MOZ_ASSERT(cookiePrincipal);
nsCOMPtr<nsICookieService> service =
do_GetService(NS_COOKIESERVICE_CONTRACTID);
if (!service) {
return nullptr;
}
// Check if limit-foreign is required.
uint32_t dummyRejectedReason = 0;
if (aDocument->CookieJarSettings()->GetLimitForeignContexts() &&
!aHasExistingCookiesLambda(baseDomain,
cookiePrincipal->OriginAttributesRef()) &&
!service->HasExistingCookies(baseDomain,
cookiePrincipal->OriginAttributesRef()) &&
!ShouldAllowAccessFor(innerWindow, aCookieParser.HostURI(),
&dummyRejectedReason)) {
return nullptr;

Просмотреть файл

@ -107,8 +107,6 @@ class CookieCommons final {
CookieParser& aCookieParser, dom::Document* aDocument,
const nsACString& aCookieString, int64_t aCurrentTimeInUsec,
nsIEffectiveTLDService* aTLDService, mozIThirdPartyUtil* aThirdPartyUtil,
std::function<bool(const nsACString&, const OriginAttributes&)>&&
aHasExistingCookiesLambda,
nsACString& aBaseDomain, OriginAttributes& aAttrs);
static already_AddRefed<nsICookieJarSettings> GetCookieJarSettings(

Просмотреть файл

@ -860,7 +860,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// aCookieHeader is an in/out param to point to the next cookie, if
// there is one. Save the present value for logging purposes
nsCString savedCookieHeader(aCookieHeader);
mCookieString.Assign(aCookieHeader);
// newCookie says whether there are multiple cookies in the header;
// so we can handle them separately.
@ -897,7 +897,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// reject cookie if name and value are empty, per RFC6265bis
if (mCookieData.name().IsEmpty() && mCookieData.value().IsEmpty()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"cookie name and value are empty");
RejectCookie(RejectedEmptyNameAndValue);
@ -906,7 +906,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// reject cookie if it's over the size limit, per RFC2109
if (!CookieCommons::CheckNameAndValueSize(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"cookie too big (> 4kb)");
RejectCookie(RejectedNameValueOversize);
return newCookie;
@ -921,7 +921,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
}
if (!CookieCommons::CheckName(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"invalid name character");
RejectCookie(RejectedInvalidCharName);
return newCookie;
@ -929,14 +929,14 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// domain & path checks
if (!CheckDomain(mCookieData, mHostURI, aBaseDomain, aRequireHostMatch)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"failed the domain tests");
RejectCookie(RejectedInvalidDomain);
return newCookie;
}
if (!CheckPath()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"failed the path tests");
return newCookie;
}
@ -945,7 +945,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// `__Host-` or `__Secure-`
if (mCookieData.name().IsEmpty() && (HasSecurePrefix(mCookieData.value()) ||
HasHostPrefix(mCookieData.value()))) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"failed hidden prefix tests");
RejectCookie(RejectedInvalidPrefix);
return newCookie;
@ -953,14 +953,14 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// magic prefix checks. MUST be run after CheckDomain() and CheckPath()
if (!CheckPrefixes(mCookieData, potentiallyTrustworthy)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"failed the prefix tests");
RejectCookie(RejectedInvalidPrefix);
return newCookie;
}
if (!CookieCommons::CheckValue(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"invalid value character");
RejectCookie(RejectedInvalidCharValue);
return newCookie;
@ -968,7 +968,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
// if the new cookie is httponly, make sure we're not coming from script
if (!aFromHttp && mCookieData.isHttpOnly()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"cookie is httponly; coming from script");
RejectCookie(RejectedHttpOnlyButFromScript);
return newCookie;
@ -994,7 +994,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
laxByDefault ? mCookieData.sameSite() : mCookieData.rawSameSite();
if ((effectiveSameSite != nsICookie::SAMESITE_NONE) &&
aIsForeignAndNotAddon) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"failed the samesite tests");
RejectCookie(RejectedForNonSameSiteness);
return newCookie;
@ -1010,7 +1010,7 @@ bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
(aIsInPrivateBrowsing &&
StaticPrefs::
network_cookie_cookieBehavior_optInPartitioning_pbmode())) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, mCookieString,
"foreign cookies must be partitioned");
RejectCookie(RejectedForeignNoPartitionedError);
return newCookie;
@ -1028,5 +1028,9 @@ void CookieParser::RejectCookie(Rejection aRejection) {
mRejection = aRejection;
}
void CookieParser::GetCookieString(nsACString& aCookieString) const {
aCookieString.Assign(mCookieString);
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -63,6 +63,8 @@ class CookieParser final {
return mCookieData;
}
void GetCookieString(nsACString& aCookieString) const;
// Public for testing
bool ParseMaxAgeAttribute(const nsACString& aMaxage, int64_t* aValue);
@ -112,6 +114,7 @@ class CookieParser final {
} mWarnings;
CookieStruct mCookieData;
nsCString mCookieString;
};
} // namespace net

Просмотреть файл

@ -419,77 +419,6 @@ CookieService::GetCookieStringFromHttp(nsIURI* aHostURI, nsIChannel* aChannel,
return NS_OK;
}
NS_IMETHODIMP
CookieService::SetCookieStringFromDocument(Document* aDocument,
const nsACString& aCookieString) {
NS_ENSURE_ARG(aDocument);
if (!IsInitialized()) {
return NS_OK;
}
nsCOMPtr<nsIURI> documentURI;
nsAutoCString baseDomain;
OriginAttributes attrs;
int64_t currentTimeInUsec = PR_Now();
// This function is executed in this context, I don't need to keep objects
// alive.
auto hasExistingCookiesLambda = [&](const nsACString& aBaseDomain,
const OriginAttributes& aAttrs) {
CookieStorage* storage = PickStorage(aAttrs);
return !!storage->CountCookiesFromHost(aBaseDomain,
aAttrs.mPrivateBrowsingId);
};
auto* basePrincipal = BasePrincipal::Cast(aDocument->NodePrincipal());
basePrincipal->GetURI(getter_AddRefs(documentURI));
if (NS_WARN_IF(!documentURI)) {
// Document's principal is not a content or null (may be system), so
// can't set cookies
return NS_OK;
}
// Console report takes care of the correct reporting at the exit of this
// method.
RefPtr<ConsoleReportCollector> crc = new ConsoleReportCollector();
auto scopeExit = MakeScopeExit([&] { crc->FlushConsoleReports(aDocument); });
CookieParser cookieParser(crc, documentURI);
RefPtr<Cookie> cookie = CookieCommons::CreateCookieFromDocument(
cookieParser, aDocument, aCookieString, currentTimeInUsec, mTLDService,
mThirdPartyUtil, hasExistingCookiesLambda, baseDomain, attrs);
if (!cookie) {
return NS_OK;
}
bool thirdParty = true;
nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
// in gtests we don't have a window, let's consider those requests as 3rd
// party.
if (innerWindow) {
ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
if (thirdPartyUtil) {
Unused << thirdPartyUtil->IsThirdPartyWindow(
innerWindow->GetOuterWindow(), nullptr, &thirdParty);
}
}
if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
return NS_OK;
}
// add the cookie to the list. AddCookie() takes care of logging.
PickStorage(attrs)->AddCookie(
&cookieParser, baseDomain, attrs, cookie, currentTimeInUsec, documentURI,
aCookieString, false, thirdParty, aDocument->GetBrowsingContext());
return NS_OK;
}
NS_IMETHODIMP
CookieService::SetCookieStringFromHttp(nsIURI* aHostURI,
const nsACString& aCookieHeader,
@ -1694,9 +1623,8 @@ bool CookieService::SetCookiesFromIPC(const nsACString& aBaseDomain,
}
void CookieService::GetCookiesFromHost(
const nsACString& aBaseDomain,
const mozilla::OriginAttributes& aOriginAttributes,
nsTArray<RefPtr<mozilla::net::Cookie>>& aCookies) {
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes,
nsTArray<RefPtr<Cookie>>& aCookies) {
if (!IsInitialized()) {
return;
}
@ -1705,9 +1633,8 @@ void CookieService::GetCookiesFromHost(
storage->GetCookiesFromHost(aBaseDomain, aOriginAttributes, aCookies);
}
void CookieService::StaleCookies(
const nsTArray<RefPtr<mozilla::net::Cookie>>& aCookies,
int64_t aCurrentTimeInUsec) {
void CookieService::StaleCookies(const nsTArray<RefPtr<Cookie>>& aCookies,
int64_t aCurrentTimeInUsec) {
if (!IsInitialized()) {
return;
}
@ -1727,5 +1654,37 @@ void CookieService::StaleCookies(
storage->StaleCookies(aCookies, aCurrentTimeInUsec);
}
bool CookieService::HasExistingCookies(
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes) {
if (!IsInitialized()) {
return false;
}
CookieStorage* storage = PickStorage(aOriginAttributes);
return !!storage->CountCookiesFromHost(aBaseDomain,
aOriginAttributes.mPrivateBrowsingId);
}
void CookieService::AddCookieFromDocument(
CookieParser& aCookieParser, const nsACString& aBaseDomain,
const OriginAttributes& aOriginAttributes, Cookie& aCookie,
int64_t aCurrentTimeInUsec, nsIURI* aDocumentURI, bool aThirdParty,
Document* aDocument) {
MOZ_ASSERT(aDocumentURI);
MOZ_ASSERT(aDocument);
if (!IsInitialized()) {
return;
}
nsAutoCString cookieString;
aCookieParser.GetCookieString(cookieString);
PickStorage(aOriginAttributes)
->AddCookie(&aCookieParser, aBaseDomain, aOriginAttributes, &aCookie,
aCurrentTimeInUsec, aDocumentURI, cookieString, false,
aThirdParty, aDocument->GetBrowsingContext());
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -272,17 +272,6 @@ IPCResult CookieServiceChild::RecvTrackCookiesLoad(
return IPC_OK();
}
uint32_t CookieServiceChild::CountCookiesFromHashTable(
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttrs) {
CookiesList* cookiesList = nullptr;
nsCString baseDomain;
CookieKey key(aBaseDomain, aOriginAttrs);
mCookiesMap.Get(key, &cookiesList);
return cookiesList ? cookiesList->Length() : 0;
}
/* static */ bool CookieServiceChild::RequireThirdPartyCheck(
nsILoadInfo* aLoadInfo) {
if (!aLoadInfo) {
@ -352,123 +341,6 @@ CookieServiceChild::GetCookieStringFromHttp(nsIURI* /*aHostURI*/,
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
CookieServiceChild::SetCookieStringFromDocument(
dom::Document* aDocument, const nsACString& aCookieString) {
NS_ENSURE_ARG(aDocument);
nsCOMPtr<nsIURI> documentURI;
nsAutoCString baseDomain;
OriginAttributes attrs;
// This function is executed in this context, I don't need to keep objects
// alive.
auto hasExistingCookiesLambda = [&](const nsACString& aBaseDomain,
const OriginAttributes& aAttrs) {
return !!CountCookiesFromHashTable(aBaseDomain, aAttrs);
};
auto* basePrincipal = BasePrincipal::Cast(aDocument->NodePrincipal());
basePrincipal->GetURI(getter_AddRefs(documentURI));
if (NS_WARN_IF(!documentURI)) {
// Document's principal is not a content or null (may be system), so
// can't set cookies
return NS_OK;
}
// Console report takes care of the correct reporting at the exit of this
// method.
RefPtr<ConsoleReportCollector> crc = new ConsoleReportCollector();
auto scopeExit = MakeScopeExit([&] { crc->FlushConsoleReports(aDocument); });
CookieParser cookieParser(crc, documentURI);
RefPtr<Cookie> cookie = CookieCommons::CreateCookieFromDocument(
cookieParser, aDocument, aCookieString, PR_Now(), mTLDService,
mThirdPartyUtil, hasExistingCookiesLambda, baseDomain, attrs);
if (!cookie) {
return NS_OK;
}
bool thirdParty = true;
nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
// in gtests we don't have a window, let's consider those requests as 3rd
// party.
if (innerWindow) {
ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
if (thirdPartyUtil) {
Unused << thirdPartyUtil->IsThirdPartyWindow(
innerWindow->GetOuterWindow(), nullptr, &thirdParty);
}
}
if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
return NS_OK;
}
CookieKey key(baseDomain, attrs);
CookiesList* cookies = mCookiesMap.Get(key);
if (cookies) {
// We need to see if the cookie we're setting would overwrite an httponly
// or a secure one. This would not affect anything we send over the net
// (those come from the parent, which already checks this),
// but script could see an inconsistent view of things.
// CHIPS - If the cookie has the "Partitioned" attribute set it will be
// stored in the partitioned cookie jar.
bool needPartitioned = StaticPrefs::network_cookie_CHIPS_enabled() &&
cookie->RawIsPartitioned();
nsCOMPtr<nsIPrincipal> principal =
needPartitioned ? aDocument->PartitionedPrincipal()
: aDocument->EffectiveCookiePrincipal();
bool isPotentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
for (uint32_t i = 0; i < cookies->Length(); ++i) {
RefPtr<Cookie> existingCookie = cookies->ElementAt(i);
if (existingCookie->Name().Equals(cookie->Name()) &&
existingCookie->Host().Equals(cookie->Host()) &&
existingCookie->Path().Equals(cookie->Path())) {
// Can't overwrite an httponly cookie from a script context.
if (existingCookie->IsHttpOnly()) {
return NS_OK;
}
// prevent insecure cookie from overwriting a secure one in insecure
// context.
if (existingCookie->IsSecure() && !isPotentiallyTrustworthy) {
return NS_OK;
}
}
}
}
RecordDocumentCookie(cookie, attrs);
if (CanSend()) {
nsTArray<CookieStruct> cookiesToSend;
cookiesToSend.AppendElement(cookie->ToIPC());
// Asynchronously call the parent.
dom::WindowGlobalChild* windowGlobalChild =
aDocument->GetWindowGlobalChild();
// If there is no WindowGlobalChild fall back to PCookieService SetCookies.
if (NS_WARN_IF(!windowGlobalChild)) {
SendSetCookies(baseDomain, attrs, documentURI, false, thirdParty,
cookiesToSend);
return NS_OK;
}
windowGlobalChild->SendSetCookies(baseDomain, attrs, documentURI, false,
thirdParty, cookiesToSend);
}
return NS_OK;
}
NS_IMETHODIMP
CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
const nsACString& aCookieString,
@ -518,7 +390,7 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
aCookieString,
CountCookiesFromHashTable(baseDomain, storagePrincipalOriginAttributes),
HasExistingCookies(baseDomain, storagePrincipalOriginAttributes),
storagePrincipalOriginAttributes, &rejectedReason);
if (cookieStatus != STATUS_ACCEPTED &&
@ -653,9 +525,8 @@ CookieServiceChild::RunInTransaction(
}
void CookieServiceChild::GetCookiesFromHost(
const nsACString& aBaseDomain,
const mozilla::OriginAttributes& aOriginAttributes,
nsTArray<RefPtr<mozilla::net::Cookie>>& aCookies) {
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes,
nsTArray<RefPtr<Cookie>>& aCookies) {
CookieKey key(aBaseDomain, aOriginAttributes);
CookiesList* cookiesList = nullptr;
@ -666,11 +537,89 @@ void CookieServiceChild::GetCookiesFromHost(
}
}
void CookieServiceChild::StaleCookies(
const nsTArray<RefPtr<mozilla::net::Cookie>>& aCookies,
int64_t aCurrentTimeInUsec) {
void CookieServiceChild::StaleCookies(const nsTArray<RefPtr<Cookie>>& aCookies,
int64_t aCurrentTimeInUsec) {
// Nothing to do here.
}
bool CookieServiceChild::HasExistingCookies(
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes) {
CookiesList* cookiesList = nullptr;
CookieKey key(aBaseDomain, aOriginAttributes);
mCookiesMap.Get(key, &cookiesList);
return cookiesList ? cookiesList->Length() : 0;
}
void CookieServiceChild::AddCookieFromDocument(
CookieParser& aCookieParser, const nsACString& aBaseDomain,
const OriginAttributes& aOriginAttributes, Cookie& aCookie,
int64_t aCurrentTimeInUsec, nsIURI* aDocumentURI, bool aThirdParty,
dom::Document* aDocument) {
MOZ_ASSERT(aDocumentURI);
MOZ_ASSERT(aDocument);
CookieKey key(aBaseDomain, aOriginAttributes);
CookiesList* cookies = mCookiesMap.Get(key);
if (cookies) {
// We need to see if the cookie we're setting would overwrite an httponly
// or a secure one. This would not affect anything we send over the net
// (those come from the parent, which already checks this),
// but script could see an inconsistent view of things.
// CHIPS - If the cookie has the "Partitioned" attribute set it will be
// stored in the partitioned cookie jar.
bool needPartitioned = StaticPrefs::network_cookie_CHIPS_enabled() &&
aCookie.RawIsPartitioned();
nsCOMPtr<nsIPrincipal> principal =
needPartitioned ? aDocument->PartitionedPrincipal()
: aDocument->EffectiveCookiePrincipal();
bool isPotentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
for (uint32_t i = 0; i < cookies->Length(); ++i) {
RefPtr<Cookie> existingCookie = cookies->ElementAt(i);
if (existingCookie->Name().Equals(aCookie.Name()) &&
existingCookie->Host().Equals(aCookie.Host()) &&
existingCookie->Path().Equals(aCookie.Path())) {
// Can't overwrite an httponly cookie from a script context.
if (existingCookie->IsHttpOnly()) {
return;
}
// prevent insecure cookie from overwriting a secure one in insecure
// context.
if (existingCookie->IsSecure() && !isPotentiallyTrustworthy) {
return;
}
}
}
}
RecordDocumentCookie(&aCookie, aOriginAttributes);
if (CanSend()) {
nsTArray<CookieStruct> cookiesToSend;
cookiesToSend.AppendElement(aCookie.ToIPC());
// Asynchronously call the parent.
dom::WindowGlobalChild* windowGlobalChild =
aDocument->GetWindowGlobalChild();
// If there is no WindowGlobalChild fall back to PCookieService SetCookies.
if (NS_WARN_IF(!windowGlobalChild)) {
SendSetCookies(aBaseDomain, aOriginAttributes, aDocumentURI, false,
aThirdParty, cookiesToSend);
return;
}
windowGlobalChild->SendSetCookies(aBaseDomain, aOriginAttributes,
aDocumentURI, false, aThirdParty,
cookiesToSend);
}
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -48,9 +48,6 @@ class CookieServiceChild final : public PCookieServiceChild,
void RecordDocumentCookie(Cookie* aCookie, const OriginAttributes& aAttrs);
uint32_t CountCookiesFromHashTable(const nsACString& aBaseDomain,
const OriginAttributes& aOriginAttrs);
static bool RequireThirdPartyCheck(nsILoadInfo* aLoadInfo);
mozilla::ipc::IPCResult RecvTrackCookiesLoad(

Просмотреть файл

@ -16,12 +16,15 @@ namespace mozilla {
namespace net {
class Cookie;
class CookieParser;
}
}
%}
[ref] native const_OriginAttributes(const mozilla::OriginAttributes);
native CookiePtr(RefPtr<mozilla::net::Cookie>);
native CookieRefPtr(RefPtr<mozilla::net::Cookie>);
[ref] native CookiePtr(mozilla::net::Cookie);
[ref] native CookieParserPtr(mozilla::net::CookieParser);
/**
* @see nsICookieService::runInTransaction
@ -88,12 +91,26 @@ interface nsICookieService : nsISupports
*/
[noscript, notxpcom, nostdcall] void getCookiesFromHost(in ACString aBaseDomain,
in const_OriginAttributes aOriginAttributes,
out Array<CookiePtr> aCookies);
out Array<CookieRefPtr> aCookies);
/* Update the last access to stale cookies */
[noscript, notxpcom, nostdcall] void staleCookies(in Array<CookiePtr> aCookies,
[noscript, notxpcom, nostdcall] void staleCookies(in Array<CookieRefPtr> aCookies,
in int64_t aCurrentTimeInUsec);
/* Return true if there are existing cookies for the host and OriginAttributes. */
[noscript, notxpcom, nostdcall] boolean hasExistingCookies(in ACString aBaseDomain,
in const_OriginAttributes aOriginAttributes);
/* Return true if there are existing cookies for the host and OriginAttributes. */
[noscript, notxpcom, nostdcall] void addCookieFromDocument(in CookieParserPtr aCookieParser,
in ACString aBaseDomain,
in const_OriginAttributes aOriginAttributes,
in CookiePtr aCookie,
in int64_t aCurrentTimeInUsec,
in nsIURI aDocumentURI,
in boolean aThirdParty,
in Document document);
/*
* Get the complete cookie string associated with the URI.
*
@ -112,18 +129,6 @@ interface nsICookieService : nsISupports
*/
ACString getCookieStringFromHttp(in nsIURI aURI, in nsIChannel aChannel);
/*
* Set the cookie string associated with a Document. This method is meant to
* be used for `document.cookie` only. Any security check about
* storage-access permission and cookie behavior must be done by the caller.
*
* @param aDocument
* The document.
* @param aCookie
* the cookie string to set.
*/
void setCookieStringFromDocument(in Document aDocument, in ACString aCookie);
/*
* Set the cookie string and expires associated with the URI.
*

Просмотреть файл

@ -82,9 +82,9 @@ const unpartitionedOAs = createOriginAttributes("");
// Set partitioned and unpartitioned cookie from first-party document.
// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
// This calls CookieServiceChild::SetCookieStringFromDocument() internally.
// CookieService::SetCookieStringFromDocument() is not explicitly tested since
// CHIPS are in the common function CookieCommons::CreateCookieFromDocument().
// This uses CookieServiceChild internally. CookieService is not explicitly
// tested since CHIPS are in the common function
// CookieCommons::CreateCookieFromDocument().
add_task(
async function test_chips_store_partitioned_document_first_party_child() {
const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
@ -126,9 +126,9 @@ add_task(
// Set partitioned and unpartitioned cookie from third-party document with storage
// access. CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
// This calls CookieServiceChild::SetCookieStringFromDocument() internally.
// CookieService::SetCookieStringFromDocument() is not explicitly tested since
// CHIPS are in the common function CookieCommons::CreateCookieFromDocument().
// This uses CookieServiceChild internally. CookieService is not explicitly
// tested since CHIPS are in the common function
// CookieCommons::CreateCookieFromDocument().
add_task(
async function test_chips_store_partitioned_document_third_party_storage_access_child() {
const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);

Просмотреть файл

@ -157,7 +157,7 @@ void GetACookieNoHttp(nsICookieService* aCookieService, const char* aSpec,
nsAutoString cookie;
ErrorResult err;
document->GetCookie(cookie, err);
MOZ_ASSERT(!err.Failed());
EXPECT_TRUE(!err.Failed());
CopyUTF16toUTF8(cookie, aCookie);
}
@ -213,6 +213,9 @@ void InitPrefs(nsIPrefBranch* aPrefBranch) {
Preferences::SetBool("network.cookieJarSettings.unblocked_for_testing", true);
Preferences::SetBool("dom.securecontext.allowlist_onions", false);
Preferences::SetBool("network.cookie.sameSite.schemeful", false);
// Disable a few security checks for document.cookie
Preferences::SetBool("dom.cookie.testing.enabled", true);
}
TEST(TestCookie, TestCookieMain)

Просмотреть файл

@ -234,7 +234,7 @@ add_task(async () => {
await testDomainCookie("http://co.uk/", "co.uk");
// Test that trailing dots are treated differently for purposes of the
// 'domain' attribute when using setCookieStringFromDocument.
// 'domain' attribute.
await testTrailingDotCookie("http://localhost/", "localhost");
await testTrailingDotCookie("http://foo.com/", "foo.com");

Просмотреть файл

@ -4,7 +4,7 @@
// Note that E2E test doesn't seem possible via setting a cookie
// from xpcshell (main process) since:
// 1. setCookieStringFromHttp requires a valid URL
// 2. setCookieStringFromDocument requires access to the document
// 2. document.cookie requires access to the document
// 3. Services.cookies.add() is just a backdoor that will bypass
// Similarly, even with a browser test, in order to call
// content.document.cookie we would need to SpecialPowers.spawn

1
tools/@types/lib.gecko.xpcom.d.ts поставляемый
Просмотреть файл

@ -7708,7 +7708,6 @@ interface nsICookieService extends nsISupports {
readonly BEHAVIOR_LAST: 5;
getCookieStringFromHttp(aURI: nsIURI, aChannel: nsIChannel): string;
setCookieStringFromDocument(aDocument: Document, aCookie: string): void;
setCookieStringFromHttp(aURI: nsIURI, aCookie: string, aChannel: nsIChannel): void;
runInTransaction(aCallback: nsICookieTransactionCallback): void;
}