diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-06 12:48:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:33:43 +0000 |
commit | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (patch) | |
tree | fa14ba0ca8d2683ba2efdabd246dc9b18a1229c6 /chromium/net/cookies | |
parent | 79b4f909db1049fca459c07cca55af56a9b54fe3 (diff) | |
download | qtwebengine-chromium-7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3.tar.gz |
BASELINE: Update Chromium to 84.0.4147.141
Change-Id: Ib85eb4cfa1cbe2b2b81e5022c8cad5c493969535
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/cookies')
25 files changed, 2269 insertions, 1071 deletions
diff --git a/chromium/net/cookies/canonical_cookie.cc b/chromium/net/cookies/canonical_cookie.cc index fb17243ed6a..e87937435d9 100644 --- a/chromium/net/cookies/canonical_cookie.cc +++ b/chromium/net/cookies/canonical_cookie.cc @@ -115,20 +115,99 @@ uint32_t GetWarningBitmask( return 1u << static_cast<uint32_t>(reason); } +// Captures Strict -> Lax context downgrade with Strict cookie +bool IsBreakingStrictToLaxDowngrade( + CookieOptions::SameSiteCookieContext::ContextType context, + CookieOptions::SameSiteCookieContext::ContextType schemeful_context, + CookieEffectiveSameSite effective_same_site, + bool is_cookie_being_set) { + if (context == + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT && + schemeful_context == + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX && + effective_same_site == CookieEffectiveSameSite::STRICT_MODE) { + // This downgrade only applies when a SameSite=Strict cookie is being sent. + // A Strict -> Lax downgrade will not affect a Strict cookie which is being + // set because it will be set in either context. + return !is_cookie_being_set; + } + + return false; +} + +// Captures Strict -> Cross-site context downgrade with {Strict, Lax} cookie +// Captures Strict -> Lax Unsafe context downgrade with {Strict, Lax} cookie. +// This is treated as a cross-site downgrade due to the Lax Unsafe context +// behaving like cross-site. +bool IsBreakingStrictToCrossDowngrade( + CookieOptions::SameSiteCookieContext::ContextType context, + CookieOptions::SameSiteCookieContext::ContextType schemeful_context, + CookieEffectiveSameSite effective_same_site) { + bool breaking_schemeful_context = + schemeful_context == + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE || + schemeful_context == CookieOptions::SameSiteCookieContext::ContextType:: + SAME_SITE_LAX_METHOD_UNSAFE; + + bool strict_lax_enforcement = + effective_same_site == CookieEffectiveSameSite::STRICT_MODE || + effective_same_site == CookieEffectiveSameSite::LAX_MODE || + // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of + // our SameSite enforcement check. + effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE; + + if (context == + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT && + breaking_schemeful_context && strict_lax_enforcement) { + return true; + } + + return false; +} + +// Captures Lax -> Cross context downgrade with {Strict, Lax} cookies. +// Ignores Lax Unsafe context. +bool IsBreakingLaxToCrossDowngrade( + CookieOptions::SameSiteCookieContext::ContextType context, + CookieOptions::SameSiteCookieContext::ContextType schemeful_context, + CookieEffectiveSameSite effective_same_site, + bool is_cookie_being_set) { + bool lax_enforcement = + effective_same_site == CookieEffectiveSameSite::LAX_MODE || + // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of + // our SameSite enforcement check. + effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE; + + if (context == + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX && + schemeful_context == + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE) { + // For SameSite=Strict cookies this downgrade only applies when it is being + // set. A Lax -> Cross downgrade will not affect a Strict cookie which is + // being sent because it wouldn't be sent in either context. + return effective_same_site == CookieEffectiveSameSite::STRICT_MODE + ? is_cookie_being_set + : lax_enforcement; + } + + return false; +} + void ApplySameSiteCookieWarningToStatus( CookieSameSite samesite, CookieEffectiveSameSite effective_samesite, bool is_secure, CookieOptions::SameSiteCookieContext same_site_context, - CanonicalCookie::CookieInclusionStatus* status) { + CanonicalCookie::CookieInclusionStatus* status, + bool is_cookie_being_set) { if (samesite == CookieSameSite::UNSPECIFIED && - same_site_context.context < + same_site_context.GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) { status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT); } if (effective_samesite == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE && - same_site_context.context == + same_site_context.GetContextForCookieInclusion() == CookieOptions::SameSiteCookieContext::ContextType:: SAME_SITE_LAX_METHOD_UNSAFE) { // This warning is more specific so remove the previous, more general, @@ -143,34 +222,49 @@ void ApplySameSiteCookieWarningToStatus( status->AddWarningReason( CanonicalCookie::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE); } + + // Add a warning if the cookie would be accessible in + // |same_site_context|::context but not in + // |same_site_context|::schemeful_context. + if (IsBreakingStrictToLaxDowngrade(same_site_context.context(), + same_site_context.schemeful_context(), + effective_samesite, is_cookie_being_set)) { + status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE); + } else if (IsBreakingStrictToCrossDowngrade( + same_site_context.context(), + same_site_context.schemeful_context(), effective_samesite)) { + // Which warning to apply depends on the SameSite value. + if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) { + status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE); + } else { + // LAX_MODE or LAX_MODE_ALLOW_UNSAFE. + status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE); + } + + } else if (IsBreakingLaxToCrossDowngrade( + same_site_context.context(), + same_site_context.schemeful_context(), effective_samesite, + is_cookie_being_set)) { + // Which warning to apply depends on the SameSite value. + if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) { + status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE); + } else { + // LAX_MODE or LAX_MODE_ALLOW_UNSAFE. + // This warning applies to both set/send. + status->AddWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE); + } + } + // If there are reasons to exclude the cookie other than the new SameSite // rules, don't warn about the cookie at all. status->MaybeClearSameSiteWarning(); } -// This function is used to indicate if the same-site context of a cookie should -// recorded for the histograms SameSiteDifferentSchemeRequest and -// SameSiteDifferentSchemeResponse. It returns true if the context is -// cross-scheme but not cross-site and there is an effective same-site. It -// should be removed when the histrograms are removed. -// TODO(https://crbug.com/1066231) -bool ShouldLogCrossSchemeForHistograms( - const CookieOptions::SameSiteCookieContext& context, - const CookieEffectiveSameSite effective_same_site) { - bool correct_context = - context.cross_schemeness != - CookieOptions::SameSiteCookieContext::CrossSchemeness::NONE && - context.context != - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE; - - bool correct_effective_same_site = - effective_same_site == CookieEffectiveSameSite::LAX_MODE || - effective_same_site == CookieEffectiveSameSite::STRICT_MODE || - effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE; - - return correct_context && correct_effective_same_site; -} - } // namespace // Keep defaults here in sync with content/public/common/cookie_manager.mojom. @@ -421,11 +515,32 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie( return cc; } +std::string CanonicalCookie::DomainWithoutDot() const { + return cookie_util::CookieDomainAsHost(domain_); +} + bool CanonicalCookie::IsEquivalentForSecureCookieMatching( - const CanonicalCookie& ecc) const { - return (name_ == ecc.Name() && (ecc.IsDomainMatch(DomainWithoutDot()) || - IsDomainMatch(ecc.DomainWithoutDot())) && - ecc.IsOnPath(Path())); + const CanonicalCookie& secure_cookie) const { + // Names must be the same + bool same_name = name_ == secure_cookie.Name(); + + // They should domain-match in one direction or the other. (See RFC 6265bis + // section 5.1.3.) + // TODO(chlily): This does not check for the IP address case. This is bad due + // to https://crbug.com/1069935. + bool domain_match = + IsSubdomainOf(DomainWithoutDot(), secure_cookie.DomainWithoutDot()) || + IsSubdomainOf(secure_cookie.DomainWithoutDot(), DomainWithoutDot()); + + bool path_match = secure_cookie.IsOnPath(Path()); + + bool equivalent_for_secure_cookie_matching = + same_name && domain_match && path_match; + + // IsEquivalent() is a stricter check than this. + DCHECK(!IsEquivalent(secure_cookie) || equivalent_for_secure_cookie_matching); + + return equivalent_for_secure_cookie_matching; } bool CanonicalCookie::IsOnPath(const std::string& url_path) const { @@ -498,24 +613,24 @@ CanonicalCookie::CookieInclusionStatus CanonicalCookie::IncludeForRequestURL( } UMA_HISTOGRAM_ENUMERATION( "Cookie.RequestSameSiteContext", - options.same_site_cookie_context().context, + options.same_site_cookie_context().GetContextForCookieInclusion(), CookieOptions::SameSiteCookieContext::ContextType::COUNT); switch (effective_same_site) { case CookieEffectiveSameSite::STRICT_MODE: - if (options.same_site_cookie_context().context < + if (options.same_site_cookie_context().GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT) { status.AddExclusionReason( CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT); } break; case CookieEffectiveSameSite::LAX_MODE: - if (options.same_site_cookie_context().context < + if (options.same_site_cookie_context().GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) { // Log metrics for a cookie that would have been included under the // "Lax-allow-unsafe" intervention, had it been new enough. if (SameSite() == CookieSameSite::UNSPECIFIED && - options.same_site_cookie_context().context == + options.same_site_cookie_context().GetContextForCookieInclusion() == CookieOptions::SameSiteCookieContext::ContextType:: SAME_SITE_LAX_METHOD_UNSAFE) { UMA_HISTOGRAM_CUSTOM_TIMES( @@ -533,13 +648,14 @@ CanonicalCookie::CookieInclusionStatus CanonicalCookie::IncludeForRequestURL( // TODO(crbug.com/990439): Add a browsertest for this behavior. case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE: DCHECK(SameSite() == CookieSameSite::UNSPECIFIED); - if (options.same_site_cookie_context().context < + if (options.same_site_cookie_context().GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType:: SAME_SITE_LAX_METHOD_UNSAFE) { // TODO(chlily): Do we need a separate CookieInclusionStatus for this? status.AddExclusionReason( CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX); - } else if (options.same_site_cookie_context().context == + } else if (options.same_site_cookie_context() + .GetContextForCookieInclusion() == CookieOptions::SameSiteCookieContext::ContextType:: SAME_SITE_LAX_METHOD_UNSAFE) { // Log metrics for cookies that activate the "Lax-allow-unsafe" @@ -567,25 +683,20 @@ CanonicalCookie::CookieInclusionStatus CanonicalCookie::IncludeForRequestURL( // TODO(chlily): Apply warning if SameSite-by-default is enabled but // access_semantics is LEGACY? - ApplySameSiteCookieWarningToStatus( - SameSite(), effective_same_site, IsSecure(), - options.same_site_cookie_context(), &status); + ApplySameSiteCookieWarningToStatus(SameSite(), effective_same_site, + IsSecure(), + options.same_site_cookie_context(), + &status, false /* is_cookie_being_set */); if (status.IsInclude()) { UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedRequestEffectiveSameSite", effective_same_site, CookieEffectiveSameSite::COUNT); + } - if (ShouldLogCrossSchemeForHistograms(options.same_site_cookie_context(), - effective_same_site)) { - // TODO(https://crbug.com/1066231) - UMA_HISTOGRAM_ENUMERATION( - "Cookie.SameSiteDifferentSchemeRequest", - options.same_site_cookie_context().ConvertToMetricsValue(), - CookieOptions::SameSiteCookieContext::MetricCount()); - AddSameSiteCrossSchemeWarning(&status, - options.same_site_cookie_context()); - } + if (status.ShouldRecordDowngradeMetrics()) { + UMA_HISTOGRAM_ENUMERATION("Cookie.SameSiteContextDowngradeRequest", + status.GetBreakingDowngradeMetricsEnumValue(url)); } // TODO(chlily): Log metrics. @@ -633,7 +744,7 @@ void CanonicalCookie::IsSetPermittedInContext( // This intentionally checks for `< SAME_SITE_LAX`, as we allow // `SameSite=Strict` cookies to be set for top-level navigations that // qualify for receipt of `SameSite=Lax` cookies. - if (options.same_site_cookie_context().context < + if (options.same_site_cookie_context().GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) { DVLOG(net::cookie_util::kVlogSetCookies) << "Trying to set a `SameSite=Strict` cookie from a " @@ -644,7 +755,7 @@ void CanonicalCookie::IsSetPermittedInContext( break; case CookieEffectiveSameSite::LAX_MODE: case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE: - if (options.same_site_cookie_context().context < + if (options.same_site_cookie_context().GetContextForCookieInclusion() < CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) { if (SameSite() == CookieSameSite::UNSPECIFIED) { DVLOG(net::cookie_util::kVlogSetCookies) @@ -665,26 +776,15 @@ void CanonicalCookie::IsSetPermittedInContext( break; } - ApplySameSiteCookieWarningToStatus( - SameSite(), effective_same_site, IsSecure(), - options.same_site_cookie_context(), status); + ApplySameSiteCookieWarningToStatus(SameSite(), effective_same_site, + IsSecure(), + options.same_site_cookie_context(), status, + true /* is_cookie_being_set */); if (status->IsInclude()) { UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedResponseEffectiveSameSite", effective_same_site, CookieEffectiveSameSite::COUNT); - - if (ShouldLogCrossSchemeForHistograms(options.same_site_cookie_context(), - effective_same_site)) { - // TODO(crbug.com/1034014): Change enum to one with less confusing - // phrasing. - // TODO(https://crbug.com/1066231) - UMA_HISTOGRAM_ENUMERATION( - "Cookie.SameSiteDifferentSchemeResponse", - options.same_site_cookie_context().ConvertToMetricsValue(), - CookieOptions::SameSiteCookieContext::MetricCount()); - AddSameSiteCrossSchemeWarning(status, options.same_site_cookie_context()); - } } // TODO(chlily): Log metrics. @@ -776,55 +876,6 @@ std::string CanonicalCookie::BuildCookieLine( return cookie_line; } -void net::CanonicalCookie::AddSameSiteCrossSchemeWarning( - CookieInclusionStatus* status, - CookieOptions::SameSiteCookieContext same_site_context) const { - if (same_site_context.cross_schemeness == - CookieOptions::SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE) { - switch (same_site_context.context) { - case CookieOptions::SameSiteCookieContext::ContextType:: - SAME_SITE_LAX_METHOD_UNSAFE: - status->AddWarningReason( - CookieInclusionStatus:: - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL); - break; - case CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX: - status->AddWarningReason( - CookieInclusionStatus::WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL); - break; - case CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT: - status->AddWarningReason( - CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL); - break; - default: - break; - } - } else if (same_site_context.cross_schemeness == - CookieOptions::SameSiteCookieContext::CrossSchemeness:: - SECURE_INSECURE) { - switch (same_site_context.context) { - case CookieOptions::SameSiteCookieContext::ContextType:: - SAME_SITE_LAX_METHOD_UNSAFE: - status->AddWarningReason( - CookieInclusionStatus:: - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL); - break; - case CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX: - status->AddWarningReason( - CookieInclusionStatus::WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL); - break; - case CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT: - status->AddWarningReason( - CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL); - break; - default: - break; - } - } -} - // static CanonicalCookie::CookiePrefix CanonicalCookie::GetCookiePrefix( const std::string& name) { @@ -922,12 +973,6 @@ bool CanonicalCookie::IsRecentlyCreated(base::TimeDelta age_threshold) const { return (base::Time::Now() - creation_date_) <= age_threshold; } -std::string CanonicalCookie::DomainWithoutDot() const { - if (domain_.empty() || domain_[0] != '.') - return domain_; - return domain_.substr(1); -} - CanonicalCookie::CookieInclusionStatus::CookieInclusionStatus() : exclusion_reasons_(0u), warning_reasons_(0u) {} @@ -986,6 +1031,33 @@ void CanonicalCookie::CookieInclusionStatus::MaybeClearSameSiteWarning() { RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE); } + + uint32_t context_reasons_mask = + GetExclusionBitmask(EXCLUDE_SAMESITE_STRICT) | + GetExclusionBitmask(EXCLUDE_SAMESITE_LAX) | + GetExclusionBitmask(EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX); + if (exclusion_reasons_ & ~context_reasons_mask) { + RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE); + RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE); + RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE); + RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE); + RemoveWarningReason(CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE); + } +} + +bool CanonicalCookie::CookieInclusionStatus::ShouldRecordDowngradeMetrics() + const { + uint32_t context_reasons_mask = + GetExclusionBitmask(EXCLUDE_SAMESITE_STRICT) | + GetExclusionBitmask(EXCLUDE_SAMESITE_LAX) | + GetExclusionBitmask(EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX); + + return (exclusion_reasons_ & ~context_reasons_mask) == 0u; } bool CanonicalCookie::CookieInclusionStatus::ShouldWarn() const { @@ -997,24 +1069,27 @@ bool CanonicalCookie::CookieInclusionStatus::HasWarningReason( return warning_reasons_ & GetWarningBitmask(reason); } -bool net::CanonicalCookie::CookieInclusionStatus::HasCrossSchemeWarning( +bool net::CanonicalCookie::CookieInclusionStatus::HasDowngradeWarning( CookieInclusionStatus::WarningReason* reason) const { if (!ShouldWarn()) return false; - const CookieInclusionStatus::WarningReason cross_scheme_warnings[] = { - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL, - WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL, - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL, - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL, - WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL, - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL}; - for (const auto warning : cross_scheme_warnings) { - if (HasWarningReason(warning)) { - if (reason) - *reason = warning; - return true; - } + const CookieInclusionStatus::WarningReason kDowngradeWarnings[] = { + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE, + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE, + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE, + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE, + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE, + }; + + for (auto warning : kDowngradeWarnings) { + if (!HasWarningReason(warning)) + continue; + + if (reason) + *reason = warning; + + return true; } return false; @@ -1030,6 +1105,46 @@ void CanonicalCookie::CookieInclusionStatus::RemoveWarningReason( warning_reasons_ &= ~(GetWarningBitmask(reason)); } +CanonicalCookie::CookieInclusionStatus::ContextDowngradeMetricValues +CanonicalCookie::CookieInclusionStatus::GetBreakingDowngradeMetricsEnumValue( + const GURL& url) const { + bool url_is_secure = url.SchemeIsCryptographic(); + + // Start the |reason| as something other than the downgrade warnings. + WarningReason reason = WarningReason::NUM_WARNING_REASONS; + + // Don't bother checking the return value because the default switch case + // will handle if no reason was found. + HasDowngradeWarning(&reason); + + switch (reason) { + case WarningReason::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE: + return url_is_secure + ? ContextDowngradeMetricValues::STRICT_LAX_STRICT_SECURE + : ContextDowngradeMetricValues::STRICT_LAX_STRICT_INSECURE; + case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE: + return url_is_secure + ? ContextDowngradeMetricValues::STRICT_CROSS_STRICT_SECURE + : ContextDowngradeMetricValues::STRICT_CROSS_STRICT_INSECURE; + case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE: + return url_is_secure + ? ContextDowngradeMetricValues::STRICT_CROSS_LAX_SECURE + : ContextDowngradeMetricValues::STRICT_CROSS_LAX_INSECURE; + case WarningReason::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE: + return url_is_secure + ? ContextDowngradeMetricValues::LAX_CROSS_STRICT_SECURE + : ContextDowngradeMetricValues::LAX_CROSS_STRICT_INSECURE; + case WarningReason::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE: + return url_is_secure + ? ContextDowngradeMetricValues::LAX_CROSS_LAX_SECURE + : ContextDowngradeMetricValues::LAX_CROSS_LAX_INSECURE; + default: + return url_is_secure + ? ContextDowngradeMetricValues::NO_DOWNGRADE_SECURE + : ContextDowngradeMetricValues::NO_DOWNGRADE_INSECURE; + } +} + std::string CanonicalCookie::CookieInclusionStatus::GetDebugString() const { std::string out; @@ -1081,21 +1196,16 @@ std::string CanonicalCookie::CookieInclusionStatus::GetDebugString() const { base::StrAppend(&out, {"WARN_SAMESITE_NONE_INSECURE, "}); if (HasWarningReason(WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE)) base::StrAppend(&out, {"WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE, "}); - if (HasWarningReason(WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL)) - base::StrAppend( - &out, {"WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL, "}); - if (HasWarningReason(WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL)) - base::StrAppend(&out, {"WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL, "}); - if (HasWarningReason(WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL)) - base::StrAppend(&out, {"WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL, "}); - if (HasWarningReason( - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL)) - base::StrAppend( - &out, {"WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL, "}); - if (HasWarningReason(WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL)) - base::StrAppend(&out, {"WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL, "}); - if (HasWarningReason(WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL)) - base::StrAppend(&out, {"WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL, "}); + if (HasWarningReason(WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE)) + base::StrAppend(&out, {"WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE, "}); + if (HasWarningReason(WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE)) + base::StrAppend(&out, {"WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE, "}); + if (HasWarningReason(WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE)) + base::StrAppend(&out, {"WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE, "}); + if (HasWarningReason(WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE)) + base::StrAppend(&out, {"WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE, "}); + if (HasWarningReason(WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE)) + base::StrAppend(&out, {"WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE, "}); // Strip trailing comma and space. out.erase(out.end() - 2, out.end()); diff --git a/chromium/net/cookies/canonical_cookie.h b/chromium/net/cookies/canonical_cookie.h index 8bd8b2eaccb..10b66c661b2 100644 --- a/chromium/net/cookies/canonical_cookie.h +++ b/chromium/net/cookies/canonical_cookie.h @@ -106,6 +106,10 @@ class NET_EXPORT CanonicalCookie { const std::string& Name() const { return name_; } const std::string& Value() const { return value_; } + // We represent the cookie's host-only-flag as the absence of a leading dot in + // Domain(). See IsDomainCookie() and IsHostCookie() below. + // If you want the "cookie's domain" as described in RFC 6265bis, use + // DomainWithoutDot(). const std::string& Domain() const { return domain_; } const std::string& Path() const { return path_; } const base::Time& CreationDate() const { return creation_date_; } @@ -124,6 +128,10 @@ class NET_EXPORT CanonicalCookie { return !domain_.empty() && domain_[0] == '.'; } bool IsHostCookie() const { return !IsDomainCookie(); } + // Returns the cookie's domain, with the leading dot removed, if present. + // This corresponds to the "cookie's domain" as described in RFC 6265bis. + std::string DomainWithoutDot() const; + bool IsExpired(const base::Time& current) const { return !expiry_date_.is_null() && current >= expiry_date_; } @@ -137,7 +145,7 @@ class NET_EXPORT CanonicalCookie { bool IsEquivalent(const CanonicalCookie& ecc) const { // It seems like it would make sense to take secure, httponly, and samesite // into account, but the RFC doesn't specify this. - // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForHost(). + // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForKey(). return (name_ == ecc.Name() && domain_ == ecc.Domain() && path_ == ecc.Path()); } @@ -149,18 +157,40 @@ class NET_EXPORT CanonicalCookie { } // Checks a looser set of equivalency rules than 'IsEquivalent()' in order - // to support the stricter 'Secure' behaviors specified in + // to support the stricter 'Secure' behaviors specified in Step 12 of + // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-05#section-5.4 + // which originated from the proposal in // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone#section-3 // - // Returns 'true' if this cookie's name matches |ecc|, and this cookie is - // a domain-match for |ecc| (or vice versa), and |ecc|'s path is "on" this - // cookie's path (as per 'IsOnPath()'). + // Returns 'true' if this cookie's name matches |secure_cookie|, and this + // cookie is a domain-match for |secure_cookie| (or vice versa), and + // |secure_cookie|'s path is "on" this cookie's path (as per 'IsOnPath()'). // // Note that while the domain-match cuts both ways (e.g. 'example.com' // matches 'www.example.com' in either direction), the path-match is // unidirectional (e.g. '/login/en' matches '/login' and '/', but // '/login' and '/' do not match '/login/en'). - bool IsEquivalentForSecureCookieMatching(const CanonicalCookie& ecc) const; + // + // Conceptually: + // If new_cookie.IsEquivalentForSecureCookieMatching(secure_cookie) is true, + // this means that new_cookie would "shadow" secure_cookie: they would would + // be indistinguishable when serialized into a Cookie header. This is + // important because, if an attacker is attempting to set new_cookie, it + // should not be allowed to mislead the server into using new_cookie's value + // instead of secure_cookie's. + // + // The reason for the asymmetric path comparison ("cookie1=bad; path=/a/b" + // from an insecure source is not allowed if "cookie1=good; secure; path=/a" + // exists, but "cookie2=bad; path=/a" from an insecure source is allowed if + // "cookie2=good; secure; path=/a/b" exists) is because cookies in the Cookie + // header are serialized with longer path first. (See CookieSorter in + // cookie_monster.cc.) That is, they would be serialized as "Cookie: + // cookie1=bad; cookie1=good" in one case, and "Cookie: cookie2=good; + // cookie2=bad" in the other case. The first scenario is not allowed because + // the attacker injects the bad value, whereas the second scenario is ok + // because the good value is still listed first. + bool IsEquivalentForSecureCookieMatching( + const CanonicalCookie& secure_cookie) const; void SetSourceScheme(CookieSourceScheme source_scheme) { source_scheme_ = source_scheme; @@ -170,12 +200,28 @@ class NET_EXPORT CanonicalCookie { } void SetCreationDate(const base::Time& date) { creation_date_ = date; } - // Returns true if the given |url_path| path-matches the cookie-path as - // described in section 5.1.4 in RFC 6265. + // Returns true if the given |url_path| path-matches this cookie's cookie-path + // as described in section 5.1.4 in RFC 6265. This returns true if |path_| and + // |url_path| are identical, or if |url_path| is a subdirectory of |path_|. bool IsOnPath(const std::string& url_path) const; - // Returns true if the cookie domain matches the given |host| as described in - // section 5.1.3 of RFC 6265. + // This returns true if this cookie's |domain_| indicates that it can be + // accessed by |host|. + // + // In the case where |domain_| has no leading dot, this is a host cookie and + // will only domain match if |host| is identical to |domain_|. + // + // In the case where |domain_| has a leading dot, this is a domain cookie. It + // will match |host| if |domain_| is a suffix of |host|, or if |domain_| is + // exactly equal to |host| plus a leading dot. + // + // Note that this isn't quite the same as the "domain-match" algorithm in RFC + // 6265bis, since our implementation uses the presence of a leading dot in the + // |domain_| string in place of the spec's host-only-flag. That is, if + // |domain_| has no leading dot, then we only consider it matching if |host| + // is identical (which reflects the intended behavior when the cookie has a + // host-only-flag), whereas the RFC also treats them as domain-matching if + // |domain_| is a subdomain of |host|. bool IsDomainMatch(const std::string& host) const; // Returns if the cookie should be included (and if not, why) for the given @@ -265,12 +311,6 @@ class NET_EXPORT CanonicalCookie { COOKIE_PREFIX_LAST }; - // Applies the appropriate warning for the given cross-scheme - // SameSiteCookieContext. - void AddSameSiteCrossSchemeWarning( - CookieInclusionStatus* status, - const CookieOptions::SameSiteCookieContext context) const; - // Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that // applies to the given cookie |name|. static CookiePrefix GetCookiePrefix(const std::string& name); @@ -304,9 +344,6 @@ class NET_EXPORT CanonicalCookie { // Returns whether the cookie was created at most |age_threshold| ago. bool IsRecentlyCreated(base::TimeDelta age_threshold) const; - // Returns the cookie's domain, with the leading dot removed, if present. - std::string DomainWithoutDot() const; - std::string name_; std::string value_; std::string domain_; @@ -379,20 +416,82 @@ class NET_EXPORT CanonicalCookie::CookieInclusionStatus { // enough to activate the Lax-allow-unsafe intervention. WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE = 2, - // The following warnings indicate that a SameSite cookie is being sent/set - // across schemes and with what same-site context. - // See CookieOptions::SameSiteCookieContext. - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL = 3, - WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL = 4, - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL = 5, - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL = 6, - WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL = 7, - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL = 8, + // The following warnings indicate that an included cookie with an effective + // SameSite is experiencing a SameSiteCookieContext::|context| -> + // SameSiteCookieContext::|schemeful_context| downgrade that will prevent + // its access schemefully. + // This situation means that a cookie is accessible when the + // SchemefulSameSite feature is disabled but not when it's enabled, + // indicating changed behavior and potential breakage. + // + // For example, a Strict to Lax downgrade for an effective SameSite=Strict + // cookie: + // This cookie would be accessible in the Strict context as its SameSite + // value is Strict. However its context for schemeful same-site becomes Lax. + // A strict cookie cannot be accessed in a Lax context and therefore the + // behavior has changed. + // As a counterexample, a Strict to Lax downgrade for an effective + // SameSite=Lax cookie: A Lax cookie can be accessed in both Strict and Lax + // contexts so there is no behavior change (and we don't warn about it). + // + // The warnings are in the following format: + // WARN_{context}_{schemeful_context}_DOWNGRADE_{samesite_value}_SAMESITE + // + // Of the following 5 SameSite warnings, there will be, at most, a single + // active one. + + // Strict to Lax downgrade for an effective SameSite=Strict cookie. + // This warning is only applicable for cookies being sent because a Strict + // cookie will be set in both Strict and Lax Contexts so the downgrade will + // not affect it. + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE = 3, + // Strict to Cross-site downgrade for an effective SameSite=Strict cookie. + // This also applies to Strict to Lax Unsafe downgrades due to Lax Unsafe + // behaving like Cross-site. + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE = 4, + // Strict to Cross-site downgrade for an effective SameSite=Lax cookie. + // This also applies to Strict to Lax Unsafe downgrades due to Lax Unsafe + // behaving like Cross-site. + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE = 5, + // Lax to Cross-site downgrade for an effective SameSite=Strict cookie. + // This warning is only applicable for cookies being set because a Strict + // cookie will not be sent in a Lax context so the downgrade would not + // affect it. + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE = 6, + // Lax to Cross-site downgrade for an effective SameSite=Lax cookie. + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE = 7, // This should be kept last. NUM_WARNING_REASONS }; + // These enums encode the context downgrade warnings + the secureness of the + // url sending/setting the cookie. They're used for metrics only. The format + // is {context}_{schemeful_context}_{samesite_value}_{securness}. + // NO_DOWNGRADE_{securness} indicates that a cookie didn't have a breaking + // context downgrade and was A) included B) excluded only due to insufficient + // same-site context. I.e. the cookie wasn't excluded due to other reasons + // such as third-party cookie blocking. Keep this in line with + // SameSiteCookieContextBreakingDowngradeWithSecureness in enums.xml. + enum ContextDowngradeMetricValues { + NO_DOWNGRADE_INSECURE = 0, + NO_DOWNGRADE_SECURE = 1, + + STRICT_LAX_STRICT_INSECURE = 2, + STRICT_CROSS_STRICT_INSECURE = 3, + STRICT_CROSS_LAX_INSECURE = 4, + LAX_CROSS_STRICT_INSECURE = 5, + LAX_CROSS_LAX_INSECURE = 6, + + STRICT_LAX_STRICT_SECURE = 7, + STRICT_CROSS_STRICT_SECURE = 8, + STRICT_CROSS_LAX_SECURE = 9, + LAX_CROSS_STRICT_SECURE = 10, + LAX_CROSS_LAX_SECURE = 11, + + // Keep last. + kMaxValue = LAX_CROSS_LAX_SECURE + }; // Makes a status that says include and should not warn. CookieInclusionStatus(); @@ -422,16 +521,23 @@ class NET_EXPORT CanonicalCookie::CookieInclusionStatus { // warning about it (clear the warning). void MaybeClearSameSiteWarning(); + // Whether to record the breaking downgrade metrics if the cookie is included + // or if it's only excluded because of insufficient same-site context. + bool ShouldRecordDowngradeMetrics() const; + // Whether the cookie should be warned about. bool ShouldWarn() const; // Whether the given reason for warning is present. bool HasWarningReason(WarningReason reason) const; - // Whether a cross-scheme warning is present. - // If the function returns true and |reason| is valid then |reason| will - // contain which warning was found. - bool HasCrossSchemeWarning( + // Whether a schemeful downgrade warning is present. + // A schemeful downgrade means that an included cookie with an effective + // SameSite is experiencing a SameSiteCookieContext::|context| -> + // SameSiteCookieContext::|schemeful_context| downgrade that will prevent its + // access schemefully. If the function returns true and |reason| is valid then + // |reason| will contain which warning was found. + bool HasDowngradeWarning( CookieInclusionStatus::WarningReason* reason = nullptr) const; // Add an warning reason. @@ -451,6 +557,9 @@ class NET_EXPORT CanonicalCookie::CookieInclusionStatus { warning_reasons_ = warning_reasons; } + ContextDowngradeMetricValues GetBreakingDowngradeMetricsEnumValue( + const GURL& url) const; + // Get exclusion reason(s) and warning in string format. std::string GetDebugString() const; diff --git a/chromium/net/cookies/canonical_cookie_unittest.cc b/chromium/net/cookies/canonical_cookie_unittest.cc index 493af3f415b..68548deed97 100644 --- a/chromium/net/cookies/canonical_cookie_unittest.cc +++ b/chromium/net/cookies/canonical_cookie_unittest.cc @@ -396,6 +396,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { creation_time, expiration_time, base::Time(), secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); + // The path comparison is asymmetric EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -408,6 +409,56 @@ TEST(CanonicalCookieTest, IsEquivalent) { EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); } +TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) { + struct { + struct { + const char* name; + const char* domain; + const char* path; + } cookie, secure_cookie; + bool equivalent; + bool is_symmetric; // Whether the reverse comparison has the same result. + } kTests[] = { + // Equivalent to itself + {{"A", "a.foo.com", "/"}, {"A", "a.foo.com", "/"}, true, true}, + {{"A", ".a.foo.com", "/"}, {"A", ".a.foo.com", "/"}, true, true}, + // Names are different + {{"A", "a.foo.com", "/"}, {"B", "a.foo.com", "/"}, false, true}, + // Host cookie and domain cookie with same hostname match + {{"A", "a.foo.com", "/"}, {"A", ".a.foo.com", "/"}, true, true}, + // Subdomains and superdomains match + {{"A", "a.foo.com", "/"}, {"A", ".foo.com", "/"}, true, true}, + {{"A", ".a.foo.com", "/"}, {"A", ".foo.com", "/"}, true, true}, + {{"A", "a.foo.com", "/"}, {"A", "foo.com", "/"}, true, true}, + {{"A", ".a.foo.com", "/"}, {"A", "foo.com", "/"}, true, true}, + // Different domains don't match + {{"A", "a.foo.com", "/"}, {"A", "b.foo.com", "/"}, false, true}, + {{"A", "a.foo.com", "/"}, {"A", "ba.foo.com", "/"}, false, true}, + // Path attribute matches if it is a subdomain, but not vice versa. + {{"A", "a.foo.com", "/sub"}, {"A", "a.foo.com", "/"}, true, false}, + // Different paths don't match + {{"A", "a.foo.com", "/sub"}, {"A", "a.foo.com", "/other"}, false, true}, + {{"A", "a.foo.com", "/a/b"}, {"A", "a.foo.com", "/a/c"}, false, true}, + }; + + for (auto test : kTests) { + auto cookie = std::make_unique<CanonicalCookie>( + test.cookie.name, "value1", test.cookie.domain, test.cookie.path, + base::Time(), base::Time(), base::Time(), false /* secure */, false, + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_MEDIUM); + auto secure_cookie = std::make_unique<CanonicalCookie>( + test.secure_cookie.name, "value2", test.secure_cookie.domain, + test.secure_cookie.path, base::Time(), base::Time(), base::Time(), + true /* secure */, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_MEDIUM); + + EXPECT_EQ(test.equivalent, + cookie->IsEquivalentForSecureCookieMatching(*secure_cookie)); + EXPECT_EQ(test.equivalent == test.is_symmetric, + secure_cookie->IsEquivalentForSecureCookieMatching(*cookie)); + } +} + TEST(CanonicalCookieTest, IsDomainMatch) { GURL url("http://www.example.com/test/foo.html"); base::Time creation_time = base::Time::Now(); @@ -712,6 +763,8 @@ TEST(CanonicalCookieTest, IncludeForRequestURLSameSite) { // Test cases that are the same regardless of feature status or access // semantics: + // TODO(https://crbug.com/1030938): This test will need to consider + // SchemefulSameSite when it is added to CanonicalCookie. std::vector<IncludeForRequestURLTestCase> common_test_cases = { // Strict cookies: {"Common=1;SameSite=Strict", CookieSameSite::STRICT_MODE, @@ -735,129 +788,112 @@ TEST(CanonicalCookieTest, IncludeForRequestURLSameSite) { SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), CanonicalCookie::CookieInclusionStatus()}, + // Strict cookies with downgrade: {"Common=5;SameSite=Strict", CookieSameSite::STRICT_MODE, CookieEffectiveSameSite::STRICT_MODE, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::SAME_SITE_LAX), CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( std::vector< CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL})}, + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE})}, {"Common=6;SameSite=Strict", CookieSameSite::STRICT_MODE, CookieEffectiveSameSite::STRICT_MODE, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), + CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( + std::vector< + CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), + {CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE})}, + {"Common=7;SameSite=Strict", CookieSameSite::STRICT_MODE, + CookieEffectiveSameSite::STRICT_MODE, + SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( std::vector< CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL})}, + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE})}, // Lax cookies: - {"Common=7;SameSite=Lax", CookieSameSite::LAX_MODE, + {"Common=8;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), CanonicalCookie::CookieInclusionStatus( CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX)}, - {"Common=8;SameSite=Lax", CookieSameSite::LAX_MODE, + {"Common=9;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), CanonicalCookie::CookieInclusionStatus( CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX)}, - {"Common=9;SameSite=Lax", CookieSameSite::LAX_MODE, + {"Common=10;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), CanonicalCookie::CookieInclusionStatus()}, - {"Common=10;SameSite=Lax", CookieSameSite::LAX_MODE, + {"Common=11;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), CanonicalCookie::CookieInclusionStatus()}, - {"Common=11;SameSite=Lax", CookieSameSite::LAX_MODE, + // Lax cookies with downgrade: + {"Common=12;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( - std::vector< - CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), - {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL})}, - {"Common=12;SameSite=Lax", CookieSameSite::LAX_MODE, + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX), + CanonicalCookie::CookieInclusionStatus()}, + {"Common=13;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( std::vector< CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL})}, - {"Common=13;SameSite=Lax", CookieSameSite::LAX_MODE, + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE})}, + {"Common=14;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( std::vector< CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL})}, - {"Common=14;SameSite=Lax", CookieSameSite::LAX_MODE, + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE})}, + {"Common=15;SameSite=Lax", CookieSameSite::LAX_MODE, CookieEffectiveSameSite::LAX_MODE, - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( std::vector< CanonicalCookie::CookieInclusionStatus::ExclusionReason>(), {CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL})}, + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE})}, // None and Secure cookies: - {"Common=15;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, + {"Common=16;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, CookieEffectiveSameSite::NO_RESTRICTION, SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), CanonicalCookie::CookieInclusionStatus()}, - {"Common=16;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, + {"Common=17;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, CookieEffectiveSameSite::NO_RESTRICTION, SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), CanonicalCookie::CookieInclusionStatus()}, - {"Common=17;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, - CookieEffectiveSameSite::NO_RESTRICTION, - SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), - CanonicalCookie::CookieInclusionStatus()}, {"Common=18;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, CookieEffectiveSameSite::NO_RESTRICTION, - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), CanonicalCookie::CookieInclusionStatus()}, {"Common=19;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, CookieEffectiveSameSite::NO_RESTRICTION, SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - CanonicalCookie::CookieInclusionStatus()}, - {"Common=20;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, - CookieEffectiveSameSite::NO_RESTRICTION, - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - CanonicalCookie::CookieInclusionStatus()}, - {"Common=21;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, - CookieEffectiveSameSite::NO_RESTRICTION, - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - CanonicalCookie::CookieInclusionStatus()}, - {"Common=22;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION, - CookieEffectiveSameSite::NO_RESTRICTION, - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), CanonicalCookie::CookieInclusionStatus()}}; // Test cases where the default is None (either access semantics is LEGACY, or @@ -2008,30 +2044,24 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { context_same_site_strict.set_same_site_cookie_context( CookieOptions::SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT)); - CookieOptions context_same_site_lax_to_secure; - context_same_site_lax_to_secure.set_same_site_cookie_context( + + CookieOptions context_same_site_strict_to_lax; + context_same_site_strict_to_lax.set_same_site_cookie_context( CookieOptions::SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, - CookieOptions::SameSiteCookieContext::CrossSchemeness:: - INSECURE_SECURE)); - CookieOptions context_same_site_strict_to_secure; - context_same_site_strict_to_secure.set_same_site_cookie_context( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX)); + + CookieOptions context_same_site_strict_to_cross; + context_same_site_strict_to_cross.set_same_site_cookie_context( CookieOptions::SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - CookieOptions::SameSiteCookieContext::CrossSchemeness:: - INSECURE_SECURE)); - CookieOptions context_same_site_lax_to_insecure; - context_same_site_lax_to_insecure.set_same_site_cookie_context( + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE)); + + CookieOptions context_same_site_lax_to_cross; + context_same_site_lax_to_cross.set_same_site_cookie_context( CookieOptions::SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, - CookieOptions::SameSiteCookieContext::CrossSchemeness:: - SECURE_INSECURE)); - CookieOptions context_same_site_strict_to_insecure; - context_same_site_strict_to_insecure.set_same_site_cookie_context( - CookieOptions::SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - CookieOptions::SameSiteCookieContext::CrossSchemeness:: - SECURE_INSECURE)); + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE)); { CanonicalCookie cookie_same_site_unrestricted( @@ -2048,19 +2078,22 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { EXPECT_TRUE(cookie_same_site_unrestricted .IsSetPermittedInContext(context_same_site_strict) .IsInclude()); - EXPECT_TRUE(cookie_same_site_unrestricted - .IsSetPermittedInContext(context_same_site_lax_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unrestricted - .IsSetPermittedInContext(context_same_site_strict_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unrestricted - .IsSetPermittedInContext(context_same_site_lax_to_insecure) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unrestricted - .IsSetPermittedInContext(context_same_site_strict_to_insecure) - .IsInclude()); + + CanonicalCookie::CookieInclusionStatus status_strict_to_lax = + cookie_same_site_unrestricted.IsSetPermittedInContext( + context_same_site_strict_to_lax); + EXPECT_TRUE(status_strict_to_lax.IsInclude()); + EXPECT_FALSE(status_strict_to_lax.HasDowngradeWarning()); + CanonicalCookie::CookieInclusionStatus status_strict_to_cross = + cookie_same_site_unrestricted.IsSetPermittedInContext( + context_same_site_strict_to_cross); + EXPECT_TRUE(status_strict_to_cross.IsInclude()); + EXPECT_FALSE(status_strict_to_cross.HasDowngradeWarning()); + CanonicalCookie::CookieInclusionStatus status_lax_to_cross = + cookie_same_site_unrestricted.IsSetPermittedInContext( + context_same_site_lax_to_cross); + EXPECT_TRUE(status_lax_to_cross.IsInclude()); + EXPECT_FALSE(status_lax_to_cross.HasDowngradeWarning()); } { @@ -2079,19 +2112,26 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { EXPECT_TRUE( cookie_same_site_lax.IsSetPermittedInContext(context_same_site_strict) .IsInclude()); - EXPECT_TRUE(cookie_same_site_lax - .IsSetPermittedInContext(context_same_site_lax_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_lax - .IsSetPermittedInContext(context_same_site_strict_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_lax - .IsSetPermittedInContext(context_same_site_lax_to_insecure) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_lax - .IsSetPermittedInContext(context_same_site_strict_to_insecure) - .IsInclude()); + + CanonicalCookie::CookieInclusionStatus status_strict_to_lax = + cookie_same_site_lax.IsSetPermittedInContext( + context_same_site_strict_to_lax); + EXPECT_TRUE(status_strict_to_lax.IsInclude()); + EXPECT_FALSE(status_strict_to_lax.HasDowngradeWarning()); + CanonicalCookie::CookieInclusionStatus status_strict_to_cross = + cookie_same_site_lax.IsSetPermittedInContext( + context_same_site_strict_to_cross); + EXPECT_TRUE(status_strict_to_cross.IsInclude()); + EXPECT_TRUE(status_strict_to_cross.HasWarningReason( + CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE)); + CanonicalCookie::CookieInclusionStatus status_lax_to_cross = + cookie_same_site_lax.IsSetPermittedInContext( + context_same_site_lax_to_cross); + EXPECT_TRUE(status_lax_to_cross.IsInclude()); + EXPECT_TRUE(status_lax_to_cross.HasWarningReason( + CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE)); } { @@ -2113,19 +2153,26 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { EXPECT_TRUE(cookie_same_site_strict .IsSetPermittedInContext(context_same_site_strict) .IsInclude()); - EXPECT_TRUE(cookie_same_site_strict - .IsSetPermittedInContext(context_same_site_lax_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_strict - .IsSetPermittedInContext(context_same_site_strict_to_secure) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_strict - .IsSetPermittedInContext(context_same_site_lax_to_insecure) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_strict - .IsSetPermittedInContext(context_same_site_strict_to_insecure) - .IsInclude()); + + CanonicalCookie::CookieInclusionStatus status_strict_to_lax = + cookie_same_site_strict.IsSetPermittedInContext( + context_same_site_strict_to_lax); + EXPECT_TRUE(status_strict_to_lax.IsInclude()); + EXPECT_FALSE(status_strict_to_lax.HasDowngradeWarning()); + CanonicalCookie::CookieInclusionStatus status_strict_to_cross = + cookie_same_site_strict.IsSetPermittedInContext( + context_same_site_strict_to_cross); + EXPECT_TRUE(status_strict_to_cross.IsInclude()); + EXPECT_TRUE(status_strict_to_cross.HasWarningReason( + CanonicalCookie::CookieInclusionStatus:: + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE)); + CanonicalCookie::CookieInclusionStatus status_lax_to_cross = + cookie_same_site_strict.IsSetPermittedInContext( + context_same_site_lax_to_cross); + EXPECT_TRUE(status_lax_to_cross.IsInclude()); + EXPECT_TRUE(status_lax_to_cross.HasWarningReason( + CanonicalCookie::CookieInclusionStatus:: + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE)); } // Behavior of UNSPECIFIED depends on an experiment and CookieAccessSemantics. @@ -2151,23 +2198,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CookieAccessSemantics::UNKNOWN) .IsInclude()); EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified .IsSetPermittedInContext(context_cross_site, CookieAccessSemantics::LEGACY) .IsInclude()); @@ -2180,23 +2210,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CookieAccessSemantics::LEGACY) .IsInclude()); EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified .IsSetPermittedInContext(context_cross_site, CookieAccessSemantics::NONLEGACY) .HasExactlyExclusionReasonsForTesting( @@ -2210,23 +2223,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { .IsSetPermittedInContext(context_same_site_strict, CookieAccessSemantics::NONLEGACY) .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); } { @@ -2248,23 +2244,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CookieAccessSemantics::UNKNOWN) .IsInclude()); EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::UNKNOWN) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified .IsSetPermittedInContext(context_cross_site, CookieAccessSemantics::LEGACY) .IsInclude()); @@ -2277,23 +2256,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CookieAccessSemantics::LEGACY) .IsInclude()); EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::LEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified .IsSetPermittedInContext(context_cross_site, CookieAccessSemantics::NONLEGACY) .HasExactlyExclusionReasonsForTesting( @@ -2307,23 +2269,6 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { .IsSetPermittedInContext(context_same_site_strict, CookieAccessSemantics::NONLEGACY) .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_secure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_secure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE(cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_lax_to_insecure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); - EXPECT_TRUE( - cookie_same_site_unspecified - .IsSetPermittedInContext(context_same_site_strict_to_insecure, - CookieAccessSemantics::NONLEGACY) - .IsInclude()); } } @@ -2503,39 +2448,37 @@ TEST(CookieInclusionStatusTest, RemoveWarningReason) { WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT)); } -TEST(CookieInclusionStatusTest, HasCrossSchemeWarning) { +TEST(CookieInclusionStatusTest, HasDowngradeWarning) { std::vector<CanonicalCookie::CookieInclusionStatus::WarningReason> - cross_scheme_warnings = { + downgrade_warnings = { CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_SECURE_URL, + WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE, CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_CROSS_SCHEME_SECURE_URL, + WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE, CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_SECURE_URL, + WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE, CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_METHOD_UNSAFE_CROSS_SCHEME_INSECURE_URL, + WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE, CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_LAX_CROSS_SCHEME_INSECURE_URL, - CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_STRICT_CROSS_SCHEME_INSECURE_URL}; + WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE, + }; CanonicalCookie::CookieInclusionStatus empty_status; - EXPECT_FALSE(empty_status.HasCrossSchemeWarning()); + EXPECT_FALSE(empty_status.HasDowngradeWarning()); - CanonicalCookie::CookieInclusionStatus not_cross_scheme; - not_cross_scheme.AddWarningReason( + CanonicalCookie::CookieInclusionStatus not_downgrade; + not_downgrade.AddWarningReason( CanonicalCookie::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT); - EXPECT_FALSE(not_cross_scheme.HasCrossSchemeWarning()); + EXPECT_FALSE(not_downgrade.HasDowngradeWarning()); - for (auto warning : cross_scheme_warnings) { + for (auto warning : downgrade_warnings) { CanonicalCookie::CookieInclusionStatus status; status.AddWarningReason(warning); CanonicalCookie::CookieInclusionStatus::WarningReason reason; - EXPECT_TRUE(status.HasCrossSchemeWarning(&reason)); + EXPECT_TRUE(status.HasDowngradeWarning(&reason)); EXPECT_EQ(warning, reason); } } - } // namespace net diff --git a/chromium/net/cookies/cookie_change_dispatcher_test_helpers.cc b/chromium/net/cookies/cookie_change_dispatcher_test_helpers.cc index 0db6daaa5e8..3419e6653fe 100644 --- a/chromium/net/cookies/cookie_change_dispatcher_test_helpers.cc +++ b/chromium/net/cookies/cookie_change_dispatcher_test_helpers.cc @@ -4,7 +4,7 @@ #include "net/cookies/cookie_change_dispatcher_test_helpers.h" -#include "base/logging.h" +#include "base/notreached.h" namespace net { diff --git a/chromium/net/cookies/cookie_constants.cc b/chromium/net/cookies/cookie_constants.cc index 95adeef96e9..bf8dd08c555 100644 --- a/chromium/net/cookies/cookie_constants.cc +++ b/chromium/net/cookies/cookie_constants.cc @@ -4,8 +4,8 @@ #include "net/cookies/cookie_constants.h" -#include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" #include "base/strings/string_util.h" namespace net { diff --git a/chromium/net/cookies/cookie_deletion_info.cc b/chromium/net/cookies/cookie_deletion_info.cc index 73a11949b2b..b451dc2350f 100644 --- a/chromium/net/cookies/cookie_deletion_info.cc +++ b/chromium/net/cookies/cookie_deletion_info.cc @@ -28,12 +28,8 @@ bool DomainMatchesDomains(const net::CanonicalCookie& cookie, // If the cookie's domain is is not parsed as belonging to a registry // (e.g. for IP addresses or internal hostnames) an empty string will be // returned. In this case, use the domain in the cookie. - if (effective_domain.empty()) { - if (cookie.IsDomainCookie()) - effective_domain = cookie.Domain().substr(1); - else - effective_domain = cookie.Domain(); - } + if (effective_domain.empty()) + effective_domain = cookie.DomainWithoutDot(); return match_domains.count(effective_domain) != 0; } diff --git a/chromium/net/cookies/cookie_monster.cc b/chromium/net/cookies/cookie_monster.cc index 783485c013d..533cc97ba71 100644 --- a/chromium/net/cookies/cookie_monster.cc +++ b/chromium/net/cookies/cookie_monster.cc @@ -303,7 +303,8 @@ bool IsHttpSameSiteContextAtLeast( const CookieOptions& options, CookieOptions::SameSiteCookieContext::ContextType same_site_requirement) { return !options.exclude_httponly() && - options.same_site_cookie_context().context >= same_site_requirement; + options.same_site_cookie_context().GetContextForCookieInclusion() >= + same_site_requirement; } } // namespace @@ -368,7 +369,7 @@ void CookieMonster::SetAllCookiesAsync(const CookieList& list, void CookieMonster::SetCanonicalCookieAsync( std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) { DCHECK(cookie->IsCanonical()); @@ -380,8 +381,7 @@ void CookieMonster::SetCanonicalCookieAsync( // the callback on |*this|, so the callback will not outlive // the object. &CookieMonster::SetCanonicalCookie, base::Unretained(this), - std::move(cookie), std::move(source_scheme), options, - std::move(callback)), + std::move(cookie), source_url, options, std::move(callback)), domain); } @@ -904,7 +904,7 @@ void CookieMonster::TrimDuplicateCookiesForKey(const std::string& key, dupes.erase(dupes.begin()); LOG(ERROR) << base::StringPrintf( - "Found %d duplicate cookies for host='%s', " + "Found %d duplicate cookies for key='%s', " "with {name='%s', domain='%s', path='%s'}", static_cast<int>(dupes.size()), key.c_str(), std::get<0>(signature).c_str(), std::get<1>(signature).c_str(), @@ -998,68 +998,72 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus( CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY)); bool found_equivalent_cookie = false; - CookieMap::iterator maybe_delete_it = cookies_.end(); - CanonicalCookie* cc_skipped_secure = nullptr; + CookieMap::iterator deletion_candidate_it = cookies_.end(); + CanonicalCookie* skipped_secure_cookie = nullptr; // Check every cookie matching this domain key for equivalence. CookieMapItPair range_its = cookies_.equal_range(key); for (auto cur_it = range_its.first; cur_it != range_its.second; ++cur_it) { - CanonicalCookie* cc = cur_it->second.get(); + CanonicalCookie* cur_existing_cookie = cur_it->second.get(); // Evaluate "Leave Secure Cookies Alone": - // If the cookie is being set from an insecure scheme, then if a cookie - // already exists with the same name and it is Secure, then the cookie - // should *not* be updated if they domain-match and ignoring the path - // attribute. This notion of equivalence is slightly more inclusive than the - // usual IsEquivalent() check. + // If the cookie is being set from an insecure source, then if an + // "equivalent" Secure cookie already exists, then the cookie should *not* + // be updated. + // + // "Equivalent" means they are the same by + // IsEquivalentForSecureCookieMatching(). See the comment there for + // details. (Note this is not a symmetric comparison.) This notion of + // equivalence is slightly more inclusive than the usual IsEquivalent() one. // // See: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone - if (cc->IsSecure() && !source_secure && - cookie_being_set.IsEquivalentForSecureCookieMatching(*cc)) { + if (cur_existing_cookie->IsSecure() && !source_secure && + cookie_being_set.IsEquivalentForSecureCookieMatching( + *cur_existing_cookie)) { // Hold onto this for additional Netlogging later if we end up preserving // a would-have-been-deleted cookie because of this. - cc_skipped_secure = cc; + skipped_secure_cookie = cur_existing_cookie; net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_SECURE, [&](NetLogCaptureMode capture_mode) { return NetLogCookieMonsterCookieRejectedSecure( - cc_skipped_secure, &cookie_being_set, + skipped_secure_cookie, &cookie_being_set, capture_mode); }); status->AddExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE); } - if (cookie_being_set.IsEquivalent(*cc)) { + if (cookie_being_set.IsEquivalent(*cur_existing_cookie)) { // We should never have more than one equivalent cookie, since they should // overwrite each other. CHECK(!found_equivalent_cookie) << "Duplicate equivalent cookies found, cookie store is corrupted."; - DCHECK(maybe_delete_it == cookies_.end()); + DCHECK(deletion_candidate_it == cookies_.end()); found_equivalent_cookie = true; // The |cookie_being_set| is rejected for trying to overwrite an httponly // cookie when it should not be able to. - if (skip_httponly && cc->IsHttpOnly()) { + if (skip_httponly && cur_existing_cookie->IsHttpOnly()) { net_log_.AddEvent( NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_HTTPONLY, [&](NetLogCaptureMode capture_mode) { return NetLogCookieMonsterCookieRejectedHttponly( - cc, &cookie_being_set, capture_mode); + cur_existing_cookie, &cookie_being_set, capture_mode); }); status->AddExclusionReason(CanonicalCookie::CookieInclusionStatus:: EXCLUDE_OVERWRITE_HTTP_ONLY); } else { - maybe_delete_it = cur_it; + deletion_candidate_it = cur_it; } } } - if (maybe_delete_it != cookies_.end()) { - CanonicalCookie* maybe_delete_cc = maybe_delete_it->second.get(); - if (maybe_delete_cc->Value() == cookie_being_set.Value()) - *creation_date_to_inherit = maybe_delete_cc->CreationDate(); + if (deletion_candidate_it != cookies_.end()) { + CanonicalCookie* deletion_candidate = deletion_candidate_it->second.get(); + if (deletion_candidate->Value() == cookie_being_set.Value()) + *creation_date_to_inherit = deletion_candidate->CreationDate(); if (status->IsInclude()) { - InternalDeleteCookie(maybe_delete_it, true, + InternalDeleteCookie(deletion_candidate_it, true /* sync_to_store */, already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE); } else if (status->HasExclusionReason( @@ -1067,14 +1071,14 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus( EXCLUDE_OVERWRITE_SECURE)) { // Log that we preserved a cookie that would have been deleted due to // Leave Secure Cookies Alone. This arbitrarily only logs the last - // |cc_skipped_secure| that we were left with after the for loop, even if - // there were multiple matching Secure cookies that were left alone. - DCHECK(cc_skipped_secure); + // |skipped_secure_cookie| that we were left with after the for loop, even + // if there were multiple matching Secure cookies that were left alone. + DCHECK(skipped_secure_cookie); net_log_.AddEvent( NetLogEventType::COOKIE_STORE_COOKIE_PRESERVED_SKIPPED_SECURE, [&](NetLogCaptureMode capture_mode) { return NetLogCookieMonsterCookiePreservedSkippedSecure( - cc_skipped_secure, maybe_delete_cc, &cookie_being_set, + skipped_secure_cookie, deletion_candidate, &cookie_being_set, capture_mode); }); } @@ -1145,15 +1149,14 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie( } void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) { DCHECK(thread_checker_.CalledOnValidThread()); CanonicalCookie::CookieInclusionStatus status; - std::string scheme_lower = base::ToLowerASCII(source_scheme); - bool secure_source = GURL::SchemeIsCryptographic(scheme_lower); + bool secure_source = source_url.SchemeIsCryptographic(); cc->SetSourceScheme(secure_source ? CookieSourceScheme::kSecure : CookieSourceScheme::kNonSecure); if ((cc->IsSecure() && !secure_source)) { @@ -1161,7 +1164,7 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY); } - if (!IsCookieableScheme(scheme_lower)) { + if (!IsCookieableScheme(source_url.scheme())) { status.AddExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME); } @@ -1199,6 +1202,15 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, "insecure scheme"; } + // Now that IsSetPermittedInContext() and + // MaybeDeleteEquivalentCookieAndUpdateStatus() have had a chance to set + // cookie warnings/exclusions, record the downgrade metric. + if (status.ShouldRecordDowngradeMetrics()) { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.SameSiteContextDowngradeResponse", + status.GetBreakingDowngradeMetricsEnumValue(source_url)); + } + if (status.IsInclude()) { DVLOG(net::cookie_util::kVlogSetCookies) << "SetCookie() key: " << key << " cc: " << cc->DebugString(); @@ -1658,9 +1670,7 @@ std::string CookieMonster::GetKey(base::StringPiece domain) { if (effective_domain.empty()) effective_domain = std::string(domain); - if (!effective_domain.empty() && effective_domain[0] == '.') - return effective_domain.substr(1); - return effective_domain; + return cookie_util::CookieDomainAsHost(effective_domain); } bool CookieMonster::HasCookieableScheme(const GURL& url) { diff --git a/chromium/net/cookies/cookie_monster.h b/chromium/net/cookies/cookie_monster.h index 431fab884a5..46001bd4e4e 100644 --- a/chromium/net/cookies/cookie_monster.h +++ b/chromium/net/cookies/cookie_monster.h @@ -158,7 +158,7 @@ class NET_EXPORT CookieMonster : public CookieStore { // CookieStore implementation. void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) override; void GetCookieListWithOptionsAsync(const GURL& url, @@ -306,14 +306,14 @@ class NET_EXPORT CookieMonster : public CookieStore { static const int kRecordStatisticsIntervalSeconds = 10 * 60; // Sets a canonical cookie, deletes equivalents and performs garbage - // collection. |source_scheme| indicates what scheme the cookie is being set + // collection. |source_url| indicates what URL the cookie is being set // from; secure cookies cannot be altered from insecure schemes, and some // schemes may not be authorized. // // |options| indicates if this setting operation is allowed // to affect http_only or same-site cookies. void SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback); diff --git a/chromium/net/cookies/cookie_monster_perftest.cc b/chromium/net/cookies/cookie_monster_perftest.cc index 8ac5ec1ffd0..6966a8a9e4b 100644 --- a/chromium/net/cookies/cookie_monster_perftest.cc +++ b/chromium/net/cookies/cookie_monster_perftest.cc @@ -99,7 +99,7 @@ class SetCookieCallback : public CookieTestCallback { auto cookie = CanonicalCookie::Create(gurl, cookie_line, base::Time::Now(), base::nullopt /* server_time */); cm->SetCanonicalCookieAsync( - std::move(cookie), gurl.scheme(), options_, + std::move(cookie), gurl, options_, base::BindOnce(&SetCookieCallback::Run, base::Unretained(this))); WaitForCallback(); } diff --git a/chromium/net/cookies/cookie_monster_unittest.cc b/chromium/net/cookies/cookie_monster_unittest.cc index 477961d177e..82877bcb54b 100644 --- a/chromium/net/cookies/cookie_monster_unittest.cc +++ b/chromium/net/cookies/cookie_monster_unittest.cc @@ -51,6 +51,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +#include "url/url_constants.h" namespace net { @@ -163,8 +164,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { cm->SetCanonicalCookieAsync( CanonicalCookie::Create(url, cookie_line, creation_time, base::nullopt /* server_time */), - url.scheme(), CookieOptions::MakeAllInclusive(), - callback.MakeCallback()); + url, CookieOptions::MakeAllInclusive(), callback.MakeCallback()); callback.WaitUntilDone(); return callback.result().IsInclude(); } @@ -208,119 +208,84 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { // * Two domain path cookies (.c.b.a/dir1, .c.b.a/dir1/dir2) // * Two host path cookies (w.c.b.a/dir1, w.c.b.a/dir1/dir2) + std::vector<std::unique_ptr<CanonicalCookie>> cookies; + // Domain cookies - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "dom_1", "A", ".harvard.edu", "/", base::Time(), base::Time(), - base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "dom_2", "B", ".math.harvard.edu", "/", base::Time(), base::Time(), - base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "dom_3", "C", ".bourbaki.math.harvard.edu", "/", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "dom_1", "A", ".harvard.edu", "/", base::Time(), base::Time(), + base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "dom_2", "B", ".math.harvard.edu", "/", base::Time(), base::Time(), + base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "dom_3", "C", ".bourbaki.math.harvard.edu", "/", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); // Host cookies - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "host_1", "A", url_top_level_domain_plus_1, "/", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "host_2", "B", url_top_level_domain_plus_2, "/", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "host_3", "C", url_top_level_domain_plus_3, "/", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "host_1", "A", url_top_level_domain_plus_1, "/", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "host_2", "B", url_top_level_domain_plus_2, "/", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "host_3", "C", url_top_level_domain_plus_3, "/", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); // http_only cookie - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "httpo_check", "A", url_top_level_domain_plus_2, "/", base::Time(), - base::Time(), base::Time(), false, true, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "httpo_check", "A", url_top_level_domain_plus_2, "/", base::Time(), + base::Time(), base::Time(), false, true, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); // same-site cookie - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "firstp_check", "A", url_top_level_domain_plus_2, "/", base::Time(), - base::Time(), base::Time(), false, false, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "firstp_check", "A", url_top_level_domain_plus_2, "/", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, + COOKIE_PRIORITY_DEFAULT)); // Secure cookies - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "sec_dom", "A", ".math.harvard.edu", "/", base::Time(), - base::Time(), base::Time(), true, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "https", true /*modify_httponly*/)); - - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "sec_host", "B", url_top_level_domain_plus_2, "/", base::Time(), - base::Time(), base::Time(), true, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "https", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "sec_dom", "A", ".math.harvard.edu", "/", base::Time(), base::Time(), + base::Time(), true, false, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "sec_host", "B", url_top_level_domain_plus_2, "/", base::Time(), + base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_DEFAULT)); // Domain path cookies - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "dom_path_1", "A", ".math.harvard.edu", "/dir1", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "dom_path_2", "B", ".math.harvard.edu", "/dir1/dir2", base::Time(), - base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "dom_path_1", "A", ".math.harvard.edu", "/dir1", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "dom_path_2", "B", ".math.harvard.edu", "/dir1/dir2", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); // Host path cookies - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "host_path_1", "A", url_top_level_domain_plus_2, "/dir1", - base::Time(), base::Time(), base::Time(), false, false, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); - - EXPECT_TRUE(this->SetCanonicalCookie( - cm, - std::make_unique<CanonicalCookie>( - "host_path_2", "B", url_top_level_domain_plus_2, "/dir1/dir2", - base::Time(), base::Time(), base::Time(), false, false, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "host_path_1", "A", url_top_level_domain_plus_2, "/dir1", base::Time(), + base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, + COOKIE_PRIORITY_DEFAULT)); + cookies.push_back(std::make_unique<CanonicalCookie>( + "host_path_2", "B", url_top_level_domain_plus_2, "/dir1/dir2", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT)); + + for (auto& cookie : cookies) { + GURL source_url = cookie_util::SimulatedCookieSource( + *cookie, cookie->IsSecure() ? "https" : "http"); + EXPECT_TRUE(this->SetCanonicalCookie(cm, std::move(cookie), source_url, + true /* modify_httponly */)); + } EXPECT_EQ(14U, this->GetAllCookies(cm).size()); } @@ -825,7 +790,8 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { creation_time /* last_access */, true /* secure */, false /* http_only */, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); - cm->SetCanonicalCookieAsync(std::move(cc), "https", + GURL source_url = cookie_util::SimulatedCookieSource(*cc, "https"); + cm->SetCanonicalCookieAsync(std::move(cc), source_url, CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); } @@ -970,7 +936,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { cookie_monster_->SetCanonicalCookieAsync( CanonicalCookie::Create(http_www_foo_.url(), "A=B", base::Time::Now(), base::nullopt /* server_time */), - http_www_foo_.url().scheme(), CookieOptions::MakeAllInclusive(), + http_www_foo_.url(), CookieOptions::MakeAllInclusive(), call1.MakeCallback()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(call1.was_run()); @@ -984,7 +950,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { cookie_monster_->SetCanonicalCookieAsync( CanonicalCookie::Create(http_www_foo_.url(), "X=Y", base::Time::Now(), base::nullopt /* server_time */), - http_www_foo_.url().scheme(), CookieOptions::MakeAllInclusive(), + http_www_foo_.url(), CookieOptions::MakeAllInclusive(), call2.MakeCallback()); ASSERT_TRUE(call2.was_run()); EXPECT_TRUE(call2.result().IsInclude()); @@ -1245,7 +1211,7 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) { cookie_monster_->SetCanonicalCookieAsync( CanonicalCookie::Create(http_www_foo_.url(), "A=B", base::Time::Now(), base::nullopt /* server_time */), - http_www_foo_.url().scheme(), CookieOptions::MakeAllInclusive(), + http_www_foo_.url(), CookieOptions::MakeAllInclusive(), set_cookies_callback.MakeCallback()); // Nothing happened yet, before loads are done. @@ -1475,7 +1441,7 @@ TEST_F(CookieMonsterTest, SetCookieableSchemes) { EXPECT_TRUE(SetCanonicalCookieReturnStatus( cm.get(), CanonicalCookie::Create(http_url, "y=1", now, server_time), - "http", false /*modify_httponly*/) + http_url, false /*modify_httponly*/) .IsInclude()); EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), foo_url, "x=1") @@ -1485,7 +1451,7 @@ TEST_F(CookieMonsterTest, SetCookieableSchemes) { EXPECT_TRUE(SetCanonicalCookieReturnStatus( cm.get(), CanonicalCookie::Create(foo_url, "y=1", now, server_time), - "foo", false /*modify_httponly*/) + foo_url, false /*modify_httponly*/) .HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus:: EXCLUDE_NONCOOKIEABLE_SCHEME})); @@ -1495,7 +1461,7 @@ TEST_F(CookieMonsterTest, SetCookieableSchemes) { EXPECT_TRUE(SetCanonicalCookieReturnStatus( cm_foo.get(), CanonicalCookie::Create(foo_url, "y=1", now, server_time), - "foo", false /*modify_httponly*/) + foo_url, false /*modify_httponly*/) .IsInclude()); EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm_foo.get(), http_url, "x=1") @@ -1505,7 +1471,7 @@ TEST_F(CookieMonsterTest, SetCookieableSchemes) { EXPECT_TRUE(SetCanonicalCookieReturnStatus( cm_foo.get(), CanonicalCookie::Create(http_url, "y=1", now, server_time), - "http", false /*modify_httponly*/) + http_url, false /*modify_httponly*/) .HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus:: EXCLUDE_NONCOOKIEABLE_SCHEME})); @@ -2005,7 +1971,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { cookie.name, cookie.value, cookie.domain, cookie.path, base::Time(), cookie.expiration_time, base::Time(), cookie.secure, cookie.http_only, cookie.same_site, cookie.priority), - cookie.url.scheme(), true /*modify_httponly*/)); + cookie.url, true /*modify_httponly*/)); } EXPECT_TRUE(FindAndDeleteCookie(cmout.get(), @@ -2225,7 +2191,7 @@ TEST_F(CookieMonsterTest, WhileLoadingLoadCompletesBeforeKeyLoadCompletes) { base::nullopt /* server_time */); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> set_cookie_callback; - cm->SetCanonicalCookieAsync(std::move(cookie), kUrl.scheme(), + cm->SetCanonicalCookieAsync(std::move(cookie), kUrl, CookieOptions::MakeAllInclusive(), set_cookie_callback.MakeCallback()); @@ -2314,7 +2280,7 @@ TEST_F(CookieMonsterTest, WhileLoadingGetAllSetGetAll) { base::nullopt /* server_time */); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> set_cookie_callback; - cm->SetCanonicalCookieAsync(std::move(cookie), kUrl.scheme(), + cm->SetCanonicalCookieAsync(std::move(cookie), kUrl, CookieOptions::MakeAllInclusive(), set_cookie_callback.MakeCallback()); @@ -2365,8 +2331,8 @@ TEST_F(CookieMonsterTest, CheckOrderOfCookieTaskQueueWhenLoadingCompletes) { cm->GetAllCookiesAsync(base::BindOnce( &RunClosureOnAllCookiesReceived, base::BindOnce(&CookieStore::SetCanonicalCookieAsync, - base::Unretained(cm.get()), std::move(cookie), - kUrl.scheme(), CookieOptions::MakeAllInclusive(), + base::Unretained(cm.get()), std::move(cookie), kUrl, + CookieOptions::MakeAllInclusive(), set_cookie_callback.MakeCallback()))); // Get cookie task. Queued before the delete task is executed, so should not @@ -2548,13 +2514,13 @@ TEST_F(CookieMonsterTest, HistogramCheck) { std::unique_ptr<base::HistogramSamples> samples1( expired_histogram->SnapshotSamples()); - ASSERT_TRUE(SetCanonicalCookie( - cm.get(), - std::make_unique<CanonicalCookie>( - "a", "b", "a.url", "/", base::Time(), - base::Time::Now() + base::TimeDelta::FromMinutes(59), base::Time(), - true, false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "https", true /*modify_httponly*/)); + auto cookie = std::make_unique<CanonicalCookie>( + "a", "b", "a.url", "/", base::Time(), + base::Time::Now() + base::TimeDelta::FromMinutes(59), base::Time(), true, + false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + GURL source_url = cookie_util::SimulatedCookieSource(*cookie, "https"); + ASSERT_TRUE(SetCanonicalCookie(cm.get(), std::move(cookie), source_url, + true /*modify_httponly*/)); std::unique_ptr<base::HistogramSamples> samples2( expired_histogram->SnapshotSamples()); @@ -2764,7 +2730,8 @@ TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) { base::nullopt /* server_time */); CanonicalCookie::CookieInclusionStatus status = SetCanonicalCookieReturnStatus(cm.get(), std::move(preexisting_cookie), - "https", true /* can_modify_httponly */); + https_www_foo_.url(), + true /* can_modify_httponly */); ASSERT_TRUE(status.IsInclude()); // Set a new cookie with a different name. Should work because cookies with @@ -2782,8 +2749,9 @@ TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) { base::nullopt /* server_time */); // Allow modifying HttpOnly, so that we don't skip preexisting cookies for // being HttpOnly. - status = SetCanonicalCookieReturnStatus( - cm.get(), std::move(bad_cookie), "http", true /* can_modify_httponly */); + status = SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), + http_www_foo_.url(), + true /* can_modify_httponly */); EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE})); // The preexisting cookie should still be there. @@ -2812,8 +2780,9 @@ TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) { base::nullopt /* server_time */); // Allow modifying HttpOnly, so that we don't skip preexisting cookies for // being HttpOnly. - status = SetCanonicalCookieReturnStatus( - cm.get(), std::move(bad_cookie), "http", true /* can_modify_httponly */); + status = SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), + http_www_foo_.url(), + true /* can_modify_httponly */); EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE})); // The preexisting cookie should still be there. @@ -2839,9 +2808,9 @@ TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) { bad_cookie = CanonicalCookie::Create(https_www_foo_.url(), "A=E; Secure", base::Time::Now(), base::nullopt /* server_time */); - status = - SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), "https", - false /* can_modify_httponly */); + status = SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), + https_www_foo_.url(), + false /* can_modify_httponly */); EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY})); @@ -2865,7 +2834,8 @@ TEST_F(CookieMonsterTest, SkipDontOverwriteForMultipleReasons) { base::nullopt /* server_time */); CanonicalCookie::CookieInclusionStatus status = SetCanonicalCookieReturnStatus(cm.get(), std::move(preexisting_cookie), - "https", true /* can_modify_httponly */); + https_www_foo_.url(), + true /* can_modify_httponly */); ASSERT_TRUE(status.IsInclude()); // Attempt to set a new cookie with the same name that is not Secure or @@ -2873,7 +2843,8 @@ TEST_F(CookieMonsterTest, SkipDontOverwriteForMultipleReasons) { auto cookie = CanonicalCookie::Create(http_www_foo_.url(), "A=B", base::Time::Now(), base::nullopt /* server_time */); - status = SetCanonicalCookieReturnStatus(cm.get(), std::move(cookie), "http", + status = SetCanonicalCookieReturnStatus(cm.get(), std::move(cookie), + http_www_foo_.url(), false /* can_modify_httponly */); EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE, @@ -2899,14 +2870,16 @@ TEST_F(CookieMonsterTest, DontDeleteEquivalentCookieIfSetIsRejected) { base::nullopt /* server_time */); CanonicalCookie::CookieInclusionStatus status = SetCanonicalCookieReturnStatus(cm.get(), std::move(preexisting_cookie), - "https", false /* can_modify_httponly */); + http_www_foo_.url(), + false /* can_modify_httponly */); ASSERT_TRUE(status.IsInclude()); auto bad_cookie = CanonicalCookie::Create( http_www_foo_.url(), "cookie=bar;secure", base::Time::Now(), base::nullopt /* server_time */); CanonicalCookie::CookieInclusionStatus status2 = - SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), "http", + SetCanonicalCookieReturnStatus(cm.get(), std::move(bad_cookie), + http_www_foo_.url(), false /* can_modify_httponly */); EXPECT_TRUE(status2.HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); @@ -2930,13 +2903,13 @@ TEST_F(CookieMonsterTest, SetSecureCookies) { EXPECT_TRUE( CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B;").IsInclude()); - // A secure cookie cannot be created from a URL with an insecure scheme. + // A secure cookie cannot be set from a URL with an insecure scheme. EXPECT_TRUE( CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=B; Secure") .HasExactlyExclusionReasonsForTesting( {CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); - // A secure cookie can be created from a URL with a secure scheme. + // A secure cookie can be set from a URL with a secure scheme. EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=B; Secure") .IsInclude()); @@ -3095,6 +3068,288 @@ TEST_F(CookieMonsterTest, SetSecureCookies) { NetLogEventPhase::NONE); } +// Tests the behavior of "Leave Secure Cookies Alone" in +// MaybeDeleteEquivalentCookieAndUpdateStatus(). +// Check domain-match criterion: If either cookie domain matches the other, +// don't set the insecure cookie. +TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_DomainMatch) { + std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, &net_log_)); + + // These domains will domain-match each other. + const char* kRegistrableDomain = "foo.com"; + const char* kSuperdomain = "a.foo.com"; + const char* kDomain = "b.a.foo.com"; + const char* kSubdomain = "c.b.a.foo.com"; + // This domain does not match any, aside from the registrable domain. + const char* kOtherDomain = "z.foo.com"; + + for (const char* preexisting_cookie_host : + {kRegistrableDomain, kSuperdomain, kDomain, kSubdomain}) { + GURL preexisting_cookie_url( + base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + preexisting_cookie_host})); + for (const char* new_cookie_host : + {kRegistrableDomain, kSuperdomain, kDomain, kSubdomain}) { + GURL https_url(base::StrCat( + {url::kHttpsScheme, url::kStandardSchemeSeparator, new_cookie_host})); + GURL http_url(base::StrCat( + {url::kHttpScheme, url::kStandardSchemeSeparator, new_cookie_host})); + + // Preexisting Secure host and domain cookies. + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), preexisting_cookie_url, "A=0; Secure") + .IsInclude()); + EXPECT_TRUE( + CreateAndSetCookieReturnStatus( + cm.get(), preexisting_cookie_url, + base::StrCat({"B=0; Secure; Domain=", preexisting_cookie_host})) + .IsInclude()); + + // Don't set insecure cookie from an insecure URL if equivalent secure + // cookie exists. + EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=1") + .HasExactlyExclusionReasonsForTesting( + {CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_OVERWRITE_SECURE})) + << "Insecure host cookie from " << http_url + << " should not be set if equivalent secure host cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), http_url, + base::StrCat({"A=2; Domain=", new_cookie_host})) + .HasExactlyExclusionReasonsForTesting( + {CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_OVERWRITE_SECURE})) + << "Insecure domain cookie from " << http_url + << " should not be set if equivalent secure host cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), http_url, "B=1") + .HasExactlyExclusionReasonsForTesting( + {CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_OVERWRITE_SECURE})) + << "Insecure host cookie from " << http_url + << " should not be set if equivalent secure domain cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), http_url, + base::StrCat({"B=2; Domain=", new_cookie_host})) + .HasExactlyExclusionReasonsForTesting( + {CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_OVERWRITE_SECURE})) + << "Insecure domain cookie from " << http_url + << " should not be set if equivalent secure domain cookie from " + << preexisting_cookie_url << " exists."; + + // Allow setting insecure cookie from a secure URL even if equivalent + // secure cookie exists. + EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=3;") + .IsInclude()) + << "Insecure host cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), https_url, + base::StrCat({"A=4; Domain=", new_cookie_host})) + .IsInclude()) + << "Insecure domain cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), https_url, "B=3;") + .IsInclude()) + << "Insecure host cookie from " << https_url + << " can be set even if equivalent secure domain cookie from " + << preexisting_cookie_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), https_url, + base::StrCat({"B=4; Domain=", new_cookie_host})) + .IsInclude()) + << "Insecure domain cookie from " << https_url + << " can be set even if equivalent secure domain cookie from " + << preexisting_cookie_url << " exists."; + + DeleteAll(cm.get()); + } + } + + // Test non-domain-matching case. These sets should all be allowed because the + // cookie is not equivalent. + GURL nonmatching_https_url(base::StrCat( + {url::kHttpsScheme, url::kStandardSchemeSeparator, kOtherDomain})); + + for (const char* host : {kSuperdomain, kDomain, kSubdomain}) { + GURL https_url( + base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, host})); + GURL http_url( + base::StrCat({url::kHttpScheme, url::kStandardSchemeSeparator, host})); + + // Preexisting Secure host and domain cookies. + EXPECT_TRUE(CreateAndSetCookieReturnStatus(cm.get(), nonmatching_https_url, + "A=0; Secure") + .IsInclude()); + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), nonmatching_https_url, + base::StrCat({"B=0; Secure; Domain=", kOtherDomain})) + .IsInclude()); + + // New cookie from insecure URL is set. + EXPECT_TRUE( + CreateAndSetCookieReturnStatus(cm.get(), http_url, "A=1;").IsInclude()) + << "Insecure host cookie from " << http_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), http_url, base::StrCat({"A=2; Domain=", host})) + .IsInclude()) + << "Insecure domain cookie from " << http_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE( + CreateAndSetCookieReturnStatus(cm.get(), http_url, "B=1;").IsInclude()) + << "Insecure host cookie from " << http_url + << " can be set even if equivalent secure domain cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), http_url, base::StrCat({"B=2; Domain=", host})) + .IsInclude()) + << "Insecure domain cookie from " << http_url + << " can be set even if equivalent secure domain cookie from " + << nonmatching_https_url << " exists."; + + // New cookie from secure URL is set. + EXPECT_TRUE( + CreateAndSetCookieReturnStatus(cm.get(), https_url, "A=3;").IsInclude()) + << "Insecure host cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), https_url, base::StrCat({"A=4; Domain=", host})) + .IsInclude()) + << "Insecure domain cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE( + CreateAndSetCookieReturnStatus(cm.get(), https_url, "B=3;").IsInclude()) + << "Insecure host cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), https_url, base::StrCat({"B=4; Domain=", host})) + .IsInclude()) + << "Insecure domain cookie from " << https_url + << " can be set even if equivalent secure host cookie from " + << nonmatching_https_url << " exists."; + + DeleteAll(cm.get()); + } +} + +// Tests the behavior of "Leave Secure Cookies Alone" in +// MaybeDeleteEquivalentCookieAndUpdateStatus(). +// Check path-match criterion: If the new cookie is for the same path or a +// subdirectory of the preexisting cookie's path, don't set the new cookie. +TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_PathMatch) { + std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, &net_log_)); + + // A path that is later in this list will path-match all the paths before it. + const char* kPaths[] = {"/", "/1", "/1/2", "/1/2/3"}; + // This path does not match any, aside from the root path. + const char* kOtherDirectory = "/9"; + + for (int preexisting_cookie_path_index = 0; preexisting_cookie_path_index < 4; + ++preexisting_cookie_path_index) { + const char* preexisting_cookie_path = kPaths[preexisting_cookie_path_index]; + GURL preexisting_cookie_url( + base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + "a.foo.com", preexisting_cookie_path})); + for (int new_cookie_path_index = 0; new_cookie_path_index < 4; + ++new_cookie_path_index) { + const char* new_cookie_path = kPaths[new_cookie_path_index]; + bool should_path_match = + new_cookie_path_index >= preexisting_cookie_path_index; + GURL https_url( + base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + "a.foo.com", new_cookie_path})); + GURL http_url( + base::StrCat({url::kHttpScheme, url::kStandardSchemeSeparator, + "a.foo.com", new_cookie_path})); + + // Preexisting Secure cookie. + EXPECT_TRUE( + CreateAndSetCookieReturnStatus( + cm.get(), preexisting_cookie_url, + base::StrCat({"A=0; Secure; Path=", preexisting_cookie_path})) + .IsInclude()); + + // Don't set insecure cookie from an insecure URL if equivalent secure + // cookie exists. + CanonicalCookie::CookieInclusionStatus set = + CreateAndSetCookieReturnStatus( + cm.get(), http_url, + base::StrCat({"A=1; Path=", new_cookie_path})); + EXPECT_TRUE(should_path_match + ? set.HasExactlyExclusionReasonsForTesting( + {CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_OVERWRITE_SECURE}) + : set.IsInclude()) + << "Insecure cookie from " << http_url << " should " + << (should_path_match ? "not " : "") + << "be set if equivalent secure cookie from " + << preexisting_cookie_url << " exists."; + + // Allow setting insecure cookie from a secure URL even if equivalent + // secure cookie exists. + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), https_url, + base::StrCat({"A=2; Path=", new_cookie_path})) + .IsInclude()) + << "Insecure cookie from " << http_url + << " can be set even if equivalent secure cookie from " + << preexisting_cookie_url << " exists."; + + DeleteAll(cm.get()); + } + } + + // Test non-matching-path case. These sets should all be allowed because the + // cookie is not equivalent. + GURL nonmatching_https_url( + base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + "a.foo.com", kOtherDirectory})); + + for (int new_cookie_path_index = 1; new_cookie_path_index < 4; + ++new_cookie_path_index) { + const char* new_cookie_path = kPaths[new_cookie_path_index]; + GURL https_url(base::StrCat( + {url::kHttpsScheme, url::kStandardSchemeSeparator, new_cookie_path})); + GURL http_url(base::StrCat( + {url::kHttpScheme, url::kStandardSchemeSeparator, new_cookie_path})); + + // Preexisting Secure cookie. + EXPECT_TRUE(CreateAndSetCookieReturnStatus( + cm.get(), nonmatching_https_url, + base::StrCat({"A=0; Secure; Path=", kOtherDirectory})) + .IsInclude()); + + // New cookie from insecure URL is set. + EXPECT_TRUE( + CreateAndSetCookieReturnStatus( + cm.get(), http_url, base::StrCat({"A=1; Path=", new_cookie_path})) + .IsInclude()) + << "Insecure cookie from " << http_url + << " can be set even if equivalent secure cookie from " + << nonmatching_https_url << " exists."; + + // New cookie from secure URL is set. + EXPECT_TRUE( + CreateAndSetCookieReturnStatus( + cm.get(), https_url, base::StrCat({"A=1; Path=", new_cookie_path})) + .IsInclude()) + << "Insecure cookie from " << https_url + << " can be set even if equivalent secure cookie from " + << nonmatching_https_url << " exists."; + } +} + // Tests for behavior for strict secure cookies. TEST_F(CookieMonsterTest, EvictSecureCookies) { // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint. @@ -3254,10 +3509,12 @@ TEST_F(CookieMonsterTest, SetCanonicalCookieDoesNotBlockForLoadAll) { // Start of a canonical cookie set. ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback_set; + GURL cookie_url("http://a.com/"); cm.SetCanonicalCookieAsync( - CanonicalCookie::Create(GURL("http://a.com/"), "A=B", base::Time::Now(), + CanonicalCookie::Create(cookie_url, "A=B", base::Time::Now(), base::nullopt /* server_time */), - "http", CookieOptions::MakeAllInclusive(), callback_set.MakeCallback()); + cookie_url, CookieOptions::MakeAllInclusive(), + callback_set.MakeCallback()); // Get cookies for a different URL. GetCookieListCallback callback_get; @@ -3339,7 +3596,7 @@ TEST_F(CookieMonsterTest, DeleteCookieWithInheritedTimestamps) { auto cookie = CanonicalCookie::Create(url, cookie_line, t1, server_time); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> set_callback_1; - cm.SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, + cm.SetCanonicalCookieAsync(std::move(cookie), url, options, set_callback_1.MakeCallback()); set_callback_1.WaitUntilDone(); @@ -3347,7 +3604,7 @@ TEST_F(CookieMonsterTest, DeleteCookieWithInheritedTimestamps) { cookie = CanonicalCookie::Create(url, cookie_line, t2, server_time); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> set_callback_2; - cm.SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, + cm.SetCanonicalCookieAsync(std::move(cookie), url, options, set_callback_2.MakeCallback()); set_callback_2.WaitUntilDone(); @@ -3380,7 +3637,7 @@ TEST_F(CookieMonsterTest, RejectCreatedSameSiteCookieOnSet) { // ... but the environment is checked on set, so this may be rejected then. ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; - cm.SetCanonicalCookieAsync(std::move(cookie), "http", env_cross_site, + cm.SetCanonicalCookieAsync(std::move(cookie), url, env_cross_site, callback.MakeCallback()); callback.WaitUntilDone(); EXPECT_TRUE(callback.result().HasExactlyExclusionReasonsForTesting( @@ -3404,7 +3661,7 @@ TEST_F(CookieMonsterTest, RejectCreatedSecureCookieOnSet) { // Cookie is rejected when attempting to set from a non-secure scheme. ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; - cm.SetCanonicalCookieAsync(std::move(cookie), "http", + cm.SetCanonicalCookieAsync(std::move(cookie), http_url, CookieOptions::MakeAllInclusive(), callback.MakeCallback()); callback.WaitUntilDone(); @@ -3434,7 +3691,7 @@ TEST_F(CookieMonsterTest, RejectCreatedHttpOnlyCookieOnSet) { CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT)); options_no_httponly.set_exclude_httponly(); // Default, but make it explicit. ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; - cm.SetCanonicalCookieAsync(std::move(cookie), "http", options_no_httponly, + cm.SetCanonicalCookieAsync(std::move(cookie), url, options_no_httponly, callback.MakeCallback()); callback.WaitUntilDone(); EXPECT_TRUE(callback.result().HasExactlyExclusionReasonsForTesting( @@ -3565,7 +3822,7 @@ TEST_F(CookieMonsterTest, CookiesWithoutSameSiteMustBeSecure) { CanonicalCookie cookie_copy = *cookie; CanonicalCookie::CookieInclusionStatus result = SetCanonicalCookieReturnStatus( - cm.get(), std::move(cookie), url.scheme(), + cm.get(), std::move(cookie), url, true /* can_modify_httponly (irrelevant) */); EXPECT_EQ(test.expected_set_cookie_result, result) << "Test case " << i << " failed."; diff --git a/chromium/net/cookies/cookie_options.cc b/chromium/net/cookies/cookie_options.cc index 252de953cea..3ab86e67f0c 100644 --- a/chromium/net/cookies/cookie_options.cc +++ b/chromium/net/cookies/cookie_options.cc @@ -6,20 +6,41 @@ #include "net/cookies/cookie_options.h" +#include "net/cookies/cookie_util.h" + namespace net { CookieOptions::SameSiteCookieContext CookieOptions::SameSiteCookieContext::MakeInclusive() { - return SameSiteCookieContext(ContextType::SAME_SITE_STRICT); + return SameSiteCookieContext(ContextType::SAME_SITE_STRICT, + ContextType::SAME_SITE_STRICT); +} + +CookieOptions::SameSiteCookieContext +CookieOptions::SameSiteCookieContext::MakeInclusiveForSet() { + return SameSiteCookieContext(ContextType::SAME_SITE_LAX, + ContextType::SAME_SITE_LAX); +} + +CookieOptions::SameSiteCookieContext::ContextType +CookieOptions::SameSiteCookieContext::GetContextForCookieInclusion() const { + DCHECK_LE(schemeful_context_, context_); + + if (cookie_util::IsSchemefulSameSiteEnabled()) + return schemeful_context_; + + return context_; +} + +bool operator==(const CookieOptions::SameSiteCookieContext& lhs, + const CookieOptions::SameSiteCookieContext& rhs) { + return std::tie(lhs.context_, lhs.schemeful_context_) == + std::tie(rhs.context_, rhs.schemeful_context_); } -int64_t CookieOptions::SameSiteCookieContext::ConvertToMetricsValue() const { - if (cross_schemeness == CrossSchemeness::INSECURE_SECURE) { - return static_cast<int64_t>(context) | kToSecureMask; - } else if (cross_schemeness == CrossSchemeness::SECURE_INSECURE) { - return static_cast<int64_t>(context) | kToInsecureMask; - } - return static_cast<int64_t>(context); +bool operator!=(const CookieOptions::SameSiteCookieContext& lhs, + const CookieOptions::SameSiteCookieContext& rhs) { + return !(lhs == rhs); } // Keep default values in sync with content/public/common/cookie_manager.mojom. @@ -39,15 +60,4 @@ CookieOptions CookieOptions::MakeAllInclusive() { return options; } -bool operator==(const CookieOptions::SameSiteCookieContext& lhs, - const CookieOptions::SameSiteCookieContext& rhs) { - return std::tie(lhs.context, lhs.cross_schemeness) == - std::tie(rhs.context, rhs.cross_schemeness); -} - -bool operator!=(const CookieOptions::SameSiteCookieContext& lhs, - const CookieOptions::SameSiteCookieContext& rhs) { - return !(lhs == rhs); -} - } // namespace net diff --git a/chromium/net/cookies/cookie_options.h b/chromium/net/cookies/cookie_options.h index db0c434ec3b..abf074b6aab 100644 --- a/chromium/net/cookies/cookie_options.h +++ b/chromium/net/cookies/cookie_options.h @@ -33,49 +33,53 @@ class NET_EXPORT CookieOptions { COUNT }; - // Used for when, and in what direction, same-site requests and responses - // are made in a cross-scheme context. Currently only used for metrics - // gathering and does not affect cookie behavior. - enum class CrossSchemeness { - NONE, - INSECURE_SECURE, // Insecure site-for-cookies, secure request/response - SECURE_INSECURE // Secure site-for-cookies, insecure request/response - }; - - SameSiteCookieContext() : SameSiteCookieContext(ContextType::CROSS_SITE) {} - explicit SameSiteCookieContext( - ContextType same_site_context, - CrossSchemeness cross_schemeness = CrossSchemeness::NONE) - : context(same_site_context), cross_schemeness(cross_schemeness) {} + SameSiteCookieContext() + : SameSiteCookieContext(ContextType::CROSS_SITE, + ContextType::CROSS_SITE) {} + explicit SameSiteCookieContext(ContextType same_site_context) + : SameSiteCookieContext(same_site_context, same_site_context) {} + + SameSiteCookieContext(ContextType same_site_context, + ContextType schemeful_same_site_context) + : context_(same_site_context), + schemeful_context_(schemeful_same_site_context) { + DCHECK_LE(schemeful_context_, context_); + } // Convenience method which returns a SameSiteCookieContext with the most - // inclusive context. This allows access to all SameSite cookies. + // inclusive contexts. This allows access to all SameSite cookies. static SameSiteCookieContext MakeInclusive(); - // The following functions are for conversion to the previous style of - // SameSiteCookieContext for metrics usage. This may be removed when the - // metrics using them are also removed. + // Convenience method which returns a SameSiteCookieContext with the most + // inclusive contexts for set. This allows setting all SameSite cookies. + static SameSiteCookieContext MakeInclusiveForSet(); - // Used as the "COUNT" entry in a histogram enum. - static constexpr int64_t MetricCount() { - return (static_cast<int>(ContextType::SAME_SITE_STRICT) | - kToInsecureMask) + - 1; - } - int64_t ConvertToMetricsValue() const; + // Returns the context for determining SameSite cookie inclusion. + ContextType GetContextForCookieInclusion() const; - ContextType context; + // If you're just trying to determine if a cookie is accessible you likely + // want to use GetContextForCookieInclusion() which will return the correct + // context regardless the status of same-site features. + ContextType context() const { return context_; } + void set_context(ContextType context) { context_ = context; } - CrossSchemeness cross_schemeness; + ContextType schemeful_context() const { return schemeful_context_; } + void set_schemeful_context(ContextType schemeful_context) { + schemeful_context_ = schemeful_context; + } + + NET_EXPORT friend bool operator==( + const CookieOptions::SameSiteCookieContext& lhs, + const CookieOptions::SameSiteCookieContext& rhs); + NET_EXPORT friend bool operator!=( + const CookieOptions::SameSiteCookieContext& lhs, + const CookieOptions::SameSiteCookieContext& rhs); private: - // The following variables are for conversion to the previous style of - // SameSiteCookieContext for metrics usage. This may be removed when the - // metrics using them are also removed. - // Mask indicating insecure site-for-cookies and secure request/response. - static const int kToSecureMask = 1 << 5; - // Mask indicating secure site-for-cookies and insecure request/response. - static const int kToInsecureMask = kToSecureMask << 1; + + ContextType context_; + + ContextType schemeful_context_; }; // Creates a CookieOptions object which: @@ -88,8 +92,7 @@ class NET_EXPORT CookieOptions { // These settings can be altered by calling: // // * |set_{include,exclude}_httponly()| - // * |set_same_site_cookie_context( - // CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT)| + // * |set_same_site_cookie_context()| // * |set_do_not_update_access_time()| CookieOptions(); @@ -103,7 +106,6 @@ class NET_EXPORT CookieOptions { same_site_cookie_context_ = context; } - // Strips off the cross-scheme bits to only return the same-site context. SameSiteCookieContext same_site_cookie_context() const { return same_site_cookie_context_; } @@ -131,12 +133,6 @@ class NET_EXPORT CookieOptions { bool return_excluded_cookies_; }; -NET_EXPORT bool operator==(const CookieOptions::SameSiteCookieContext& lhs, - const CookieOptions::SameSiteCookieContext& rhs); - -NET_EXPORT bool operator!=(const CookieOptions::SameSiteCookieContext& lhs, - const CookieOptions::SameSiteCookieContext& rhs); - } // namespace net #endif // NET_COOKIES_COOKIE_OPTIONS_H_ diff --git a/chromium/net/cookies/cookie_store.h b/chromium/net/cookies/cookie_store.h index 996ad7d14df..4157f0ce83a 100644 --- a/chromium/net/cookies/cookie_store.h +++ b/chromium/net/cookies/cookie_store.h @@ -62,14 +62,14 @@ class NET_EXPORT CookieStore { virtual ~CookieStore(); // Set the cookie on the cookie store. |cookie.IsCanonical()| must - // be true. |source_scheme| denotes the scheme of the resource setting this. + // be true. |source_url| denotes the url of the resource setting this. // // |options| is used to determine the context the operation is run in, and // which cookies it can alter (e.g. http only, or same site). // // The current time will be used in place of a null creation time. virtual void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) = 0; diff --git a/chromium/net/cookies/cookie_store_test_helpers.cc b/chromium/net/cookies/cookie_store_test_helpers.cc index 6ac538077e8..a709c84f134 100644 --- a/chromium/net/cookies/cookie_store_test_helpers.cc +++ b/chromium/net/cookies/cookie_store_test_helpers.cc @@ -89,12 +89,12 @@ void DelayedCookieMonster::GetCookieListWithOptionsInternalCallback( void DelayedCookieMonster::SetCanonicalCookieAsync( std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) { did_run_ = false; cookie_monster_->SetCanonicalCookieAsync( - std::move(cookie), std::move(source_scheme), options, + std::move(cookie), source_url, options, base::BindOnce(&DelayedCookieMonster::SetCookiesInternalCallback, base::Unretained(this))); DCHECK_EQ(did_run_, true); diff --git a/chromium/net/cookies/cookie_store_test_helpers.h b/chromium/net/cookies/cookie_store_test_helpers.h index a78190831b6..ae6a152b7f2 100644 --- a/chromium/net/cookies/cookie_store_test_helpers.h +++ b/chromium/net/cookies/cookie_store_test_helpers.h @@ -54,7 +54,7 @@ class DelayedCookieMonster : public CookieStore { // Post a delayed task to invoke the original callback with the results. void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, const CookieOptions& options, SetCookiesCallback callback) override; diff --git a/chromium/net/cookies/cookie_store_unittest.h b/chromium/net/cookies/cookie_store_unittest.h index 1e86c65f34a..ee8e46f9b1d 100644 --- a/chromium/net/cookies/cookie_store_unittest.h +++ b/chromium/net/cookies/cookie_store_unittest.h @@ -201,7 +201,7 @@ class CookieStoreTest : public testing::Test { return false; DCHECK(cs); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; - cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, + cs->SetCanonicalCookieAsync(std::move(cookie), url, options, callback.MakeCallback()); callback.WaitUntilDone(); return callback.result().IsInclude(); @@ -209,7 +209,7 @@ class CookieStoreTest : public testing::Test { bool SetCanonicalCookie(CookieStore* cs, std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, bool can_modify_httponly) { DCHECK(cs); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; @@ -218,8 +218,8 @@ class CookieStoreTest : public testing::Test { options.set_include_httponly(); options.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::MakeInclusive()); - cs->SetCanonicalCookieAsync(std::move(cookie), std::move(source_scheme), - options, callback.MakeCallback()); + cs->SetCanonicalCookieAsync(std::move(cookie), source_url, options, + callback.MakeCallback()); callback.WaitUntilDone(); return callback.result().IsInclude(); } @@ -268,7 +268,7 @@ class CookieStoreTest : public testing::Test { DCHECK(cs); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; - cs->SetCanonicalCookieAsync(std::move(cookie), url.scheme(), options, + cs->SetCanonicalCookieAsync(std::move(cookie), url, options, callback.MakeCallback()); callback.WaitUntilDone(); return callback.result(); @@ -277,7 +277,7 @@ class CookieStoreTest : public testing::Test { CanonicalCookie::CookieInclusionStatus SetCanonicalCookieReturnStatus( CookieStore* cs, std::unique_ptr<CanonicalCookie> cookie, - std::string source_scheme, + const GURL& source_url, bool can_modify_httponly) { DCHECK(cs); ResultSavingCookieCallback<CanonicalCookie::CookieInclusionStatus> callback; @@ -286,8 +286,8 @@ class CookieStoreTest : public testing::Test { options.set_include_httponly(); options.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::MakeInclusive()); - cs->SetCanonicalCookieAsync(std::move(cookie), std::move(source_scheme), - options, callback.MakeCallback()); + cs->SetCanonicalCookieAsync(std::move(cookie), source_url, options, + callback.MakeCallback()); callback.WaitUntilDone(); return callback.result(); } @@ -424,8 +424,8 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { one_hour_from_now, base::Time(), false, false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT)); ASSERT_TRUE(cc); - EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", - true /*modify_httponly*/)); + EXPECT_TRUE(this->SetCanonicalCookie( + cs, std::move(cc), this->www_foo_foo_.url(), true /*modify_httponly*/)); // Note that for the creation time to be set exactly, without modification, // it must be different from the one set by the line above. @@ -434,8 +434,8 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { two_hours_ago, base::Time(), one_hour_ago, false, true, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT); ASSERT_TRUE(cc); - EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", - true /*modify_httponly*/)); + EXPECT_TRUE(this->SetCanonicalCookie( + cs, std::move(cc), this->www_foo_bar_.url(), true /*modify_httponly*/)); // Because of strict secure cookies, it should not be possible to create // a secure cookie with an HTTP URL. @@ -451,8 +451,8 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); ASSERT_TRUE(cc); - EXPECT_TRUE(this->SetCanonicalCookie(cs, std::move(cc), "https", - true /*modify_httponly*/)); + EXPECT_TRUE(this->SetCanonicalCookie( + cs, std::move(cc), this->https_www_foo_.url(), true /*modify_httponly*/)); // Get all the cookies for a given URL, regardless of properties. This 'get()' // operation shouldn't update the access time, as the test checks that the @@ -550,16 +550,14 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "A", "B", foo_foo_host, "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /* secure */, false /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true)); - // Note that for the creation time to be set exactly, without modification, - // it must be different from the one set by the line above. + this->www_foo_foo_.url(), true)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "C", "D", "." + foo_bar_domain, "/bar", two_hours_ago, base::Time(), one_hour_ago, false, true, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true)); + this->www_foo_bar_.url(), true)); // A secure source is required for setting secure cookies. EXPECT_TRUE( @@ -569,7 +567,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "E", "F", http_foo_host, "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "http", true) + this->http_www_foo_.url(), true) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); @@ -582,7 +580,8 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { EXPECT_TRUE(cookie->IsSecure()); EXPECT_TRUE(status.IsInclude()); EXPECT_TRUE( - this->SetCanonicalCookieReturnStatus(cs, std::move(cookie), "http", true) + this->SetCanonicalCookieReturnStatus(cs, std::move(cookie), + this->http_www_foo_.url(), true) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); @@ -591,10 +590,10 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( - "E", "F", http_foo_host, "/", base::Time(), base::Time(), + "E", "F", https_foo_host, "/", base::Time(), base::Time(), base::Time(), true /* secure */, false /* httponly */, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "https", true /* modify_http_only */)); + this->https_www_foo_.url(), true /* modify_http_only */)); EXPECT_TRUE( this->SetCanonicalCookieReturnStatus( @@ -603,7 +602,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "E", "F", http_foo_host, "/", base::Time(), base::Time(), base::Time(), true /* secure */, false /* httponly */, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - "http", true /* modify_http_only */) + this->http_www_foo_.url(), true /* modify_http_only */) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); @@ -618,7 +617,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", false /* modify_http_only */) + this->http_www_foo_.url(), false /* modify_http_only */) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); @@ -631,7 +630,8 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { EXPECT_TRUE(c->IsHttpOnly()); EXPECT_TRUE(create_status.IsInclude()); EXPECT_TRUE( - this->SetCanonicalCookieReturnStatus(cs, std::move(c), "http", + this->SetCanonicalCookieReturnStatus(cs, std::move(c), + this->http_www_foo_.url(), false /* can_modify_httponly */) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); @@ -644,7 +644,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /* modify_http_only */)); + this->http_www_foo_.url(), true /* modify_http_only */)); EXPECT_TRUE( this->SetCanonicalCookieReturnStatus( @@ -654,7 +654,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", false /* modify_http_only */) + this->http_www_foo_.url(), false /* modify_http_only */) .HasExclusionReason( CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); } else { @@ -665,7 +665,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /* modify_http_only */)); + this->http_www_foo_.url(), true /* modify_http_only */)); } // Get all the cookies for a given URL, regardless of properties. This 'get()' @@ -743,28 +743,28 @@ TYPED_TEST_P(CookieStoreTest, SecureEnforcement) { "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), true, false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + http_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), true, false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT), - "https", true /*modify_httponly*/)); + https_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT), - "https", true /*modify_httponly*/)); + https_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT), - "http", true /*modify_httponly*/)); + http_url, true /*modify_httponly*/)); } // The iOS networking stack uses the iOS cookie parser, which we do not diff --git a/chromium/net/cookies/cookie_util.cc b/chromium/net/cookies/cookie_util.cc index ed58360cd75..7f3afe4e178 100644 --- a/chromium/net/cookies/cookie_util.cc +++ b/chromium/net/cookies/cookie_util.cc @@ -9,8 +9,9 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/check.h" #include "base/feature_list.h" -#include "base/logging.h" +#include "base/notreached.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/string_tokenizer.h" @@ -21,12 +22,15 @@ #include "net/base/url_util.h" #include "net/http/http_util.h" #include "url/gurl.h" +#include "url/url_constants.h" namespace net { namespace cookie_util { namespace { +using ContextType = CookieOptions::SameSiteCookieContext::ContextType; + base::Time MinNonNullTime() { return base::Time::FromInternalValue(1); } @@ -74,47 +78,45 @@ bool SaturatedTimeFromUTCExploded(const base::Time::Exploded& exploded, return false; } -CookieOptions::SameSiteCookieContext::CrossSchemeness ComputeSchemeChange( - const GURL& url, - const SiteForCookies& site_for_cookies) { - - CookieOptions::SameSiteCookieContext::CrossSchemeness cross_schemeness = - CookieOptions::SameSiteCookieContext::CrossSchemeness::NONE; - bool url_secure = url.SchemeIsCryptographic(); - bool site_for_cookies_secure = - GURL::SchemeIsCryptographic(site_for_cookies.scheme()); - - if (url_secure && !site_for_cookies_secure) { - cross_schemeness = - CookieOptions::SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE; - } else if (!url_secure && site_for_cookies_secure) { - cross_schemeness = - CookieOptions::SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE; +ContextType ComputeSameSiteContext(const GURL& url, + const SiteForCookies& site_for_cookies, + const base::Optional<url::Origin>& initiator, + bool compute_schemefully) { + if (site_for_cookies.IsFirstPartyWithSchemefulMode(url, + compute_schemefully)) { + // Create a SiteForCookies object from the initiator so that we can reuse + // IsFirstPartyWithSchemefulMode(). + if (!initiator || + SiteForCookies::FromOrigin(initiator.value()) + .IsFirstPartyWithSchemefulMode(url, compute_schemefully)) { + return ContextType::SAME_SITE_STRICT; + } else { + return ContextType::SAME_SITE_LAX; + } } - - return cross_schemeness; + return ContextType::CROSS_SITE; } -CookieOptions::SameSiteCookieContext ComputeSameSiteContext( +CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSet( const GURL& url, const SiteForCookies& site_for_cookies, - const base::Optional<url::Origin>& initiator) { - CookieOptions::SameSiteCookieContext same_site_type( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE); - if (site_for_cookies.IsFirstParty(url)) { - // Create a SiteForCookies object from the initiator so that we can reuse - // IsFirstParty(). - if (!initiator || - SiteForCookies::FromOrigin(initiator.value()).IsFirstParty(url)) { - same_site_type.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT; - } else { - same_site_type.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX; - } + bool force_ignore_site_for_cookies) { + if (force_ignore_site_for_cookies) + return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet(); + + // Schemeless check + if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, false)) { + return CookieOptions::SameSiteCookieContext(ContextType::CROSS_SITE, + ContextType::CROSS_SITE); } - same_site_type.cross_schemeness = ComputeSchemeChange(url, site_for_cookies); - return same_site_type; + + // Schemeful check + if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, true)) { + return CookieOptions::SameSiteCookieContext(ContextType::SAME_SITE_LAX, + ContextType::CROSS_SITE); + } + + return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet(); } } // namespace @@ -123,6 +125,12 @@ bool DomainIsHostOnly(const std::string& domain_string) { return (domain_string.empty() || domain_string[0] != '.'); } +std::string CookieDomainAsHost(const std::string& cookie_domain) { + if (DomainIsHostOnly(cookie_domain)) + return cookie_domain; + return cookie_domain.substr(1); +} + std::string GetEffectiveDomain(const std::string& scheme, const std::string& host) { if (scheme == "http" || scheme == "https" || scheme == "ws" || @@ -132,9 +140,7 @@ std::string GetEffectiveDomain(const std::string& scheme, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); } - if (!DomainIsHostOnly(host)) - return host.substr(1); - return host; + return CookieDomainAsHost(host); } bool GetCookieDomainWithString(const GURL& url, @@ -324,9 +330,19 @@ GURL CookieOriginToURL(const std::string& domain, bool is_https) { if (domain.empty()) return GURL(); - const std::string scheme = is_https ? "https" : "http"; - const std::string host = domain[0] == '.' ? domain.substr(1) : domain; - return GURL(scheme + "://" + host); + const std::string scheme = is_https ? url::kHttpsScheme : url::kHttpScheme; + return GURL(scheme + url::kStandardSchemeSeparator + + CookieDomainAsHost(domain) + "/"); +} + +GURL SimulatedCookieSource(const CanonicalCookie& cookie, + const std::string& source_scheme) { + // Note: cookie.DomainWithoutDot() could be empty for e.g. file cookies. + if (cookie.DomainWithoutDot().empty() || source_scheme.empty()) + return GURL(); + + return GURL(source_scheme + url::kStandardSchemeSeparator + + cookie.DomainWithoutDot() + cookie.Path()); } bool IsDomainMatch(const std::string& domain, const std::string& host) { @@ -421,7 +437,7 @@ CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest( const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies) { + bool force_ignore_site_for_cookies) { // Set SameSiteCookieMode according to the rules laid out in // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02: // @@ -446,25 +462,27 @@ CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest( // but appear like cross-site ones. // // * Otherwise, do not include same-site cookies. - CookieOptions::SameSiteCookieContext same_site_context; - if (attach_same_site_cookies) { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT; - same_site_context.cross_schemeness = - ComputeSchemeChange(url, site_for_cookies); - return same_site_context; - } + if (force_ignore_site_for_cookies) + return CookieOptions::SameSiteCookieContext::MakeInclusive(); + + CookieOptions::SameSiteCookieContext same_site_context; - same_site_context = ComputeSameSiteContext(url, site_for_cookies, initiator); + same_site_context.set_context( + ComputeSameSiteContext(url, site_for_cookies, initiator, false)); + same_site_context.set_schemeful_context( + ComputeSameSiteContext(url, site_for_cookies, initiator, true)); // If the method is safe, the context is Lax. Otherwise, make a note that // the method is unsafe. - if (same_site_context.context == - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX && - !net::HttpUtil::IsMethodSafe(http_method)) { - same_site_context.context = CookieOptions::SameSiteCookieContext:: - ContextType::SAME_SITE_LAX_METHOD_UNSAFE; + if (!net::HttpUtil::IsMethodSafe(http_method)) { + if (same_site_context.context() == ContextType::SAME_SITE_LAX) { + same_site_context.set_context(ContextType::SAME_SITE_LAX_METHOD_UNSAFE); + } + if (same_site_context.schemeful_context() == ContextType::SAME_SITE_LAX) { + same_site_context.set_schemeful_context( + ContextType::SAME_SITE_LAX_METHOD_UNSAFE); + } } return same_site_context; @@ -474,71 +492,63 @@ NET_EXPORT CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptGet(const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies) { - if (attach_same_site_cookies) { - CookieOptions::SameSiteCookieContext same_site_context( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT); - same_site_context.cross_schemeness = - ComputeSchemeChange(url, site_for_cookies); - return same_site_context; - } - return ComputeSameSiteContext(url, site_for_cookies, initiator); + bool force_ignore_site_for_cookies) { + if (force_ignore_site_for_cookies) + return CookieOptions::SameSiteCookieContext::MakeInclusive(); + + CookieOptions::SameSiteCookieContext same_site_context; + + same_site_context.set_context( + ComputeSameSiteContext(url, site_for_cookies, initiator, false)); + same_site_context.set_schemeful_context( + ComputeSameSiteContext(url, site_for_cookies, initiator, true)); + + return same_site_context; } CookieOptions::SameSiteCookieContext ComputeSameSiteContextForResponse( const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies) { - CookieOptions::SameSiteCookieContext same_site_context; + bool force_ignore_site_for_cookies) { // |initiator| is here in case it'll be decided to ignore |site_for_cookies| // for entirely browser-side requests (see https://crbug.com/958335). - if (attach_same_site_cookies || site_for_cookies.IsFirstParty(url)) { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX; - } else { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE; - } - same_site_context.cross_schemeness = - ComputeSchemeChange(url, site_for_cookies); - return same_site_context; + + return ComputeSameSiteContextForSet(url, site_for_cookies, + force_ignore_site_for_cookies); } CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptSet( const GURL& url, const SiteForCookies& site_for_cookies, - bool attach_same_site_cookies) { - CookieOptions::SameSiteCookieContext same_site_context; - if (attach_same_site_cookies || site_for_cookies.IsFirstParty(url)) { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX; - } else { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE; - } - same_site_context.cross_schemeness = - ComputeSchemeChange(url, site_for_cookies); - return same_site_context; + bool force_ignore_site_for_cookies) { + return ComputeSameSiteContextForSet(url, site_for_cookies, + force_ignore_site_for_cookies); } CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSubresource( const GURL& url, const SiteForCookies& site_for_cookies, - bool attach_same_site_cookies) { - CookieOptions::SameSiteCookieContext same_site_context; + bool force_ignore_site_for_cookies) { + if (force_ignore_site_for_cookies) + return CookieOptions::SameSiteCookieContext::MakeInclusive(); + // If the URL is same-site as site_for_cookies it's same-site as all frames // in the tree from the initiator frame up --- including the initiator frame. - if (attach_same_site_cookies || site_for_cookies.IsFirstParty(url)) { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT; - } else { - same_site_context.context = - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE; + + // Schemeless check + if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, false)) { + return CookieOptions::SameSiteCookieContext(ContextType::CROSS_SITE, + ContextType::CROSS_SITE); } - same_site_context.cross_schemeness = - ComputeSchemeChange(url, site_for_cookies); - return same_site_context; + + // Schemeful check + if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, true)) { + return CookieOptions::SameSiteCookieContext(ContextType::SAME_SITE_STRICT, + ContextType::CROSS_SITE); + } + + return CookieOptions::SameSiteCookieContext::MakeInclusive(); } bool IsSameSiteByDefaultCookiesEnabled() { @@ -551,6 +561,10 @@ bool IsCookiesWithoutSameSiteMustBeSecureEnabled() { features::kCookiesWithoutSameSiteMustBeSecure); } +bool IsSchemefulSameSiteEnabled() { + return base::FeatureList::IsEnabled(features::kSchemefulSameSite); +} + bool IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled() { return IsSameSiteByDefaultCookiesEnabled() && base::FeatureList::IsEnabled( diff --git a/chromium/net/cookies/cookie_util.h b/chromium/net/cookies/cookie_util.h index 19c63715b3d..3634058c1b0 100644 --- a/chromium/net/cookies/cookie_util.h +++ b/chromium/net/cookies/cookie_util.h @@ -48,6 +48,13 @@ NET_EXPORT bool GetCookieDomainWithString(const GURL& url, // i.e. it doesn't begin with a leading '.' character. NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string); +// If |cookie_domain| is nonempty and starts with a "." character, this returns +// the substring of |cookie_domain| without the leading dot. (Note only one +// leading dot is stripped, if there are multiple.) Otherwise it returns +// |cookie_domain|. This is useful for converting from CanonicalCookie's +// representation of a cookie domain to the RFC's notion of a cookie's domain. +NET_EXPORT std::string CookieDomainAsHost(const std::string& cookie_domain); + // Parses the string with the cookie expiration time (very forgivingly). // Returns the "null" time on failure. // @@ -59,8 +66,16 @@ NET_EXPORT base::Time ParseCookieExpirationTime(const std::string& time_string); // Convenience for converting a cookie origin (domain and https pair) to a URL. NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https); -// Returns true if the cookie |domain| matches the given |host| as described -// in section 5.1.3 of RFC 6265. +// Returns a URL that could have been the cookie's source. +// Not guaranteed to actually be the URL that set the cookie. Not guaranteed to +// be a valid GURL. Intended as a shim for SetCanonicalCookieAsync calls, where +// a source URL is required but only a source scheme may be available. +NET_EXPORT GURL SimulatedCookieSource(const CanonicalCookie& cookie, + const std::string& source_scheme); + +// |domain| is the output of cookie.Domain() for some cookie. This returns true +// if a |domain| indicates that the cookie can be accessed by |host|. +// See comment on CanonicalCookie::IsDomainMatch(). NET_EXPORT bool IsDomainMatch(const std::string& domain, const std::string& host); @@ -98,7 +113,7 @@ NET_EXPORT std::string SerializeRequestCookieLine( // the user directly interacting with the browser UI, e.g. entering a URL // or selecting a bookmark. // -// If |attach_same_site_cookies| is specified, all SameSite cookies will be +// If |force_ignore_site_for_cookies| is specified, all SameSite cookies will be // attached, i.e. this will return SAME_SITE_STRICT. This flag is set to true // when the |site_for_cookies| is a chrome:// URL embedding a secure origin, // among other scenarios. @@ -116,50 +131,51 @@ ComputeSameSiteContextForRequest(const std::string& http_method, const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies); + bool force_ignore_site_for_cookies); // As above, but applying for scripts. |initiator| here should be the initiator // used when fetching the document. -// If |attach_same_site_cookies| is true, this returns SAME_SITE_STRICT. +// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_STRICT. NET_EXPORT CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptGet(const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies); + bool force_ignore_site_for_cookies); // Determines which of the cookies for |url| can be set from a network response, // with respect to the SameSite attribute. This will only return CROSS_SITE or // SAME_SITE_LAX (cookie sets of SameSite=strict cookies are permitted in same // contexts that sets of SameSite=lax cookies are). -// If |attach_same_site_cookies| is true, this returns SAME_SITE_LAX. +// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_LAX. NET_EXPORT CookieOptions::SameSiteCookieContext ComputeSameSiteContextForResponse(const GURL& url, const SiteForCookies& site_for_cookies, const base::Optional<url::Origin>& initiator, - bool attach_same_site_cookies); + bool force_ignore_site_for_cookies); // Determines which of the cookies for |url| can be set from a script context, // with respect to the SameSite attribute. This will only return CROSS_SITE or // SAME_SITE_LAX (cookie sets of SameSite=strict cookies are permitted in same // contexts that sets of SameSite=lax cookies are). -// If |attach_same_site_cookies| is true, this returns SAME_SITE_LAX. +// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_LAX. NET_EXPORT CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptSet(const GURL& url, const SiteForCookies& site_for_cookies, - bool attach_same_site_cookies); + bool force_ignore_site_for_cookies); // Determines which of the cookies for |url| can be accessed when fetching a // subresources. This is either CROSS_SITE or SAME_SITE_STRICT, // since the initiator for a subresource is the frame loading it. NET_EXPORT CookieOptions::SameSiteCookieContext -// If |attach_same_site_cookies| is true, this returns SAME_SITE_STRICT. +// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_STRICT. ComputeSameSiteContextForSubresource(const GURL& url, const SiteForCookies& site_for_cookies, - bool attach_same_site_cookies); + bool force_ignore_site_for_cookies); // Returns whether the respective SameSite feature is enabled. NET_EXPORT bool IsSameSiteByDefaultCookiesEnabled(); NET_EXPORT bool IsCookiesWithoutSameSiteMustBeSecureEnabled(); +NET_EXPORT bool IsSchemefulSameSiteEnabled(); bool IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled(); bool IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled(); diff --git a/chromium/net/cookies/cookie_util_unittest.cc b/chromium/net/cookies/cookie_util_unittest.cc index 13f41bae1f5..b95e1d6da26 100644 --- a/chromium/net/cookies/cookie_util_unittest.cc +++ b/chromium/net/cookies/cookie_util_unittest.cc @@ -225,6 +225,49 @@ TEST(CookieUtilTest, TestRequestCookieParsing) { } } +TEST(CookieUtilTest, SimulatedCookieSource) { + GURL secure_url("https://b.a.com"); + GURL insecure_url("http://b.a.com"); + + struct { + std::string cookie; + std::string source_scheme; + std::string expected_simulated_source; + } kTests[]{ + {"cookie=foo", "http", "http://b.a.com/"}, + {"cookie=foo", "https", "https://b.a.com/"}, + {"cookie=foo", "wss", "wss://b.a.com/"}, + {"cookie=foo", "file", "file://b.a.com/"}, + {"cookie=foo; Domain=b.a.com", "https", "https://b.a.com/"}, + {"cookie=foo; Domain=a.com", "https", "https://a.com/"}, + {"cookie=foo; Domain=.b.a.com", "https", "https://b.a.com/"}, + {"cookie=foo; Domain=.a.com", "https", "https://a.com/"}, + {"cookie=foo; Path=/", "https", "https://b.a.com/"}, + {"cookie=foo; Path=/bar", "https", "https://b.a.com/bar"}, + {"cookie=foo; Domain=b.a.com; Path=/", "https", "https://b.a.com/"}, + {"cookie=foo; Domain=b.a.com; Path=/bar", "https", "https://b.a.com/bar"}, + {"cookie=foo; Domain=a.com; Path=/", "https", "https://a.com/"}, + {"cookie=foo; Domain=a.com; Path=/bar", "https", "https://a.com/bar"}, + }; + + for (const auto& test : kTests) { + std::vector<std::unique_ptr<CanonicalCookie>> cookies; + // It shouldn't depend on the cookie's secureness or actual source scheme. + cookies.push_back(CanonicalCookie::Create( + insecure_url, test.cookie, base::Time::Now(), base::nullopt)); + cookies.push_back(CanonicalCookie::Create( + secure_url, test.cookie, base::Time::Now(), base::nullopt)); + cookies.push_back( + CanonicalCookie::Create(secure_url, test.cookie + "; Secure", + base::Time::Now(), base::nullopt)); + for (const auto& cookie : cookies) { + GURL simulated_source = + cookie_util::SimulatedCookieSource(*cookie, test.source_scheme); + EXPECT_EQ(GURL(test.expected_simulated_source), simulated_source); + } + } +} + TEST(CookieUtilTest, TestGetEffectiveDomain) { // Note: registry_controlled_domains::GetDomainAndRegistry is tested in its // own unittests. @@ -261,25 +304,24 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( - SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptGet( GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( - SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://notexample.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), @@ -287,32 +329,53 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), url::Origin::Create(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForScriptGet( - GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://notexample.com")), - url::Origin::Create(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://notexample.com")), + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForScriptGet( - GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("https://notexample.com")), - url::Origin::Create(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://notexample.com")), + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://a.com"), SiteForCookies::FromUrl(GURL("http://b.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); + + // |site_for_cookies| not being schemefully_same -> it's cross-site. + SiteForCookies insecure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("http://example.com")); + insecure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("http://example.com"), insecure_not_schemefully_same, + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + + SiteForCookies secure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("https://example.com")); + secure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("https://example.com"), secure_not_schemefully_same, + url::Origin::Create(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); // Same |site_for_cookies|, but not |initiator| -> it's same-site lax. EXPECT_EQ( @@ -322,27 +385,46 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); // This isn't a full on origin check --- subdomains and different schema are - // accepted. + // accepted. For SameSiteCookieContext::schemeful_context the scheme is + // considered. EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX), cookie_util::ComputeSameSiteContextForScriptGet( - GURL("https://example.com"), + GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), - url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + url::Origin::Create(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX), cookie_util::ComputeSameSiteContextForScriptGet( - GURL("http://example.com"), + GURL("https://example.com"), SiteForCookies::FromUrl(GURL("https://example.com")), - url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("http://from-elsewhere.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("http://from-elsewhere.com")), + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), @@ -350,7 +432,7 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub2.example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), @@ -358,7 +440,24 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub.example.com:8080")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); + + // wss/https and http/ws are considered the same for schemeful purposes. + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("wss://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("ws://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); // nullopt |initiator| is trusted for purposes of strict, an opaque one isn't. EXPECT_EQ(SameSiteCookieContext( @@ -367,25 +466,25 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ( - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForScriptGet( - GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://example.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ( - SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForScriptGet( - GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("https://example.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptGet( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -393,7 +492,8 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), - base::nullopt /*initiator*/, false /* attach_same_site_cookies */)); + base::nullopt /*initiator*/, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -401,9 +501,9 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin(), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); - // |attach_same_site_cookies| causes (some variant of) SAME_SITE_STRICT to be + // |force_ignore_site_for_cookies| causes SAME_SITE_STRICT to be // returned. EXPECT_EQ( SameSiteCookieContext( @@ -411,7 +511,8 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt /*initiator*/, true /* attach_same_site_cookies */)); + base::nullopt /*initiator*/, + true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -420,7 +521,7 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), url::Origin::Create(GURL("http://example.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -428,7 +529,7 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://a.com"), SiteForCookies::FromUrl(GURL("http://b.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -437,25 +538,23 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForScriptGet( GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForScriptGet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -464,7 +563,7 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub2.example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -473,37 +572,59 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForScriptGet) { GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub.example.com:8080")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); } TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { using SameSiteCookieContext = CookieOptions::SameSiteCookieContext; - EXPECT_EQ( - SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), - cookie_util::ComputeSameSiteContextForRequest( - "GET", GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt /*initiator*/, false /*attach_same_site_cookies*/)); - EXPECT_EQ( - SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForRequest( - "GET", GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt /*initiator*/, false /*attach_same_site_cookies*/)); + EXPECT_EQ(SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("http://notexample.com")), + base::nullopt /*initiator*/, + false /*force_ignore_site_for_cookies*/)); + EXPECT_EQ(SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://notexample.com")), + base::nullopt /*initiator*/, + false /*force_ignore_site_for_cookies*/)); + + EXPECT_EQ(SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://notexample.com")), + base::nullopt /*initiator*/, + false /*force_ignore_site_for_cookies*/)); + // |site_for_cookies| not being schemefully_same -> it's cross-site. + SiteForCookies insecure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("http://example.com")); + insecure_not_schemefully_same.SetSchemefullySameForTesting(false); EXPECT_EQ( SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( - "GET", GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("https://notexample.com")), - base::nullopt /*initiator*/, false /*attach_same_site_cookies*/)); + "GET", GURL("http://example.com"), insecure_not_schemefully_same, + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + + SiteForCookies secure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("https://example.com")); + secure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("https://example.com"), secure_not_schemefully_same, + url::Origin::Create(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); - // |attach_same_site_cookies| = true bypasses all checks. + // |force_ignore_site_for_cookies| = true bypasses all checks. EXPECT_EQ( SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT), @@ -511,7 +632,7 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "GET", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /*attach_same_site_cookies*/)); + true /*force_ignore_site_for_cookies*/)); EXPECT_EQ( SameSiteCookieContext( @@ -520,7 +641,7 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "POST", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /*attach_same_site_cookies*/)); + true /*force_ignore_site_for_cookies*/)); EXPECT_EQ( SameSiteCookieContext( @@ -529,7 +650,7 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "GET", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://question.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - true /*attach_same_site_cookies*/)); + true /*force_ignore_site_for_cookies*/)); EXPECT_EQ( SameSiteCookieContext( @@ -538,7 +659,7 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "GET", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ( SameSiteCookieContext( @@ -547,45 +668,80 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "POST", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "GET", GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "POST", GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "GET", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "POST", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://example.com")), url::Origin::Create(GURL("http://example.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); // Normally, lax requests also require a safe method. + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("http://example.com")), + false /*force_ignore_site_for_cookies*/)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("https://example.com")), + false /*force_ignore_site_for_cookies*/)); + + // wss/https and http/ws are considered the same for schemeful purposes. + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("wss://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("ws://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), @@ -593,7 +749,7 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "GET", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ( SameSiteCookieContext( @@ -602,25 +758,45 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "HEAD", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForRequest( - "GET", GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://example.com")), - url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("http://from-elsewhere.com")), + false /*force_ignore_site_for_cookies*/)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForRequest( - "GET", GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("https://example.com")), - url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForRequest( + "GET", GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("http://from-elsewhere.com")), + false /*force_ignore_site_for_cookies*/)); + + EXPECT_EQ( + SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), + cookie_util::ComputeSameSiteContextForRequest( + "POST", GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + url::Origin::Create(GURL("http://example.com")), + false /*force_ignore_site_for_cookies*/)); + + EXPECT_EQ( + SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE), + cookie_util::ComputeSameSiteContextForRequest( + "POST", GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + url::Origin::Create(GURL("https://example.com")), + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext(CookieOptions::SameSiteCookieContext:: ContextType::SAME_SITE_LAX_METHOD_UNSAFE), @@ -628,34 +804,25 @@ TEST(CookieUtilTest, ComputeSameSiteContextForRequest) { "POST", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "POST", GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest( "POST", GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://example.com")), url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); - - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForRequest( - "POST", GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://example.com")), - url::Origin::Create(GURL("http://from-elsewhere.com")), - false /*attach_same_site_cookies*/)); + false /*force_ignore_site_for_cookies*/)); } TEST(CookieUtilTest, ComputeSameSiteContextForSet) { @@ -665,64 +832,168 @@ TEST(CookieUtilTest, ComputeSameSiteContextForSet) { cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForResponse( GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://notexample.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); - // Same as above except |attach_same_site_cookies| makes it return LAX. + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForResponse( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), base::nullopt, + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForResponse( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), base::nullopt, + false /* force_ignore_site_for_cookies */)); + + // Same as above except |force_ignore_site_for_cookies| makes it return LAX. EXPECT_EQ( SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), base::nullopt, - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), base::nullopt, + true /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), base::nullopt, + true /* force_ignore_site_for_cookies */)); + + // |site_for_cookies| not being schemefully_same -> it's cross-site. + SiteForCookies insecure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("http://example.com")); + insecure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForResponse( + GURL("http://example.com"), insecure_not_schemefully_same, + base::nullopt, false /* force_ignore_site_for_cookies */)); + + SiteForCookies secure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("https://example.com")); + secure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForResponse( + GURL("https://example.com"), secure_not_schemefully_same, + base::nullopt, false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptSet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptSet( GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForScriptSet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("https://notexample.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); - // Same as above except |attach_same_site_cookies| makes it return LAX. + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); + + // Same as above except |force_ignore_site_for_cookies| makes it return LAX. EXPECT_EQ( SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), cookie_util::ComputeSameSiteContextForScriptSet( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + true /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + true /* force_ignore_site_for_cookies */)); + + // |site_for_cookies| not being schemefully_same -> it's cross-site. + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("http://example.com"), insecure_not_schemefully_same, + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("https://example.com"), secure_not_schemefully_same, + false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -730,42 +1001,58 @@ TEST(CookieUtilTest, ComputeSameSiteContextForSet) { cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com/dir"), SiteForCookies::FromUrl(GURL("http://sub.example.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com/dir"), SiteForCookies::FromUrl(GURL("http://sub.example.com")), - base::nullopt, true /* attach_same_site_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForResponse( - GURL("http://example.com/dir"), - SiteForCookies::FromUrl(GURL("https://sub.example.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, true /* force_ignore_site_for_cookies */)); + + // wss/https and http/ws are considered the same for schemeful purposes. + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("ws://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), base::nullopt, + true /* force_ignore_site_for_cookies */)); + + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("wss://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), base::nullopt, + true /* force_ignore_site_for_cookies */)); + EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForResponse( GURL("http://example.com/dir"), SiteForCookies::FromUrl(GURL("https://sub.example.com")), - base::nullopt, true /* attach_same_site_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForResponse( - GURL("https://example.com/dir"), - SiteForCookies::FromUrl(GURL("http://sub.example.com")), - base::nullopt, false /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("http://example.com/dir"), + SiteForCookies::FromUrl(GURL("https://sub.example.com")), + base::nullopt, true /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForResponse( GURL("https://example.com/dir"), SiteForCookies::FromUrl(GURL("http://sub.example.com")), - base::nullopt, true /* attach_same_site_cookies */)); + base::nullopt, false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForResponse( + GURL("https://example.com/dir"), + SiteForCookies::FromUrl(GURL("http://sub.example.com")), + base::nullopt, true /* force_ignore_site_for_cookies */)); EXPECT_EQ( SameSiteCookieContext( @@ -773,21 +1060,35 @@ TEST(CookieUtilTest, ComputeSameSiteContextForSet) { cookie_util::ComputeSameSiteContextForScriptSet( GURL("http://example.com/dir"), SiteForCookies::FromUrl(GURL("http://sub.example.com")), - false /* attach_same_site_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForScriptSet( - GURL("http://example.com/dir"), - SiteForCookies::FromUrl(GURL("https://sub.example.com")), - false /* attach_same_site_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::SAME_SITE_LAX, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForScriptSet( - GURL("https://example.com/dir"), - SiteForCookies::FromUrl(GURL("http://sub.example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("ws://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("wss://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("http://example.com/dir"), + SiteForCookies::FromUrl(GURL("https://sub.example.com")), + false /* force_ignore_site_for_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForScriptSet( + GURL("https://example.com/dir"), + SiteForCookies::FromUrl(GURL("http://sub.example.com")), + false /* force_ignore_site_for_cookies */)); } TEST(CookieUtilTest, TestComputeSameSiteContextForSubresource) { @@ -798,62 +1099,116 @@ TEST(CookieUtilTest, TestComputeSameSiteContextForSubresource) { cookie_util::ComputeSameSiteContextForSubresource( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), - cookie_util::ComputeSameSiteContextForSubresource( - GURL("https://example.com"), - SiteForCookies::FromUrl(GURL("http://notexample.com")), - false /* attach_same_site_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("https://example.com"), + SiteForCookies::FromUrl(GURL("http://notexample.com")), + false /* force_ignore_site_for_cookies */)); - EXPECT_EQ(SameSiteCookieContext( - SameSiteCookieContext::ContextType::CROSS_SITE, - SameSiteCookieContext::CrossSchemeness::SECURE_INSECURE), - cookie_util::ComputeSameSiteContextForSubresource( - GURL("http://example.com"), - SiteForCookies::FromUrl(GURL("https://notexample.com")), - false /* attach_same_site_cookies */)); + EXPECT_EQ( + SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://notexample.com")), + false /* force_ignore_site_for_cookies */)); - // Same as above except |attach_same_site_cookies| makes it return STRICT. + // Same as above except |force_ignore_site_for_cookies| makes it return + // STRICT. EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForSubresource( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://notexample.com")), - true /* attach_same_site_cookies */)); + true /* force_ignore_site_for_cookies */)); + + // |site_for_cookies| not being schemefully_same -> it's cross-site. + SiteForCookies insecure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("http://example.com")); + insecure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("http://example.com"), insecure_not_schemefully_same, + false /* force_ignore_site_for_cookies */)); + + SiteForCookies secure_not_schemefully_same = + SiteForCookies::FromUrl(GURL("https://example.com")); + secure_not_schemefully_same.SetSchemefullySameForTesting(false); + EXPECT_EQ( + SameSiteCookieContext( + CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("https://example.com"), secure_not_schemefully_same, + false /* force_ignore_site_for_cookies */)); // This isn't a full on origin check --- subdomains and different schema are - // accepted. + // accepted. For SameSiteCookieContext::schemeful_context the scheme is + // considered. EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT, - SameSiteCookieContext::CrossSchemeness::INSECURE_SECURE), + SameSiteCookieContext::ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForSubresource( GURL("https://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT, + SameSiteCookieContext::ContextType::CROSS_SITE), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("http://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForSubresource( GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub2.example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForSubresource( GURL("http://sub.example.com"), SiteForCookies::FromUrl(GURL("http://sub.example.com:8080")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); EXPECT_EQ(SameSiteCookieContext( SameSiteCookieContext::ContextType::SAME_SITE_STRICT), cookie_util::ComputeSameSiteContextForSubresource( GURL("http://example.com"), SiteForCookies::FromUrl(GURL("http://example.com")), - false /* attach_same_site_cookies */)); + false /* force_ignore_site_for_cookies */)); + + // wss/https and http/ws are considered the same for schemeful purposes. + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("ws://example.com"), + SiteForCookies::FromUrl(GURL("http://example.com")), + false /* force_ignore_site_for_cookies */)); + + EXPECT_EQ(SameSiteCookieContext( + SameSiteCookieContext::ContextType::SAME_SITE_STRICT), + cookie_util::ComputeSameSiteContextForSubresource( + GURL("wss://example.com"), + SiteForCookies::FromUrl(GURL("https://example.com")), + false /* force_ignore_site_for_cookies */)); } TEST(CookieUtilTest, AdaptCookieInclusionStatusToBool) { diff --git a/chromium/net/cookies/parse_cookie_line_fuzzer.cc b/chromium/net/cookies/parse_cookie_line_fuzzer.cc index e65412f1a82..4ea774ab748 100644 --- a/chromium/net/cookies/parse_cookie_line_fuzzer.cc +++ b/chromium/net/cookies/parse_cookie_line_fuzzer.cc @@ -7,7 +7,7 @@ #include <fuzzer/FuzzedDataProvider.h> -#include "base/logging.h" +#include "base/check_op.h" #include "net/cookies/parsed_cookie.h" const std::string GetArbitraryString(FuzzedDataProvider* data_provider) { diff --git a/chromium/net/cookies/site_for_cookies.cc b/chromium/net/cookies/site_for_cookies.cc index 8c152a67943..759b4e7a372 100644 --- a/chromium/net/cookies/site_for_cookies.cc +++ b/chromium/net/cookies/site_for_cookies.cc @@ -7,6 +7,7 @@ #include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/cookies/cookie_util.h" namespace net { @@ -20,7 +21,7 @@ std::string RegistrableDomainOrHost(const std::string& host) { } // namespace -SiteForCookies::SiteForCookies() = default; +SiteForCookies::SiteForCookies() : schemefully_same_(false) {} SiteForCookies::SiteForCookies(const SiteForCookies& other) = default; SiteForCookies::SiteForCookies(SiteForCookies&& other) = default; @@ -35,6 +36,7 @@ SiteForCookies& SiteForCookies::operator=(SiteForCookies&& site_for_cookies) = // static bool SiteForCookies::FromWire(const std::string& scheme, const std::string& registrable_domain, + bool schemefully_same, SiteForCookies* out) { // Make sure scheme meets precondition of methods like // GURL::SchemeIsCryptographic. @@ -46,6 +48,8 @@ bool SiteForCookies::FromWire(const std::string& scheme, if (registrable_domain != candidate.registrable_domain_) return false; + candidate.schemefully_same_ = schemefully_same; + *out = std::move(candidate); return true; } @@ -65,25 +69,34 @@ SiteForCookies SiteForCookies::FromUrl(const GURL& url) { } std::string SiteForCookies::ToDebugString() const { + std::string same_scheme_string = schemefully_same_ ? "true" : "false"; return base::StrCat({"SiteForCookies: {scheme=", scheme_, - "; registrable_domain=", registrable_domain_, "}"}); + "; registrable_domain=", registrable_domain_, + "; schemefully_same=", same_scheme_string, "}"}); } bool SiteForCookies::IsFirstParty(const GURL& url) const { - if (scheme_.empty() || !url.is_valid()) - return false; - - std::string other_registrable_domain = RegistrableDomainOrHost(url.host()); + return IsFirstPartyWithSchemefulMode( + url, cookie_util::IsSchemefulSameSiteEnabled()); +} - if (registrable_domain_.empty()) - return other_registrable_domain.empty() && (scheme_ == url.scheme()); +bool SiteForCookies::IsFirstPartyWithSchemefulMode( + const GURL& url, + bool compute_schemefully) const { + if (compute_schemefully) + return IsSchemefullyFirstParty(url); - return registrable_domain_ == other_registrable_domain; + return IsSchemelesslyFirstParty(url); } bool SiteForCookies::IsEquivalent(const SiteForCookies& other) const { - if (scheme_.empty()) - return other.scheme_.empty(); + if (IsNull()) + return other.IsNull(); + + if (cookie_util::IsSchemefulSameSiteEnabled() && + !CompatibleScheme(other.scheme())) { + return false; + } if (registrable_domain_.empty()) return other.registrable_domain_.empty() && (scheme_ == other.scheme_); @@ -91,16 +104,91 @@ bool SiteForCookies::IsEquivalent(const SiteForCookies& other) const { return registrable_domain_ == other.registrable_domain_; } +void SiteForCookies::MarkIfCrossScheme(const url::Origin& other) { + // If |this| is IsNull() then |this| doesn't match anything which means that + // the scheme check is pointless. Also exit early if schemefully_same_ is + // already false. + if (IsNull() || !schemefully_same_) + return; + + // Mark if |other| is opaque. Opaque origins shouldn't match. + if (other.opaque()) { + schemefully_same_ = false; + return; + } + + if (CompatibleScheme(other.scheme())) + return; + + // The two are cross-scheme to each other. + schemefully_same_ = false; +} + GURL SiteForCookies::RepresentativeUrl() const { - if (scheme_.empty()) + if (IsNull()) return GURL(); GURL result(base::StrCat({scheme_, "://", registrable_domain_, "/"})); DCHECK(result.is_valid()); return result; } +bool SiteForCookies::IsNull() const { + if (cookie_util::IsSchemefulSameSiteEnabled()) + return scheme_.empty() || !schemefully_same_; + + return scheme_.empty(); +} + SiteForCookies::SiteForCookies(const std::string& scheme, const std::string& host) - : scheme_(scheme), registrable_domain_(RegistrableDomainOrHost(host)) {} + : scheme_(scheme), + registrable_domain_(RegistrableDomainOrHost(host)), + schemefully_same_(!scheme.empty()) {} + +bool SiteForCookies::CompatibleScheme(const std::string& other_scheme) const { + DCHECK(base::IsStringASCII(other_scheme)); + DCHECK(base::ToLowerASCII(other_scheme) == other_scheme); + + // Exact match case. + if (scheme_ == other_scheme) + return true; + + // ["https", "wss"] case. + if ((scheme_ == url::kHttpsScheme || scheme_ == url::kWssScheme) && + (other_scheme == url::kHttpsScheme || other_scheme == url::kWssScheme)) { + return true; + } + + // ["http", "ws"] case. + if ((scheme_ == url::kHttpScheme || scheme_ == url::kWsScheme) && + (other_scheme == url::kHttpScheme || other_scheme == url::kWsScheme)) { + return true; + } + + return false; +} + +bool SiteForCookies::IsSchemefullyFirstParty(const GURL& url) const { + // Can't use IsNull() as we want the same behavior regardless of + // SchemefulSameSite feature status. + if (scheme_.empty() || !schemefully_same_ || !url.is_valid()) + return false; + + return CompatibleScheme(url.scheme()) && IsSchemelesslyFirstParty(url); +} + +bool SiteForCookies::IsSchemelesslyFirstParty(const GURL& url) const { + // Can't use IsNull() as we want the same behavior regardless of + // SchemefulSameSite feature status. + if (scheme_.empty() || !url.is_valid()) + return false; + + std::string other_registrable_domain = RegistrableDomainOrHost(url.host()); + + if (registrable_domain_.empty()) + return other_registrable_domain.empty() && (scheme_ == url.scheme()); + + return registrable_domain_ == other_registrable_domain; +} } // namespace net diff --git a/chromium/net/cookies/site_for_cookies.h b/chromium/net/cookies/site_for_cookies.h index 680a9a16330..0de33fabeba 100644 --- a/chromium/net/cookies/site_for_cookies.h +++ b/chromium/net/cookies/site_for_cookies.h @@ -23,12 +23,13 @@ namespace net { // 2) They both have empty hostnames and equal schemes. // Invalid URLs are not first party to anything. // -// TODO(morlovich): It may make sense to require scheme to match in case (1) -// too, where the notion of matching makes http/https/ws/wss equivalent, but -// all other schemes are distinct. -// -// This should wait until SiteForCookies type is used everywhere relevant, so -// any changes are consistent. +// With the SchemefulSameSite feature enabled the policy is that two valid URLs +// would be considered the same party if either: +// 1) They both have compatible schemes along with non-empty and equal +// registrable domains or hostnames/IPs. See CompatibleScheme() for more details +// on what it means to have a compatible scheme. +// 2) They both have empty hostnames and exactly equal schemes. Invalid URLs are +// not first party to anything. class NET_EXPORT SiteForCookies { public: // Matches nothing. @@ -50,6 +51,7 @@ class NET_EXPORT SiteForCookies { // did not lie, merely that they are well-formed. static bool FromWire(const std::string& scheme, const std::string& registrable_domain, + bool schemefully_same, SiteForCookies* out); // If the origin is opaque, returns SiteForCookies that matches nothing. @@ -61,16 +63,39 @@ class NET_EXPORT SiteForCookies { // Equivalent to FromOrigin(url::Origin::Create(url)). static SiteForCookies FromUrl(const GURL& url); + // Returns a string with the values of the member variables. + // |schemefully_same| being false does not change the output. std::string ToDebugString() const; // Returns true if |url| should be considered first-party to the context // |this| represents. bool IsFirstParty(const GURL& url) const; + // Don't use this function unless you know what you're doing, if you're unsure + // you probably want IsFirstParty(). + // + // If |compute_schemefully| is true this function will return true if |url| + // should be considered first-party to the context |this| represents when the + // compatibility of the schemes are taken into account. + // + // If |compute_schemefully| is false this function will return true if |url| + // should be considered first-party to the context |this| represents when the + // compatibility of the scheme are not taken into account. Note that schemes + // are still compared for exact equality if neither |this| nor |url| have a + // registered domain. + // + // See CompatibleScheme() for more information on scheme compatibility. + bool IsFirstPartyWithSchemefulMode(const GURL& url, + bool compute_schemefully) const; + // Returns true if |other.IsFirstParty()| is true for exactly the same URLs // as |this->IsFirstParty| (potentially none). bool IsEquivalent(const SiteForCookies& other) const; + // Clears the schemefully_same_ flag if |other|'s scheme is cross-scheme to + // |this|. Schemes are considered cross-scheme if they're !CompatibleScheme(). + void MarkIfCrossScheme(const url::Origin& other); + // Returns a URL that's first party to this SiteForCookies (an empty URL if // none) --- that is, it has the property that // site_for_cookies.IsEquivalent( @@ -87,12 +112,30 @@ class NET_EXPORT SiteForCookies { const std::string& registrable_domain() const { return registrable_domain_; } + // Used for serialization/deserialization. This value is irrelevant if + // IsNull() is true. + bool schemefully_same() const { return schemefully_same_; } + + void SetSchemefullySameForTesting(bool schemefully_same) { + schemefully_same_ = schemefully_same; + } + // Returns true if this SiteForCookies matches nothing. - bool IsNull() const { return scheme_.empty(); } + // If the SchemefulSameSite feature is enabled then !schemefully_same_ causes + // this function to return true. + bool IsNull() const; private: SiteForCookies(const std::string& scheme, const std::string& host); + // Two schemes are considered compatible if they exactly match, they are both + // in ["https", "wss"], or they are both in ["http", "ws"]. + bool CompatibleScheme(const std::string& other_scheme) const; + + bool IsSchemefullyFirstParty(const GURL& url) const; + + bool IsSchemelesslyFirstParty(const GURL& url) const; + // These should be canonicalized appropriately by GURL/url::Origin. // An empty |scheme_| means that this matches nothing. std::string scheme_; @@ -102,6 +145,23 @@ class NET_EXPORT SiteForCookies { // just the bare hostname or IP, or an empty string if this represents // something like file:/// std::string registrable_domain_; + + // Used to indicate if the SiteForCookies would be the same if computed + // schemefully. A schemeful computation means to take the |scheme_| as well as + // the |registrable_domain_| into account when determining first-partyness. + // See CompatibleScheme() for more information on scheme comparison. + // + // True means to treat |this| as-is while false means that |this| should be + // treated as if it matches nothing i.e. IsNull() returns true. + // + // This value is important in the case where the SiteForCookies is being used + // to assess the first-partyness of a sub-frame in a document. + // + // For a SiteForCookies with !scheme_.empty() this value starts as true and + // will only go false via MarkIfCrossScheme(), otherwise this value is + // irrelevant (For tests this value can also be modified by + // SetSchemefullySameForTesting()). + bool schemefully_same_; }; } // namespace net diff --git a/chromium/net/cookies/site_for_cookies_unittest.cc b/chromium/net/cookies/site_for_cookies_unittest.cc index 3583f1a744e..640b9987628 100644 --- a/chromium/net/cookies/site_for_cookies_unittest.cc +++ b/chromium/net/cookies/site_for_cookies_unittest.cc @@ -7,6 +7,8 @@ #include <vector> #include "base/strings/strcat.h" +#include "base/test/scoped_feature_list.h" +#include "net/base/features.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" @@ -15,6 +17,16 @@ namespace net { namespace { +class SchemefulSiteForCookiesTest : public ::testing::Test { + public: + SchemefulSiteForCookiesTest() { + scope_feature_list_.InitAndEnableFeature(features::kSchemefulSameSite); + } + + protected: + base::test::ScopedFeatureList scope_feature_list_; +}; + // Tests that all URLs from |equivalent| produce SiteForCookies that match // URLs in the set and are equivalent to each other, and are distinct and // don't match |distinct|. @@ -70,8 +82,9 @@ TEST(SiteForCookiesTest, Default) { SiteForCookies::FromOrigin(url::Origin()))); EXPECT_EQ("", should_match_none.scheme()); - EXPECT_EQ("SiteForCookies: {scheme=; registrable_domain=}", - should_match_none.ToDebugString()); + EXPECT_EQ( + "SiteForCookies: {scheme=; registrable_domain=; schemefully_same=false}", + should_match_none.ToDebugString()); } TEST(SiteForCookiesTest, Basic) { @@ -89,6 +102,38 @@ TEST(SiteForCookiesTest, Basic) { TestEquivalentAndDistinct(equivalent, distinct, "example.com"); } +// Similar to SiteForCookiesTest_Basic with a focus on testing secure SFCs. +TEST_F(SchemefulSiteForCookiesTest, BasicSecure) { + std::vector<GURL> equivalent = {GURL("https://example.com"), + GURL("wss://example.com"), + GURL("https://sub1.example.com:42/something"), + GURL("wss://sub2.example.com/something")}; + + std::vector<GURL> distinct = { + GURL("http://example.com"), GURL("https://example.org"), + GURL("ws://example.com"), GURL("https://com/i_am_a_tld"), + GURL("file://example.com/helo"), + }; + + TestEquivalentAndDistinct(equivalent, distinct, "example.com"); +} + +// Similar to SiteForCookiesTest_Basic with a focus on testing insecure SFCs. +TEST_F(SchemefulSiteForCookiesTest, BasicInsecure) { + std::vector<GURL> equivalent = {GURL("http://example.com"), + GURL("ws://example.com"), + GURL("http://sub1.example.com:42/something"), + GURL("ws://sub2.example.com/something")}; + + std::vector<GURL> distinct = { + GURL("https://example.com"), GURL("http://example.org"), + GURL("wss://example.com"), GURL("http://com/i_am_a_tld"), + GURL("file://example.com/helo"), + }; + + TestEquivalentAndDistinct(equivalent, distinct, "example.com"); +} + TEST(SiteForCookiesTest, File) { std::vector<GURL> equivalent = {GURL("file:///a/b/c"), GURL("file:///etc/shaaadow")}; @@ -103,6 +148,7 @@ TEST(SiteForCookiesTest, Extension) { url::AddStandardScheme("chrome-extension", url::SCHEME_WITH_HOST); std::vector<GURL> equivalent = {GURL("chrome-extension://abc/"), GURL("chrome-extension://abc/foo.txt"), + GURL("https://abc"), GURL("http://abc"), // This one is disputable. GURL("file://abc/bar.txt")}; @@ -111,6 +157,23 @@ TEST(SiteForCookiesTest, Extension) { TestEquivalentAndDistinct(equivalent, distinct, "abc"); } +// Similar to SiteForCookiesTest_Extension with a focus on ensuring that http(s) +// schemes are distinct. +TEST_F(SchemefulSiteForCookiesTest, Extension) { + url::ScopedSchemeRegistryForTests scoped_registry; + url::AddStandardScheme("chrome-extension", url::SCHEME_WITH_HOST); + std::vector<GURL> equivalent = { + GURL("chrome-extension://abc/"), + GURL("chrome-extension://abc/foo.txt"), + }; + + std::vector<GURL> distinct = {GURL("chrome-extension://def"), + GURL("https://abc"), GURL("http://abc"), + GURL("file://abc/bar.txt")}; + + TestEquivalentAndDistinct(equivalent, distinct, "abc"); +} + TEST(SiteForCookiesTest, NonStandard) { // If we don't register the scheme, nothing matches, even identical ones std::vector<GURL> equivalent; @@ -134,39 +197,210 @@ TEST(SiteForCookiesTest, Blob) { EXPECT_TRUE(from_blob.IsFirstParty(GURL("http://sub.example.org/resource"))); EXPECT_EQ("https", from_blob.scheme()); - EXPECT_EQ("SiteForCookies: {scheme=https; registrable_domain=example.org}", - from_blob.ToDebugString()); + EXPECT_EQ( + "SiteForCookies: {scheme=https; registrable_domain=example.org; " + "schemefully_same=true}", + from_blob.ToDebugString()); EXPECT_EQ("https://example.org/", from_blob.RepresentativeUrl().spec()); EXPECT_TRUE(from_blob.IsEquivalent( SiteForCookies::FromUrl(GURL("http://www.example.org:631")))); } +// Similar to SiteForCookiesTest_Blob with a focus on a secure blob. +TEST_F(SchemefulSiteForCookiesTest, SecureBlob) { + SiteForCookies from_blob = SiteForCookies::FromUrl( + GURL("blob:https://example.org/9115d58c-bcda-ff47-86e5-083e9a2153041")); + + EXPECT_TRUE(from_blob.IsFirstParty(GURL("https://sub.example.org/resource"))); + EXPECT_FALSE(from_blob.IsFirstParty(GURL("http://sub.example.org/resource"))); + EXPECT_EQ("https", from_blob.scheme()); + EXPECT_EQ( + "SiteForCookies: {scheme=https; registrable_domain=example.org; " + "schemefully_same=true}", + from_blob.ToDebugString()); + EXPECT_EQ("https://example.org/", from_blob.RepresentativeUrl().spec()); + EXPECT_TRUE(from_blob.IsEquivalent( + SiteForCookies::FromUrl(GURL("https://www.example.org:631")))); + EXPECT_FALSE(from_blob.IsEquivalent( + SiteForCookies::FromUrl(GURL("http://www.example.org:631")))); +} + +// Similar to SiteForCookiesTest_Blob with a focus on an insecure blob. +TEST_F(SchemefulSiteForCookiesTest, InsecureBlob) { + SiteForCookies from_blob = SiteForCookies::FromUrl( + GURL("blob:http://example.org/9115d58c-bcda-ff47-86e5-083e9a2153041")); + + EXPECT_TRUE(from_blob.IsFirstParty(GURL("http://sub.example.org/resource"))); + EXPECT_FALSE( + from_blob.IsFirstParty(GURL("https://sub.example.org/resource"))); + EXPECT_EQ("http", from_blob.scheme()); + EXPECT_EQ( + "SiteForCookies: {scheme=http; registrable_domain=example.org; " + "schemefully_same=true}", + from_blob.ToDebugString()); + EXPECT_EQ("http://example.org/", from_blob.RepresentativeUrl().spec()); + EXPECT_TRUE(from_blob.IsEquivalent( + SiteForCookies::FromUrl(GURL("http://www.example.org:631")))); + EXPECT_FALSE(from_blob.IsEquivalent( + SiteForCookies::FromUrl(GURL("https://www.example.org:631")))); +} + TEST(SiteForCookiesTest, Wire) { SiteForCookies out; // Empty one. - EXPECT_TRUE(SiteForCookies::FromWire("", "", &out)); + EXPECT_TRUE(SiteForCookies::FromWire("", "", false, &out)); + EXPECT_TRUE(out.IsNull()); + + EXPECT_TRUE(SiteForCookies::FromWire("", "", true, &out)); EXPECT_TRUE(out.IsNull()); // Not a valid scheme. - EXPECT_FALSE(SiteForCookies::FromWire("aH", "example.com", &out)); + EXPECT_FALSE(SiteForCookies::FromWire("aH", "example.com", false, &out)); EXPECT_TRUE(out.IsNull()); // Not a eTLD + 1 (or something hosty). - EXPECT_FALSE(SiteForCookies::FromWire("http", "sub.example.com", &out)); + EXPECT_FALSE( + SiteForCookies::FromWire("http", "sub.example.com", false, &out)); EXPECT_TRUE(out.IsNull()); // This is fine, though. - EXPECT_TRUE(SiteForCookies::FromWire("https", "127.0.0.1", &out)); + EXPECT_TRUE(SiteForCookies::FromWire("https", "127.0.0.1", true, &out)); EXPECT_FALSE(out.IsNull()); - EXPECT_EQ("SiteForCookies: {scheme=https; registrable_domain=127.0.0.1}", - out.ToDebugString()); + EXPECT_EQ( + "SiteForCookies: {scheme=https; registrable_domain=127.0.0.1; " + "schemefully_same=true}", + out.ToDebugString()); + + EXPECT_TRUE(SiteForCookies::FromWire("https", "127.0.0.1", false, &out)); + EXPECT_FALSE(out.IsNull()); + EXPECT_EQ( + "SiteForCookies: {scheme=https; registrable_domain=127.0.0.1; " + "schemefully_same=false}", + out.ToDebugString()); // As is actual eTLD+1. - EXPECT_TRUE(SiteForCookies::FromWire("wss", "example.com", &out)); + EXPECT_TRUE(SiteForCookies::FromWire("wss", "example.com", true, &out)); EXPECT_FALSE(out.IsNull()); - EXPECT_EQ("SiteForCookies: {scheme=wss; registrable_domain=example.com}", - out.ToDebugString()); + EXPECT_EQ( + "SiteForCookies: {scheme=wss; registrable_domain=example.com; " + "schemefully_same=true}", + out.ToDebugString()); +} + +// Similar to SiteForCookiesTest_Wire except that schemefully_same has an +// effect. +TEST_F(SchemefulSiteForCookiesTest, Wire) { + SiteForCookies out; + + // Empty one. + EXPECT_TRUE(SiteForCookies::FromWire("", "", false, &out)); + EXPECT_TRUE(out.IsNull()); + + EXPECT_TRUE(SiteForCookies::FromWire("", "", true, &out)); + EXPECT_TRUE(out.IsNull()); + + // Not a valid scheme. + EXPECT_FALSE(SiteForCookies::FromWire("aH", "example.com", false, &out)); + EXPECT_TRUE(out.IsNull()); + + // Not a eTLD + 1 (or something hosty). + EXPECT_FALSE( + SiteForCookies::FromWire("http", "sub.example.com", false, &out)); + EXPECT_TRUE(out.IsNull()); + + // This is fine, though. + EXPECT_TRUE(SiteForCookies::FromWire("https", "127.0.0.1", true, &out)); + EXPECT_FALSE(out.IsNull()); + EXPECT_EQ( + "SiteForCookies: {scheme=https; registrable_domain=127.0.0.1; " + "schemefully_same=true}", + out.ToDebugString()); + + // This one's schemefully_same is false + EXPECT_TRUE(SiteForCookies::FromWire("https", "127.0.0.1", false, &out)); + EXPECT_TRUE(out.IsNull()); + + // As is actual eTLD+1. + EXPECT_TRUE(SiteForCookies::FromWire("wss", "example.com", true, &out)); + EXPECT_FALSE(out.IsNull()); + EXPECT_EQ( + "SiteForCookies: {scheme=wss; registrable_domain=example.com; " + "schemefully_same=true}", + out.ToDebugString()); +} + +TEST(SiteForCookiesTest, SameScheme) { + struct TestCase { + const char* first; + const char* second; + bool expected_value; + }; + + const TestCase kTestCases[] = { + {"http://a.com", "http://a.com", true}, + {"https://a.com", "https://a.com", true}, + {"ws://a.com", "ws://a.com", true}, + {"wss://a.com", "wss://a.com", true}, + {"https://a.com", "wss://a.com", true}, + {"wss://a.com", "https://a.com", true}, + {"http://a.com", "ws://a.com", true}, + {"ws://a.com", "http://a.com", true}, + {"file://a.com", "file://a.com", true}, + {"file://folder1/folder2/file.txt", "file://folder1/folder2/file.txt", + true}, + {"ftp://a.com", "ftp://a.com", true}, + {"http://a.com", "file://a.com", false}, + {"ws://a.com", "wss://a.com", false}, + {"wss://a.com", "ws://a.com", false}, + {"https://a.com", "http://a.com", false}, + {"file://a.com", "https://a.com", false}, + {"https://a.com", "file://a.com", false}, + {"file://a.com", "ftp://a.com", false}, + {"ftp://a.com", "file://a.com", false}, + }; + + for (const TestCase& t : kTestCases) { + SiteForCookies first = SiteForCookies::FromUrl(GURL(t.first)); + url::Origin second = url::Origin::Create(GURL(t.second)); + EXPECT_FALSE(first.IsNull()); + first.MarkIfCrossScheme(second); + EXPECT_EQ(first.schemefully_same(), t.expected_value); + } +} + +TEST(SiteForCookiesTest, SameSchemeOpaque) { + url::Origin not_opaque_secure = + url::Origin::Create(GURL("https://site.example")); + url::Origin not_opaque_nonsecure = + url::Origin::Create(GURL("http://site.example")); + // Check an opaque origin made from a triple origin and one from the default + // constructor. + const url::Origin kOpaqueOrigins[] = { + not_opaque_secure.DeriveNewOpaqueOrigin(), + not_opaque_nonsecure.DeriveNewOpaqueOrigin(), url::Origin()}; + + for (const url::Origin& origin : kOpaqueOrigins) { + SiteForCookies secure_sfc = SiteForCookies::FromOrigin(not_opaque_secure); + EXPECT_FALSE(secure_sfc.IsNull()); + SiteForCookies nonsecure_sfc = + SiteForCookies::FromOrigin(not_opaque_nonsecure); + EXPECT_FALSE(nonsecure_sfc.IsNull()); + + EXPECT_TRUE(secure_sfc.schemefully_same()); + secure_sfc.MarkIfCrossScheme(origin); + EXPECT_FALSE(secure_sfc.schemefully_same()); + + EXPECT_TRUE(nonsecure_sfc.schemefully_same()); + nonsecure_sfc.MarkIfCrossScheme(origin); + EXPECT_FALSE(nonsecure_sfc.schemefully_same()); + + SiteForCookies opaque_sfc = SiteForCookies::FromOrigin(origin); + EXPECT_TRUE(opaque_sfc.IsNull()); + // Slightly implementation detail specific as the value isn't relevant for + // null SFCs. + EXPECT_FALSE(nonsecure_sfc.schemefully_same()); + } } } // namespace diff --git a/chromium/net/cookies/static_cookie_policy.cc b/chromium/net/cookies/static_cookie_policy.cc index 25c6e411826..96a45759a5f 100644 --- a/chromium/net/cookies/static_cookie_policy.cc +++ b/chromium/net/cookies/static_cookie_policy.cc @@ -4,7 +4,7 @@ #include "net/cookies/static_cookie_policy.h" -#include "base/logging.h" +#include "base/notreached.h" #include "net/base/net_errors.h" #include "net/cookies/site_for_cookies.h" #include "url/gurl.h" diff --git a/chromium/net/cookies/test_cookie_access_delegate.cc b/chromium/net/cookies/test_cookie_access_delegate.cc index 41893830adb..78750a8152e 100644 --- a/chromium/net/cookies/test_cookie_access_delegate.cc +++ b/chromium/net/cookies/test_cookie_access_delegate.cc @@ -48,7 +48,7 @@ void TestCookieAccessDelegate::SetIgnoreSameSiteRestrictionsScheme( std::string TestCookieAccessDelegate::GetKeyForDomainValue( const std::string& domain) const { DCHECK(!domain.empty()); - return domain[0] == '.' ? domain.substr(1) : domain; + return cookie_util::CookieDomainAsHost(domain); } } // namespace net |