Bug 1904557 - part 2 - Improve cookie logging - Move the warning/reject logic in CookieParser, r=timhuang,cookie-reviewers,edgul

Differential Revision: https://phabricator.services.mozilla.com/D214807
This commit is contained in:
Andrea Marchesini 2024-06-28 13:19:39 +00:00
Родитель 6c8772e17c
Коммит e10558d5a0
9 изменённых файлов: 475 добавлений и 338 удалений

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

@ -8,14 +8,13 @@
#include "CookieLogging.h"
#include "CookieParser.h"
#include "CookieService.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/ContentBlockingNotifier.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/Telemetry.h"
#include "mozIThirdPartyUtil.h"
#include "nsContentUtils.h"
#include "nsICookiePermission.h"
@ -335,29 +334,20 @@ CookieStatus CookieStatusForWindow(nsPIDOMWindowInner* aWindow,
// static
already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
Document* aDocument, const nsACString& aCookieString,
int64_t currentTimeInUsec, nsIEffectiveTLDService* aTLDService,
mozIThirdPartyUtil* aThirdPartyUtil,
CookieParser& aCookieParser, Document* aDocument,
const nsACString& aCookieString, int64_t currentTimeInUsec,
nsIEffectiveTLDService* aTLDService, mozIThirdPartyUtil* aThirdPartyUtil,
std::function<bool(const nsACString&, const OriginAttributes&)>&&
aHasExistingCookiesLambda,
nsIURI** aDocumentURI, nsACString& aBaseDomain, OriginAttributes& aAttrs) {
nsCOMPtr<nsIURI> principalURI;
auto* basePrincipal = BasePrincipal::Cast(aDocument->NodePrincipal());
basePrincipal->GetURI(getter_AddRefs(principalURI));
if (NS_WARN_IF(!principalURI)) {
// Document's principal is not a content or null (may be system), so
// can't set cookies
return nullptr;
}
if (!CookieCommons::IsSchemeSupported(principalURI)) {
nsACString& aBaseDomain, OriginAttributes& aAttrs) {
if (!CookieCommons::IsSchemeSupported(aCookieParser.HostURI())) {
return nullptr;
}
nsAutoCString baseDomain;
bool requireHostMatch = false;
nsresult rv = CookieCommons::GetBaseDomain(aTLDService, principalURI,
baseDomain, requireHostMatch);
nsresult rv = CookieCommons::GetBaseDomain(
aTLDService, aCookieParser.HostURI(), baseDomain, requireHostMatch);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
@ -369,8 +359,9 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
bool isForeignAndNotAddon = false;
if (!BasePrincipal::Cast(aDocument->NodePrincipal())->AddonPolicy()) {
rv = aThirdPartyUtil->IsThirdPartyWindow(
innerWindow->GetOuterWindow(), principalURI, &isForeignAndNotAddon);
rv = aThirdPartyUtil->IsThirdPartyWindow(innerWindow->GetOuterWindow(),
aCookieParser.HostURI(),
&isForeignAndNotAddon);
if (NS_WARN_IF(NS_FAILED(rv))) {
isForeignAndNotAddon = true;
}
@ -384,34 +375,26 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
// If we are here, we have been already accepted by the anti-tracking.
// We just need to check if we have to be in session-only mode.
CookieStatus cookieStatus = CookieStatusForWindow(innerWindow, principalURI);
CookieStatus cookieStatus =
CookieStatusForWindow(innerWindow, aCookieParser.HostURI());
MOZ_ASSERT(cookieStatus == STATUS_ACCEPTED ||
cookieStatus == STATUS_ACCEPT_SESSION);
// 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); });
nsCString cookieString(aCookieString);
CookieStruct cookieData;
MOZ_ASSERT(cookieData.creationTime() == 0, "Must be initialized to 0");
bool canSetCookie = false;
CookieParser::CanSetCookie(
principalURI, baseDomain, cookieData, requireHostMatch, cookieStatus,
cookieString, false, isForeignAndNotAddon, mustBePartitioned,
aDocument->IsInPrivateBrowsing(), crc, canSetCookie);
aCookieParser.Parse(baseDomain, requireHostMatch, cookieStatus, cookieString,
false, isForeignAndNotAddon, mustBePartitioned,
aDocument->IsInPrivateBrowsing());
if (!canSetCookie) {
if (!aCookieParser.ContainsCookie()) {
return nullptr;
}
// check permissions from site permission list.
if (!CookieCommons::CheckCookiePermission(aDocument->NodePrincipal(),
aDocument->CookieJarSettings(),
cookieData)) {
NotifyRejectionToObservers(principalURI, OPERATION_WRITE);
aCookieParser.CookieData())) {
NotifyRejectionToObservers(aCookieParser.HostURI(), OPERATION_WRITE);
ContentBlockingNotifier::OnDecision(
innerWindow, ContentBlockingNotifier::BlockingDecision::eBlock,
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION);
@ -422,8 +405,8 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
// cookie jar independent of context. If the cookies are stored in the
// partitioned cookie jar anyway no special treatment of CHIPS cookies
// necessary.
bool needPartitioned =
StaticPrefs::network_cookie_CHIPS_enabled() && cookieData.isPartitioned();
bool needPartitioned = StaticPrefs::network_cookie_CHIPS_enabled() &&
aCookieParser.CookieData().isPartitioned();
nsCOMPtr<nsIPrincipal> cookiePrincipal =
needPartitioned ? aDocument->PartitionedPrincipal()
: aDocument->EffectiveCookiePrincipal();
@ -434,12 +417,13 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
if (aDocument->CookieJarSettings()->GetLimitForeignContexts() &&
!aHasExistingCookiesLambda(baseDomain,
cookiePrincipal->OriginAttributesRef()) &&
!ShouldAllowAccessFor(innerWindow, principalURI, &dummyRejectedReason)) {
!ShouldAllowAccessFor(innerWindow, aCookieParser.HostURI(),
&dummyRejectedReason)) {
return nullptr;
}
RefPtr<Cookie> cookie =
Cookie::Create(cookieData, cookiePrincipal->OriginAttributesRef());
RefPtr<Cookie> cookie = Cookie::Create(
aCookieParser.CookieData(), cookiePrincipal->OriginAttributesRef());
MOZ_ASSERT(cookie);
cookie->SetLastAccessed(currentTimeInUsec);
@ -448,7 +432,6 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument(
aBaseDomain = baseDomain;
aAttrs = cookiePrincipal->OriginAttributesRef();
principalURI.forget(aDocumentURI);
return cookie.forget();
}

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

@ -44,6 +44,7 @@ enum CookieStatus {
};
class Cookie;
class CookieParser;
// pref string constants
static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber";
@ -99,12 +100,12 @@ class CookieCommons final {
CookieStruct& aCookieData);
static already_AddRefed<Cookie> CreateCookieFromDocument(
dom::Document* aDocument, const nsACString& aCookieString,
int64_t aCurrentTimeInUsec, nsIEffectiveTLDService* aTLDService,
mozIThirdPartyUtil* aThirdPartyUtil,
CookieParser& aCookieParser, dom::Document* aDocument,
const nsACString& aCookieString, int64_t aCurrentTimeInUsec,
nsIEffectiveTLDService* aTLDService, mozIThirdPartyUtil* aThirdPartyUtil,
std::function<bool(const nsACString&, const OriginAttributes&)>&&
aHasExistingCookiesLambda,
nsIURI** aDocumentURI, nsACString& aBaseDomain, OriginAttributes& aAttrs);
nsACString& aBaseDomain, OriginAttributes& aAttrs);
static already_AddRefed<nsICookieJarSettings> GetCookieJarSettings(
nsIChannel* aChannel);

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

@ -4,12 +4,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CookieParser.h"
#include "CookieLogging.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/Telemetry.h"
#include "nsIConsoleReportCollector.h"
#include "nsIScriptError.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "prprf.h"
constexpr char ATTRIBUTE_PATH[] = "path";
constexpr uint64_t ATTRIBUTE_MAX_LENGTH = 1024;
constexpr auto CONSOLE_CHIPS_CATEGORY = "cookiesCHIPS"_ns;
constexpr auto CONSOLE_OVERSIZE_CATEGORY = "cookiesOversize"_ns;
@ -22,6 +30,180 @@ constexpr auto SAMESITE_MDN_URL =
namespace mozilla {
namespace net {
CookieParser::CookieParser(nsIConsoleReportCollector* aCRC, nsIURI* aHostURI)
: mCRC(aCRC), mHostURI(aHostURI) {
MOZ_COUNT_CTOR(CookieParser);
MOZ_ASSERT(aCRC);
MOZ_ASSERT(aHostURI);
}
CookieParser::~CookieParser() {
MOZ_COUNT_DTOR(CookieParser);
#define COOKIE_LOGGING_WITH_NAME(category, x) \
CookieLogging::LogMessageToConsole( \
mCRC, mHostURI, nsIScriptError::errorFlag, category, x, \
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(mCookieData.name())});
switch (mRejection) {
case NoRejection:
break;
case RejectedInvalidCharAttributes:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharAttributes"_ns);
break;
case RejectedNoneRequiresSecure:
COOKIE_LOGGING_WITH_NAME(CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedNonRequiresSecure2"_ns);
break;
case RejectedPartitionedRequiresSecure:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedPartitionedRequiresSecure"_ns);
break;
case RejectedEmptyNameAndValue:
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY, "CookieRejectedEmptyNameAndValue"_ns,
nsTArray<nsString>());
break;
case RejectedNameValueOversize: {
AutoTArray<nsString, 2> params = {
NS_ConvertUTF8toUTF16(mCookieData.name())};
nsString size;
size.AppendInt(kMaxBytesPerCookie);
params.AppendElement(size);
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag,
CONSOLE_OVERSIZE_CATEGORY, "CookieOversize"_ns, params);
break;
}
case RejectedInvalidCharName:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharName"_ns);
break;
case RejectedInvalidDomain:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidDomain"_ns);
break;
case RejectedInvalidPrefix:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidPrefix"_ns);
break;
case RejectedInvalidCharValue:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharValue"_ns);
break;
case RejectedHttpOnlyButFromScript:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedHttpOnlyButFromScript"_ns);
break;
case RejectedSecureButNonHttps:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedSecureButNonHttps"_ns);
break;
case RejectedForNonSameSiteness:
COOKIE_LOGGING_WITH_NAME(CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedForNonSameSiteness"_ns);
break;
case RejectedForeignNoPartitionedError:
COOKIE_LOGGING_WITH_NAME(CONSOLE_CHIPS_CATEGORY,
"CookieForeignNoPartitionedError"_ns);
break;
case RejectedByPermissionManager:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedByPermissionManager"_ns);
break;
case RejectedNonsecureOverSecure:
COOKIE_LOGGING_WITH_NAME(CONSOLE_REJECTION_CATEGORY,
"CookieRejectedNonsecureOverSecure"_ns);
break;
}
#undef COOKIE_LOGGING_WITH_NAME
if (mRejection != NoRejection || !mContainsCookie) {
return;
}
for (const char* attribute : mWarnings.mAttributeOversize) {
AutoTArray<nsString, 3> params = {NS_ConvertUTF8toUTF16(mCookieData.name()),
NS_ConvertUTF8toUTF16(attribute)};
nsString size;
size.AppendInt(ATTRIBUTE_MAX_LENGTH);
params.AppendElement(size);
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag, CONSOLE_OVERSIZE_CATEGORY,
"CookieAttributeIgnored"_ns, params);
}
for (const char* attribute : mWarnings.mAttributeOverwritten) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag, CONSOLE_OVERSIZE_CATEGORY,
"CookieAttributeOverwritten"_ns,
AutoTArray<nsString, 2>{NS_ConvertUTF8toUTF16(mCookieData.name()),
NS_ConvertUTF8toUTF16(attribute)});
}
if (mWarnings.mInvalidSameSiteAttribute) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::infoFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieSameSiteValueInvalid2"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(mCookieData.name())});
}
if (mWarnings.mSameSiteNoneRequiresSecureForBeta) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedNonRequiresSecureForBeta3"_ns,
AutoTArray<nsString, 2>{NS_ConvertUTF8toUTF16(mCookieData.name()),
SAMESITE_MDN_URL});
}
if (mWarnings.mSameSiteLaxForced) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::infoFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieLaxForced2"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(mCookieData.name())});
}
if (mWarnings.mSameSiteLaxForcedForBeta) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieLaxForcedForBeta2"_ns,
AutoTArray<nsString, 2>{NS_ConvertUTF8toUTF16(mCookieData.name()),
SAMESITE_MDN_URL});
}
if (mWarnings.mForeignNoPartitionedWarning) {
CookieLogging::LogMessageToConsole(
mCRC, mHostURI, nsIScriptError::warningFlag, CONSOLE_CHIPS_CATEGORY,
"CookieForeignNoPartitionedWarning"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(mCookieData.name()),
});
}
}
// clang-format off
// The following comment block elucidates the function of ParseAttributes.
/******************************************************************************
@ -193,36 +375,25 @@ static inline void SetSameSiteAttributeDefault(CookieStruct& aCookieData) {
aCookieData.rawSameSite() = nsICookie::SAMESITE_NONE;
}
bool CheckAttributeSize(nsIConsoleReportCollector* aCRC, nsIURI* aHostURI,
const CookieStruct& aCookieData, const char* aAttribute,
const nsACString& aValue) {
static uint16_t kMaxAttributeLength = 1024;
if (aValue.Length() > kMaxAttributeLength) {
AutoTArray<nsString, 3> params = {NS_ConvertUTF8toUTF16(aCookieData.name()),
NS_ConvertUTF8toUTF16(aAttribute)};
nsString size;
size.AppendInt(kMaxAttributeLength);
params.AppendElement(size);
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_OVERSIZE_CATEGORY,
"CookieAttributeIgnored"_ns, params);
bool CookieParser::CheckAttributeSize(const nsACString& currentValue,
const char* aAttribute,
const nsACString& aValue) {
if (aValue.Length() > ATTRIBUTE_MAX_LENGTH) {
mWarnings.mAttributeOversize.AppendElement(aAttribute);
return false;
}
if (!currentValue.IsEmpty()) {
mWarnings.mAttributeOverwritten.AppendElement(aAttribute);
}
return true;
}
// Parses attributes from cookie header. expires/max-age attributes aren't
// folded into the cookie struct here, because we don't know which one to use
// until we've parsed the header.
// static
bool CookieParser::ParseAttributes(nsIConsoleReportCollector* aCRC,
nsIURI* aHostURI, nsCString& aCookieHeader,
CookieStruct& aCookieData,
bool CookieParser::ParseAttributes(nsCString& aCookieHeader,
nsACString& aExpires, nsACString& aMaxage,
bool& aAcceptedByParser) {
aAcceptedByParser = false;
@ -244,10 +415,10 @@ bool CookieParser::ParseAttributes(nsIConsoleReportCollector* aCRC,
nsACString::const_char_iterator cookieEnd;
aCookieHeader.EndReading(cookieEnd);
aCookieData.isSecure() = false;
aCookieData.isHttpOnly() = false;
mCookieData.isSecure() = false;
mCookieData.isHttpOnly() = false;
SetSameSiteAttributeDefault(aCookieData);
SetSameSiteAttributeDefault(mCookieData);
nsDependentCSubstring tokenString(cookieStart, cookieStart);
nsDependentCSubstring tokenValue(cookieStart, cookieStart);
@ -262,10 +433,10 @@ bool CookieParser::ParseAttributes(nsIConsoleReportCollector* aCRC,
newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue,
equalsFound);
if (equalsFound) {
aCookieData.name() = tokenString;
aCookieData.value() = tokenValue;
mCookieData.name() = tokenString;
mCookieData.value() = tokenValue;
} else {
aCookieData.value() = tokenString;
mCookieData.value() = tokenString;
}
// extract remaining attributes
@ -274,65 +445,55 @@ bool CookieParser::ParseAttributes(nsIConsoleReportCollector* aCRC,
equalsFound);
if (ContainsControlChars(tokenString) || ContainsControlChars(tokenValue)) {
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::errorFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharAttributes"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(aCookieData.name())});
RejectCookie(RejectedInvalidCharAttributes);
return newCookie;
}
// decide which attribute we have, and copy the string
if (tokenString.LowerCaseEqualsLiteral(ATTRIBUTE_PATH)) {
if (CheckAttributeSize(aCRC, aHostURI, aCookieData, ATTRIBUTE_PATH,
tokenValue)) {
aCookieData.path() = tokenValue;
if (CheckAttributeSize(mCookieData.path(), ATTRIBUTE_PATH, tokenValue)) {
mCookieData.path() = tokenValue;
}
} else if (tokenString.LowerCaseEqualsLiteral(kDomain)) {
if (CheckAttributeSize(aCRC, aHostURI, aCookieData, kDomain,
tokenValue)) {
aCookieData.host() = tokenValue;
if (CheckAttributeSize(mCookieData.host(), kDomain, tokenValue)) {
mCookieData.host() = tokenValue;
}
} else if (tokenString.LowerCaseEqualsLiteral(kExpires)) {
if (CheckAttributeSize(aCRC, aHostURI, aCookieData, kExpires,
tokenValue)) {
if (CheckAttributeSize(aExpires, kExpires, tokenValue)) {
aExpires = tokenValue;
}
} else if (tokenString.LowerCaseEqualsLiteral(kMaxage)) {
if (CheckAttributeSize(aCRC, aHostURI, aCookieData, kMaxage,
tokenValue)) {
if (CheckAttributeSize(aMaxage, kMaxage, tokenValue)) {
aMaxage = tokenValue;
}
// ignore any tokenValue for isSecure; just set the boolean
} else if (tokenString.LowerCaseEqualsLiteral(kSecure)) {
aCookieData.isSecure() = true;
mCookieData.isSecure() = true;
// ignore any tokenValue for isPartitioned; just set the boolean
} else if (tokenString.LowerCaseEqualsLiteral(kPartitioned)) {
aCookieData.isPartitioned() = true;
mCookieData.isPartitioned() = true;
// ignore any tokenValue for isHttpOnly (see bug 178993);
// just set the boolean
} else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly)) {
aCookieData.isHttpOnly() = true;
mCookieData.isHttpOnly() = true;
} else if (tokenString.LowerCaseEqualsLiteral(kSameSite)) {
if (tokenValue.LowerCaseEqualsLiteral(kSameSiteLax)) {
SetSameSiteAttribute(aCookieData, nsICookie::SAMESITE_LAX);
SetSameSiteAttribute(mCookieData, nsICookie::SAMESITE_LAX);
} else if (tokenValue.LowerCaseEqualsLiteral(kSameSiteStrict)) {
SetSameSiteAttribute(aCookieData, nsICookie::SAMESITE_STRICT);
SetSameSiteAttribute(mCookieData, nsICookie::SAMESITE_STRICT);
} else if (tokenValue.LowerCaseEqualsLiteral(kSameSiteNone)) {
SetSameSiteAttribute(aCookieData, nsICookie::SAMESITE_NONE);
SetSameSiteAttribute(mCookieData, nsICookie::SAMESITE_NONE);
} else {
// Reset to Default if unknown token value (see Bug 1682450)
SetSameSiteAttributeDefault(aCookieData);
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::infoFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieSameSiteValueInvalid2"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(aCookieData.name())});
SetSameSiteAttributeDefault(mCookieData);
mWarnings.mInvalidSameSiteAttribute = true;
}
}
}
@ -342,60 +503,42 @@ bool CookieParser::ParseAttributes(nsIConsoleReportCollector* aCRC,
// If same-site is explicitly set to 'none' but this is not a secure context,
// let's abort the parsing.
if (!aCookieData.isSecure() &&
aCookieData.sameSite() == nsICookie::SAMESITE_NONE) {
if (!mCookieData.isSecure() &&
mCookieData.sameSite() == nsICookie::SAMESITE_NONE) {
if (StaticPrefs::network_cookie_sameSite_noneRequiresSecure()) {
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::errorFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedNonRequiresSecure2"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(aCookieData.name())});
RejectCookie(RejectedNoneRequiresSecure);
return newCookie;
}
// Still warn about the missing Secure attribute when not enforcing.
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedNonRequiresSecureForBeta3"_ns,
AutoTArray<nsString, 2>{NS_ConvertUTF8toUTF16(aCookieData.name()),
SAMESITE_MDN_URL});
mWarnings.mSameSiteNoneRequiresSecureForBeta = true;
}
// Ensure the partitioned cookie is set with the secure attribute if CHIPS
// is enabled.
if (StaticPrefs::network_cookie_CHIPS_enabled() &&
aCookieData.isPartitioned() && !aCookieData.isSecure()) {
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::errorFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedPartitionedRequiresSecure"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(aCookieData.name())});
mCookieData.isPartitioned() && !mCookieData.isSecure()) {
RejectCookie(RejectedPartitionedRequiresSecure);
return newCookie;
}
if (aCookieData.rawSameSite() == nsICookie::SAMESITE_NONE &&
aCookieData.sameSite() == nsICookie::SAMESITE_LAX) {
if (mCookieData.rawSameSite() == nsICookie::SAMESITE_NONE &&
mCookieData.sameSite() == nsICookie::SAMESITE_LAX) {
bool laxByDefault =
StaticPrefs::network_cookie_sameSite_laxByDefault() &&
!nsContentUtils::IsURIInPrefList(
aHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
mHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
if (laxByDefault) {
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::infoFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieLaxForced2"_ns,
AutoTArray<nsString, 1>{NS_ConvertUTF8toUTF16(aCookieData.name())});
mWarnings.mSameSiteLaxForced = true;
} else {
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag,
CONSOLE_SAMESITE_CATEGORY, "CookieLaxForcedForBeta2"_ns,
AutoTArray<nsString, 2>{NS_ConvertUTF8toUTF16(aCookieData.name()),
SAMESITE_MDN_URL});
mWarnings.mSameSiteLaxForcedForBeta = true;
}
}
// Cookie accepted.
aAcceptedByParser = true;
MOZ_ASSERT(Cookie::ValidateSameSite(aCookieData));
MOZ_ASSERT(Cookie::ValidateSameSite(mCookieData));
return newCookie;
}
@ -432,20 +575,18 @@ nsAutoCString GetPathFromURI(nsIURI* aHostURI) {
} // namespace
bool CookieParser::CheckPath(CookieStruct& aCookieData,
nsIConsoleReportCollector* aCRC,
nsIURI* aHostURI) {
bool CookieParser::CheckPath() {
// if a path is given, check the host has permission
if (aCookieData.path().IsEmpty() || aCookieData.path().First() != '/') {
nsAutoCString path = GetPathFromURI(aHostURI);
if (CheckAttributeSize(aCRC, aHostURI, aCookieData, ATTRIBUTE_PATH, path)) {
aCookieData.path() = path;
if (mCookieData.path().IsEmpty() || mCookieData.path().First() != '/') {
nsAutoCString path = GetPathFromURI(mHostURI);
if (CheckAttributeSize(mCookieData.path(), ATTRIBUTE_PATH, path)) {
mCookieData.path() = path;
}
}
MOZ_ASSERT(CookieCommons::CheckPathSize(aCookieData));
MOZ_ASSERT(CookieCommons::CheckPathSize(mCookieData));
return !aCookieData.path().Contains('\t');
return !mCookieData.path().Contains('\t');
}
// static
@ -655,20 +796,16 @@ static void RecordPartitionedTelemetry(const CookieStruct& aCookieData,
// processes a single cookie, and returns true if there are more cookies
// to be processed
bool CookieParser::CanSetCookie(
nsIURI* aHostURI, const nsACString& aBaseDomain, CookieStruct& aCookieData,
bool aRequireHostMatch, CookieStatus aStatus, nsCString& aCookieHeader,
bool aFromHttp, bool aIsForeignAndNotAddon, bool aPartitionedOnly,
bool aIsInPrivateBrowsing, nsIConsoleReportCollector* aCRC,
bool& aSetCookie) {
MOZ_ASSERT(aHostURI);
aSetCookie = false;
bool CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
CookieStatus aStatus, nsCString& aCookieHeader,
bool aFromHttp, bool aIsForeignAndNotAddon,
bool aPartitionedOnly, bool aIsInPrivateBrowsing) {
MOZ_ASSERT(!mContainsCookie);
// init expiryTime such that session cookies won't prematurely expire
aCookieData.expiry() = INT64_MAX;
mCookieData.expiry() = INT64_MAX;
aCookieData.schemeMap() = CookieCommons::URIToSchemeType(aHostURI);
mCookieData.schemeMap() = CookieCommons::URIToSchemeType(mHostURI);
// aCookieHeader is an in/out param to point to the next cookie, if
// there is one. Save the present value for logging purposes
@ -679,8 +816,8 @@ bool CookieParser::CanSetCookie(
nsAutoCString expires;
nsAutoCString maxage;
bool acceptedByParser = false;
bool newCookie = ParseAttributes(aCRC, aHostURI, aCookieHeader, aCookieData,
expires, maxage, acceptedByParser);
bool newCookie =
ParseAttributes(aCookieHeader, expires, maxage, acceptedByParser);
if (!acceptedByParser) {
return newCookie;
}
@ -693,153 +830,106 @@ bool CookieParser::CanSetCookie(
// 2 = secure and "http:"
// 3 = secure and "https:"
bool potentiallyTrustworthy =
nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI);
nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(mHostURI);
int64_t currentTimeInUsec = PR_Now();
// calculate expiry time of cookie.
aCookieData.isSession() =
GetExpiry(aCookieData, expires, maxage,
mCookieData.isSession() =
GetExpiry(mCookieData, expires, maxage,
currentTimeInUsec / PR_USEC_PER_SEC, aFromHttp);
if (aStatus == STATUS_ACCEPT_SESSION) {
// force lifetime to session. note that the expiration time, if set above,
// will still apply.
aCookieData.isSession() = true;
mCookieData.isSession() = true;
}
// reject cookie if name and value are empty, per RFC6265bis
if (aCookieData.name().IsEmpty() && aCookieData.value().IsEmpty()) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (mCookieData.name().IsEmpty() && mCookieData.value().IsEmpty()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"cookie name and value are empty");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedEmptyNameAndValue"_ns, nsTArray<nsString>());
RejectCookie(RejectedEmptyNameAndValue);
return newCookie;
}
// reject cookie if it's over the size limit, per RFC2109
if (!CookieCommons::CheckNameAndValueSize(aCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CookieCommons::CheckNameAndValueSize(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"cookie too big (> 4kb)");
AutoTArray<nsString, 2> params = {
NS_ConvertUTF8toUTF16(aCookieData.name())};
nsString size;
size.AppendInt(kMaxBytesPerCookie);
params.AppendElement(size);
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_OVERSIZE_CATEGORY,
"CookieOversize"_ns, params);
RejectCookie(RejectedNameValueOversize);
return newCookie;
}
CookieCommons::RecordUnicodeTelemetry(aCookieData);
CookieCommons::RecordUnicodeTelemetry(mCookieData);
// We count SetCookie operations in the parent process only for HTTP set
// cookies to prevent double counting.
if (XRE_IsParentProcess() || !aFromHttp) {
RecordPartitionedTelemetry(aCookieData, aIsForeignAndNotAddon);
RecordPartitionedTelemetry(mCookieData, aIsForeignAndNotAddon);
}
if (!CookieCommons::CheckName(aCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CookieCommons::CheckName(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"invalid name character");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharName"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedInvalidCharName);
return newCookie;
}
// domain & path checks
if (!CheckDomain(aCookieData, aHostURI, aBaseDomain, aRequireHostMatch)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CheckDomain(mCookieData, mHostURI, aBaseDomain, aRequireHostMatch)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"failed the domain tests");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidDomain"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedInvalidDomain);
return newCookie;
}
if (!CheckPath(aCookieData, aCRC, aHostURI)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CheckPath()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"failed the path tests");
return newCookie;
}
// If a cookie is nameless, then its value must not start with
// `__Host-` or `__Secure-`
if (aCookieData.name().IsEmpty() && (HasSecurePrefix(aCookieData.value()) ||
HasHostPrefix(aCookieData.value()))) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (mCookieData.name().IsEmpty() && (HasSecurePrefix(mCookieData.value()) ||
HasHostPrefix(mCookieData.value()))) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"failed hidden prefix tests");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidPrefix"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedInvalidPrefix);
return newCookie;
}
// magic prefix checks. MUST be run after CheckDomain() and CheckPath()
if (!CheckPrefixes(aCookieData, potentiallyTrustworthy)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CheckPrefixes(mCookieData, potentiallyTrustworthy)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"failed the prefix tests");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidPrefix"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedInvalidPrefix);
return newCookie;
}
if (!CookieCommons::CheckValue(aCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!CookieCommons::CheckValue(mCookieData)) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"invalid value character");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedInvalidCharValue"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedInvalidCharValue);
return newCookie;
}
// if the new cookie is httponly, make sure we're not coming from script
if (!aFromHttp && aCookieData.isHttpOnly()) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
if (!aFromHttp && mCookieData.isHttpOnly()) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"cookie is httponly; coming from script");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedHttpOnlyButFromScript"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedHttpOnlyButFromScript);
return newCookie;
}
// If the new cookie is non-https and wants to set secure flag,
// browser have to ignore this new cookie.
// (draft-ietf-httpbis-cookie-alone section 3.1)
if (aCookieData.isSecure() && !potentiallyTrustworthy) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
if (mCookieData.isSecure() && !potentiallyTrustworthy) {
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, aCookieHeader,
"non-https cookie can't set secure flag");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedSecureButNonHttps"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedSecureButNonHttps);
return newCookie;
}
@ -848,20 +938,14 @@ bool CookieParser::CanSetCookie(
bool laxByDefault =
StaticPrefs::network_cookie_sameSite_laxByDefault() &&
!nsContentUtils::IsURIInPrefList(
aHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
mHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
auto effectiveSameSite =
laxByDefault ? aCookieData.sameSite() : aCookieData.rawSameSite();
laxByDefault ? mCookieData.sameSite() : mCookieData.rawSameSite();
if ((effectiveSameSite != nsICookie::SAMESITE_NONE) &&
aIsForeignAndNotAddon) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"failed the samesite tests");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_SAMESITE_CATEGORY,
"CookieRejectedForNonSameSiteness"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedForNonSameSiteness);
return newCookie;
}
@ -869,33 +953,29 @@ bool CookieParser::CanSetCookie(
// but is foreign we should give the developer a message.
// If CHIPS isn't required yet, we will warn the console
// that we have upcoming changes. Otherwise we give a rejection message.
if (aPartitionedOnly && !aCookieData.isPartitioned() &&
if (aPartitionedOnly && !mCookieData.isPartitioned() &&
aIsForeignAndNotAddon) {
if (StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() ||
(aIsInPrivateBrowsing &&
StaticPrefs::
network_cookie_cookieBehavior_optInPartitioning_pbmode())) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
COOKIE_LOGFAILURE(SET_COOKIE, mHostURI, savedCookieHeader,
"foreign cookies must be partitioned");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_CHIPS_CATEGORY,
"CookieForeignNoPartitionedError"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
RejectCookie(RejectedForeignNoPartitionedError);
return newCookie;
}
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_CHIPS_CATEGORY,
"CookieForeignNoPartitionedWarning"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookieData.name()),
});
mWarnings.mForeignNoPartitionedWarning = true;
}
aSetCookie = true;
mContainsCookie = true;
return newCookie;
}
void CookieParser::RejectCookie(Rejection aRejection) {
MOZ_ASSERT(mRejection == NoRejection);
MOZ_ASSERT(aRejection != NoRejection);
mRejection = aRejection;
}
} // namespace net
} // namespace mozilla

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

@ -6,7 +6,11 @@
#ifndef mozilla_net_CookieParser_h
#define mozilla_net_CookieParser_h
#include <nsCOMPtr.h>
#include "CookieCommons.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
class nsIConsoleReportCollector;
class nsIURI;
@ -16,12 +20,48 @@ namespace net {
class CookieParser final {
public:
static bool CanSetCookie(nsIURI* aHostURI, const nsACString& aBaseDomain,
CookieStruct& aCookieData, bool aRequireHostMatch,
CookieStatus aStatus, nsCString& aCookieHeader,
bool aFromHttp, bool aIsForeignAndNotAddon,
bool aPartitionedOnly, bool aIsInPrivateBrowsing,
nsIConsoleReportCollector* aCRC, bool& aSetCookie);
enum Rejection {
// The cookie is OK or not parsed yet.
NoRejection,
RejectedInvalidCharAttributes,
RejectedNoneRequiresSecure,
RejectedPartitionedRequiresSecure,
RejectedEmptyNameAndValue,
RejectedNameValueOversize,
RejectedInvalidCharName,
RejectedInvalidDomain,
RejectedInvalidPrefix,
RejectedInvalidCharValue,
RejectedHttpOnlyButFromScript,
RejectedSecureButNonHttps,
RejectedForNonSameSiteness,
RejectedForeignNoPartitionedError,
RejectedByPermissionManager,
RejectedNonsecureOverSecure,
};
CookieParser(nsIConsoleReportCollector* aCRC, nsIURI* aHostURI);
~CookieParser();
nsIURI* HostURI() const { return mHostURI; }
bool Parse(const nsACString& aBaseDomain, bool aRequireHostMatch,
CookieStatus aStatus, nsCString& aCookieHeader, bool aFromHttp,
bool aIsForeignAndNotAddon, bool aPartitionedOnly,
bool aIsInPrivateBrowsing);
bool ContainsCookie() const {
MOZ_ASSERT_IF(mContainsCookie, mRejection == NoRejection);
return mContainsCookie;
}
void RejectCookie(Rejection aRejection);
CookieStruct& CookieData() {
MOZ_ASSERT(ContainsCookie());
return mCookieData;
}
private:
static bool GetTokenValue(nsACString::const_char_iterator& aIter,
@ -30,23 +70,44 @@ class CookieParser final {
nsDependentCSubstring& aTokenValue,
bool& aEqualsFound);
static bool ParseAttributes(nsIConsoleReportCollector* aCRC, nsIURI* aHostURI,
nsCString& aCookieHeader,
CookieStruct& aCookieData, nsACString& aExpires,
nsACString& aMaxage, bool& aAcceptedByParser);
bool ParseAttributes(nsCString& aCookieHeader, nsACString& aExpires,
nsACString& aMaxage, bool& aAcceptedByParser);
static bool GetExpiry(CookieStruct& aCookieData, const nsACString& aExpires,
const nsACString& aMaxage, int64_t aCurrentTime,
bool aFromHttp);
static bool CheckPath(CookieStruct& aCookieData,
nsIConsoleReportCollector* aCRC, nsIURI* aHostURI);
bool CheckPath();
bool CheckAttributeSize(const nsACString& currentValue,
const char* aAttribute, const nsACString& aValue);
static bool CheckPrefixes(CookieStruct& aCookieData, bool aSecureRequest);
static bool CheckDomain(CookieStruct& aCookieData, nsIURI* aHostURI,
const nsACString& aBaseDomain,
bool aRequireHostMatch);
static bool HasSecurePrefix(const nsACString& aString);
static bool HasHostPrefix(const nsACString& aString);
nsCOMPtr<nsIConsoleReportCollector> mCRC;
nsCOMPtr<nsIURI> mHostURI;
// True if the parsing succeeded.
bool mContainsCookie = false;
Rejection mRejection = NoRejection;
struct Warnings {
nsTArray<const char*> mAttributeOversize;
nsTArray<const char*> mAttributeOverwritten;
bool mInvalidSameSiteAttribute = false;
bool mSameSiteNoneRequiresSecureForBeta = false;
bool mSameSiteLaxForced = false;
bool mSameSiteLaxForcedForBeta = false;
bool mForeignNoPartitionedWarning = false;
} mWarnings;
CookieStruct mCookieData;
};
} // namespace net

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

@ -6,15 +6,16 @@
#include "CookieCommons.h"
#include "CookieLogging.h"
#include "CookieParser.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/ContentBlockingNotifier.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/net/CookiePersistentStorage.h"
#include "mozilla/net/CookiePrivateStorage.h"
@ -632,9 +633,24 @@ CookieService::SetCookieStringFromDocument(Document* aDocument,
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(
aDocument, aCookieString, currentTimeInUsec, mTLDService, mThirdPartyUtil,
hasExistingCookiesLambda, getter_AddRefs(documentURI), baseDomain, attrs);
cookieParser, aDocument, aCookieString, currentTimeInUsec, mTLDService,
mThirdPartyUtil, hasExistingCookiesLambda, baseDomain, attrs);
if (!cookie) {
return NS_OK;
}
@ -657,12 +673,9 @@ CookieService::SetCookieStringFromDocument(Document* aDocument,
return NS_OK;
}
nsCOMPtr<nsIConsoleReportCollector> crc =
do_QueryInterface(aDocument->GetChannel());
// add the cookie to the list. AddCookie() takes care of logging.
PickStorage(attrs)->AddCookie(
crc, baseDomain, attrs, cookie, currentTimeInUsec, documentURI,
&cookieParser, baseDomain, attrs, cookie, currentTimeInUsec, documentURI,
aCookieString, false, thirdParty, aDocument->GetBrowsingContext());
return NS_OK;
}
@ -808,33 +821,27 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI,
// process each cookie in the header
bool moreCookieToRead = true;
while (moreCookieToRead) {
CookieStruct cookieData;
bool canSetCookie = false;
CookieParser cookieParser(crc, aHostURI);
moreCookieToRead = CookieParser::CanSetCookie(
aHostURI, baseDomain, cookieData, requireHostMatch, cookieStatus,
cookieHeader, true, isForeignAndNotAddon, mustBePartitioned,
storagePrincipalOriginAttributes.IsPrivateBrowsing(), crc,
canSetCookie);
moreCookieToRead = cookieParser.Parse(
baseDomain, requireHostMatch, cookieStatus, cookieHeader, true,
isForeignAndNotAddon, mustBePartitioned,
storagePrincipalOriginAttributes.IsPrivateBrowsing());
if (!canSetCookie) {
if (!cookieParser.ContainsCookie()) {
continue;
}
// check permissions from site permission list.
if (!CookieCommons::CheckCookiePermission(aChannel, cookieData)) {
if (!CookieCommons::CheckCookiePermission(aChannel,
cookieParser.CookieData())) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
"cookie rejected by permission manager");
CookieCommons::NotifyRejected(
aHostURI, aChannel,
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION,
OPERATION_WRITE);
CookieLogging::LogMessageToConsole(
crc, aHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY, "CookieRejectedByPermissionManager"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(cookieData.name()),
});
cookieParser.RejectCookie(CookieParser::RejectedByPermissionManager);
continue;
}
@ -842,8 +849,9 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI,
// cookie jar independent of context. If the cookies are stored in the
// partitioned cookie jar anyway no special treatment of CHIPS cookies
// necessary.
bool needPartitioned =
isCHIPS && cookieData.isPartitioned() && !isPartitionedPrincipal;
bool needPartitioned = isCHIPS &&
cookieParser.CookieData().isPartitioned() &&
!isPartitionedPrincipal;
OriginAttributes& cookieOriginAttributes =
needPartitioned ? partitionedPrincipalOriginAttributes
: storagePrincipalOriginAttributes;
@ -853,7 +861,8 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI,
!partitionedPrincipalOriginAttributes.mPartitionKey.IsEmpty());
// create a new Cookie
RefPtr<Cookie> cookie = Cookie::Create(cookieData, cookieOriginAttributes);
RefPtr<Cookie> cookie =
Cookie::Create(cookieParser.CookieData(), cookieOriginAttributes);
MOZ_ASSERT(cookie);
int64_t currentTimeInUsec = PR_Now();
@ -865,8 +874,8 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI,
RefPtr<BrowsingContext> bc = loadInfo->GetTargetBrowsingContext();
// add the cookie to the list. AddCookie() takes care of logging.
storage->AddCookie(crc, baseDomain, cookieOriginAttributes, cookie,
currentTimeInUsec, aHostURI, aCookieHeader, true,
storage->AddCookie(&cookieParser, baseDomain, cookieOriginAttributes,
cookie, currentTimeInUsec, aHostURI, aCookieHeader, true,
isForeignAndNotAddon, bc);
}

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

@ -15,6 +15,7 @@
#include "mozilla/LoadInfo.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Document.h"
#include "mozilla/ipc/URIUtils.h"
@ -34,7 +35,6 @@
#include "nsIWebProgressListener.h"
#include "nsQueryObject.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "ThirdPartyUtil.h"
#include "nsIConsoleReportCollector.h"
@ -501,9 +501,24 @@ CookieServiceChild::SetCookieStringFromDocument(
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(
aDocument, aCookieString, PR_Now(), mTLDService, mThirdPartyUtil,
hasExistingCookiesLambda, getter_AddRefs(documentURI), baseDomain, attrs);
cookieParser, aDocument, aCookieString, PR_Now(), mTLDService,
mThirdPartyUtil, hasExistingCookiesLambda, baseDomain, attrs);
if (!cookie) {
return NS_OK;
}
@ -696,28 +711,20 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
nsTArray<CookieStruct> cookiesToSend, partitionedCookiesToSend;
bool moreCookies;
do {
CookieStruct cookieData;
bool canSetCookie = false;
moreCookies = CookieParser::CanSetCookie(
aHostURI, baseDomain, cookieData, requireHostMatch, cookieStatus,
cookieString, true, isForeignAndNotAddon, mustBePartitioned,
storagePrincipalOriginAttributes.IsPrivateBrowsing(), crc,
canSetCookie);
if (!canSetCookie) {
CookieParser parser(crc, aHostURI);
moreCookies =
parser.Parse(baseDomain, requireHostMatch, cookieStatus, cookieString,
true, isForeignAndNotAddon, mustBePartitioned,
storagePrincipalOriginAttributes.IsPrivateBrowsing());
if (!parser.ContainsCookie()) {
continue;
}
// check permissions from site permission list.
if (!CookieCommons::CheckCookiePermission(aChannel, cookieData)) {
if (!CookieCommons::CheckCookiePermission(aChannel, parser.CookieData())) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieString,
"cookie rejected by permission manager");
constexpr auto CONSOLE_REJECTION_CATEGORY = "cookiesRejection"_ns;
CookieLogging::LogMessageToConsole(
crc, aHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY, "CookieRejectedByPermissionManager"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(cookieData.name()),
});
parser.RejectCookie(CookieParser::RejectedByPermissionManager);
CookieCommons::NotifyRejected(
aHostURI, aChannel,
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION,
@ -729,8 +736,8 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
// cookie jar independent of context. If the cookies are stored in the
// partitioned cookie jar anyway no special treatment of CHIPS cookies
// necessary.
bool needPartitioned =
isCHIPS && cookieData.isPartitioned() && !isPartitionedPrincipal;
bool needPartitioned = isCHIPS && parser.CookieData().isPartitioned() &&
!isPartitionedPrincipal;
nsTArray<CookieStruct>& cookiesToSendRef =
needPartitioned ? partitionedCookiesToSend : cookiesToSend;
OriginAttributes& cookieOriginAttributes =
@ -741,7 +748,8 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
needPartitioned,
!partitionedPrincipalOriginAttributes.mPartitionKey.IsEmpty());
RefPtr<Cookie> cookie = Cookie::Create(cookieData, cookieOriginAttributes);
RefPtr<Cookie> cookie =
Cookie::Create(parser.CookieData(), cookieOriginAttributes);
MOZ_ASSERT(cookie);
cookie->SetLastAccessed(currentTimeInUsec);
@ -749,7 +757,7 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
Cookie::GenerateUniqueCreationTime(currentTimeInUsec));
RecordDocumentCookie(cookie, cookieOriginAttributes);
cookiesToSendRef.AppendElement(cookieData);
cookiesToSendRef.AppendElement(parser.CookieData());
} while (moreCookies);
// Asynchronously call the parent.

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

@ -6,6 +6,7 @@
#include "Cookie.h"
#include "CookieCommons.h"
#include "CookieLogging.h"
#include "CookieParser.h"
#include "CookieNotification.h"
#include "mozilla/net/MozURL_ffi.h"
#include "nsCOMPtr.h"
@ -498,7 +499,7 @@ void CookieStorage::NotifyChanged(nsISupports* aSubject,
// replaces an existing cookie; or adds the cookie to the hashtable, and
// deletes a cookie (if maximum number of cookies has been reached). also
// performs list maintenance by removing expired cookies.
void CookieStorage::AddCookie(nsIConsoleReportCollector* aCRC,
void CookieStorage::AddCookie(CookieParser* aCookieParser,
const nsACString& aBaseDomain,
const OriginAttributes& aOriginAttributes,
Cookie* aCookie, int64_t aCurrentTimeInUsec,
@ -517,7 +518,6 @@ void CookieStorage::AddCookie(nsIConsoleReportCollector* aCRC,
potentiallyTrustworthy =
nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI);
}
constexpr auto CONSOLE_REJECTION_CATEGORY = "cookiesRejection"_ns;
bool oldCookieIsSession = false;
// Step1, call FindSecureCookie(). FindSecureCookie() would
// find the existing cookie with the security flag and has
@ -536,12 +536,9 @@ void CookieStorage::AddCookie(nsIConsoleReportCollector* aCRC,
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
"cookie can't save because older cookie is secure "
"cookie but newer cookie is non-secure cookie");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY,
"CookieRejectedNonsecureOverSecure"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookie->Name()),
});
if (aCookieParser) {
aCookieParser->RejectCookie(CookieParser::RejectedNonsecureOverSecure);
}
return;
}
@ -581,13 +578,10 @@ void CookieStorage::AddCookie(nsIConsoleReportCollector* aCRC,
COOKIE_LOGFAILURE(
SET_COOKIE, aHostURI, aCookieHeader,
"previously stored cookie is httponly; coming from script");
CookieLogging::LogMessageToConsole(
aCRC, aHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY,
"CookieRejectedHttpOnlyButFromScript"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(aCookie->Name()),
});
if (aCookieParser) {
aCookieParser->RejectCookie(
CookieParser::RejectedHttpOnlyButFromScript);
}
return;
}

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

@ -16,7 +16,6 @@
#include "CookieCommons.h"
class nsIArray;
class nsIConsoleReportCollector;
class nsICookie;
class nsICookieTransactionCallback;
class nsIPrefBranch;
@ -25,6 +24,7 @@ namespace mozilla {
namespace net {
class Cookie;
class CookieParser;
// Inherit from CookieKey so this can be stored in nsTHashTable
// TODO: why aren't we using nsClassHashTable<CookieKey, ArrayType>?
@ -123,7 +123,7 @@ class CookieStorage : public nsIObserver, public nsSupportsWeakReference {
dom::BrowsingContext* aBrowsingContext = nullptr,
bool aOldCookieIsSession = false);
void AddCookie(nsIConsoleReportCollector* aCRC, const nsACString& aBaseDomain,
void AddCookie(CookieParser* aCookieParser, const nsACString& aBaseDomain,
const OriginAttributes& aOriginAttributes, Cookie* aCookie,
int64_t aCurrentTimeInUsec, nsIURI* aHostURI,
const nsACString& aCookieHeader, bool aFromHttp,

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

@ -91,6 +91,7 @@ CookieRejectedForNonSameSiteness=Cookie “%1$S” has been rejected because it
CookieRejectedPartitionedRequiresSecure=Cookie “%1$S” has been rejected because it has the “Partitioned” attribute but is missing the “secure” attribute.
# LOCALIZATION NOTE (CookieAttributeIgnored): %1$S is the cookie name. %2$S is the attribute name. %3$S is the number of bytes. "B" means bytes.
CookieAttributeIgnored=The value of the attribute “%2$S” for the cookie “%1$S” has been rejected because its size is too big. Max size is %3$S B.
CookieAttributeOverwritten=The value of the attribute “%2$S” for the cookie “%1$S” has been overwritten.
# LOCALIZATION NOTE (CookieForeignNoPartitionedWarning): %1$S is the cookie name. Do not translate "Partitioned"
CookieForeignNoPartitionedWarning=Cookie “%1$S” will soon be rejected because it is foreign and does not have the “Partitioned“ attribute.