0

Don't send SameSite=Lax cookies on cross-site, non-top-level requests

This fixes a bug which was sending SameSite=Lax cookies on cross-site,
*non*-top-level requests when the site-for-cookies was same-site with
the request URL. If the request is not strictly same-site, we should
only be sending Lax cookies when the site-for-cookies is same-site with
the request URL, *and* the request is a top-level navigation. Similarly
for accepting cookies set on responses.

This implements the fix behind a flag (default enabled) to allow
reverting to the old behavior if there is too much site breakage as a
result of the fix.

Bug: 1166211
Change-Id: I2cebf8011010903cd016d7d7c1a32bf84aa325ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2653663
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Maksim Orlovich <morlovich@chromium.org>
Commit-Queue: Lily Chen <chlily@chromium.org>
Cr-Commit-Position: refs/heads/master@{#851323}
This commit is contained in:
Lily Chen
2021-02-05 23:00:29 +00:00
committed by Chromium LUCI CQ
parent 38e0bfea1d
commit b332c5609e
16 changed files with 673 additions and 204 deletions

@ -1117,6 +1117,7 @@ void InterceptionJob::ProcessSetCookies(const net::HttpResponseHeaders& headers,
create_loader_params_->request.url, create_loader_params_->request.url,
create_loader_params_->request.site_for_cookies, create_loader_params_->request.site_for_cookies,
create_loader_params_->request.request_initiator, create_loader_params_->request.request_initiator,
create_loader_params_->request.is_main_frame,
should_treat_as_first_party)); should_treat_as_first_party));
// |this| might be deleted here if |cookies| is empty! // |this| might be deleted here if |cookies| is empty!
@ -1265,7 +1266,8 @@ void InterceptionJob::FetchCookies(
options.set_same_site_cookie_context( options.set_same_site_cookie_context(
net::cookie_util::ComputeSameSiteContextForRequest( net::cookie_util::ComputeSameSiteContextForRequest(
request.method, request.url, request.site_for_cookies, request.method, request.url, request.site_for_cookies,
request.request_initiator, should_treat_as_first_party)); request.request_initiator, request.is_main_frame,
should_treat_as_first_party));
cookie_manager_->GetCookieList(request.url, options, std::move(callback)); cookie_manager_->GetCookieList(request.url, options, std::move(callback));
} }

@ -199,5 +199,8 @@ constexpr base::Feature kFirstPartySets{"FirstPartySets",
const base::FeatureParam<bool> kFirstPartySetsIsDogfooder{ const base::FeatureParam<bool> kFirstPartySetsIsDogfooder{
&kFirstPartySets, "FirstPartySetsIsDogfooder", false}; &kFirstPartySets, "FirstPartySetsIsDogfooder", false};
const base::Feature kSameSiteCookiesBugfix1166211{
"SameSiteCookiesBugfix1166211", base::FEATURE_ENABLED_BY_DEFAULT};
} // namespace features } // namespace features
} // namespace net } // namespace net

@ -294,6 +294,15 @@ NET_EXPORT extern const base::Feature kFirstPartySets;
// feature. // feature.
NET_EXPORT extern const base::FeatureParam<bool> kFirstPartySetsIsDogfooder; NET_EXPORT extern const base::FeatureParam<bool> kFirstPartySetsIsDogfooder;
// Controls whether the fix for crbug.com/1166211 is enabled. When this is
// enabled, SameSite=Lax cookies may only be accessed for cross-site requests if
// they are top-level navigations. When it is disabled, the (incorrect) previous
// behavior that allows SameSite=Lax cookies on cross-site, non-top-level
// requests if all frame ancestors are same-site with the request URL is used
// instead. This fix is implemented behind a flag (kill switch) due to potential
// compatibility risk.
NET_EXPORT extern const base::Feature kSameSiteCookiesBugfix1166211;
} // namespace features } // namespace features
} // namespace net } // namespace net

@ -83,21 +83,49 @@ bool SaturatedTimeFromUTCExploded(const base::Time::Exploded& exploded,
return false; return false;
} }
// This function consolidates the common logic for computing SameSite cookie
// access context in various situations (HTTP vs JS; get vs set).
//
// `is_http` is whether the current cookie access request is associated with a
// network request (as opposed to a non-HTTP API, i.e., JavaScript).
//
// `compute_schemefully` is whether the current computation is for a
// schemeful_context, i.e. whether scheme should be considered when comparing
// two sites.
//
// See documentation of `ComputeSameSiteContextForRequest` for explanations of
// other parameters.
ContextType ComputeSameSiteContext(const GURL& url, ContextType ComputeSameSiteContext(const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool is_http,
bool is_main_frame_navigation,
bool compute_schemefully) { bool compute_schemefully) {
if (site_for_cookies.IsFirstPartyWithSchemefulMode(url, bool site_for_cookies_is_same_site =
compute_schemefully)) { site_for_cookies.IsFirstPartyWithSchemefulMode(url, compute_schemefully);
// If the request is a main frame navigation, site_for_cookies must either be
// null (for opaque origins, e.g., data: origins) or same-site with the
// request URL (both schemefully and schemelessly), and the URL cannot be
// ws/wss (these schemes are not navigable).
DCHECK(!is_main_frame_navigation || site_for_cookies_is_same_site ||
site_for_cookies.IsNull());
DCHECK(!is_main_frame_navigation || !url.SchemeIsWSOrWSS());
if (site_for_cookies_is_same_site) {
// Create a SiteForCookies object from the initiator so that we can reuse // Create a SiteForCookies object from the initiator so that we can reuse
// IsFirstPartyWithSchemefulMode(). // IsFirstPartyWithSchemefulMode().
if (!initiator || if (!initiator ||
SiteForCookies::FromOrigin(initiator.value()) SiteForCookies::FromOrigin(initiator.value())
.IsFirstPartyWithSchemefulMode(url, compute_schemefully)) { .IsFirstPartyWithSchemefulMode(url, compute_schemefully)) {
return ContextType::SAME_SITE_STRICT; return ContextType::SAME_SITE_STRICT;
} else {
return ContextType::SAME_SITE_LAX;
} }
// Preserve old behavior if the bugfix is disabled.
if (!base::FeatureList::IsEnabled(features::kSameSiteCookiesBugfix1166211))
return ContextType::SAME_SITE_LAX;
if (!is_http || is_main_frame_navigation)
return ContextType::SAME_SITE_LAX;
} }
return ContextType::CROSS_SITE; return ContextType::CROSS_SITE;
} }
@ -105,23 +133,26 @@ ContextType ComputeSameSiteContext(const GURL& url,
CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSet( CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSet(
const GURL& url, const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
bool force_ignore_site_for_cookies) { const base::Optional<url::Origin>& initiator,
if (force_ignore_site_for_cookies) bool is_http,
return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet(); bool is_main_frame_navigation) {
CookieOptions::SameSiteCookieContext same_site_context;
// Schemeless check same_site_context.set_context(ComputeSameSiteContext(
if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, false)) { url, site_for_cookies, initiator, is_http, is_main_frame_navigation,
return CookieOptions::SameSiteCookieContext(ContextType::CROSS_SITE, false /* compute_schemefully */));
ContextType::CROSS_SITE); same_site_context.set_schemeful_context(ComputeSameSiteContext(
} url, site_for_cookies, initiator, is_http, is_main_frame_navigation,
true /* compute_schemefully */));
// Schemeful check // Setting any SameSite={Strict,Lax} cookie only requires a LAX context, so
if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, true)) { // normalize any strictly same-site contexts to Lax for cookie writes.
return CookieOptions::SameSiteCookieContext(ContextType::SAME_SITE_LAX, if (same_site_context.context() == ContextType::SAME_SITE_STRICT)
ContextType::CROSS_SITE); same_site_context.set_context(ContextType::SAME_SITE_LAX);
} if (same_site_context.schemeful_context() == ContextType::SAME_SITE_STRICT)
same_site_context.set_schemeful_context(ContextType::SAME_SITE_LAX);
return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet(); return same_site_context;
} }
} // namespace } // namespace
@ -470,22 +501,24 @@ CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest(
const GURL& url, const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool is_main_frame_navigation,
bool force_ignore_site_for_cookies) { bool force_ignore_site_for_cookies) {
// Set SameSiteCookieMode according to the rules laid out in // Set SameSiteCookieContext according to the rules laid out in
// https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02: // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis:
// //
// * Include both "strict" and "lax" same-site cookies if the request's // * Include both "strict" and "lax" same-site cookies if the request's
// |url|, |initiator|, and |site_for_cookies| all have the same // |url|, |initiator|, and |site_for_cookies| all have the same
// registrable domain. Note: this also covers the case of a request // registrable domain. Note: this also covers the case of a request
// without an initiator (only happens for browser-initiated main frame // without an initiator (only happens for browser-initiated main frame
// navigations). // navigations). If computing schemefully, the schemes must also match.
// //
// * Include only "lax" same-site cookies if the request's |URL| and // * Include only "lax" same-site cookies if the request's |URL| and
// |site_for_cookies| have the same registrable domain, _and_ the // |site_for_cookies| have the same registrable domain, _and_ the
// request's |http_method| is "safe" ("GET" or "HEAD"). // request's |http_method| is "safe" ("GET" or "HEAD"), and the request
// is a main frame navigation.
// //
// This case should generally occur only for cross-site requests which // This case should occur only for cross-site requests which
// target a top-level browsing context. // target a top-level browsing context, with a "safe" method.
// //
// * Include both "strict" and "lax" same-site cookies if the request is // * Include both "strict" and "lax" same-site cookies if the request is
// tagged with a flag allowing it. // tagged with a flag allowing it.
@ -501,10 +534,12 @@ CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest(
CookieOptions::SameSiteCookieContext same_site_context; CookieOptions::SameSiteCookieContext same_site_context;
same_site_context.set_context( same_site_context.set_context(ComputeSameSiteContext(
ComputeSameSiteContext(url, site_for_cookies, initiator, false)); url, site_for_cookies, initiator, true /* is_http */,
same_site_context.set_schemeful_context( is_main_frame_navigation, false /* compute_schemefully */));
ComputeSameSiteContext(url, site_for_cookies, initiator, true)); same_site_context.set_schemeful_context(ComputeSameSiteContext(
url, site_for_cookies, initiator, true /* is_http */,
is_main_frame_navigation, true /* compute_schemefully */));
// If the method is safe, the context is Lax. Otherwise, make a note that // If the method is safe, the context is Lax. Otherwise, make a note that
// the method is unsafe. // the method is unsafe.
@ -531,10 +566,12 @@ ComputeSameSiteContextForScriptGet(const GURL& url,
CookieOptions::SameSiteCookieContext same_site_context; CookieOptions::SameSiteCookieContext same_site_context;
same_site_context.set_context( same_site_context.set_context(ComputeSameSiteContext(
ComputeSameSiteContext(url, site_for_cookies, initiator, false)); url, site_for_cookies, initiator, false /* is_http */,
same_site_context.set_schemeful_context( false /* is_main_frame_navigation */, false /* compute_schemefully */));
ComputeSameSiteContext(url, site_for_cookies, initiator, true)); same_site_context.set_schemeful_context(ComputeSameSiteContext(
url, site_for_cookies, initiator, false /* is_http */,
false /* is_main_frame_navigation */, true /* compute_schemefully */));
return same_site_context; return same_site_context;
} }
@ -543,20 +580,39 @@ CookieOptions::SameSiteCookieContext ComputeSameSiteContextForResponse(
const GURL& url, const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool is_main_frame_navigation,
bool force_ignore_site_for_cookies) { bool force_ignore_site_for_cookies) {
// |initiator| is here in case it'll be decided to ignore |site_for_cookies| if (force_ignore_site_for_cookies)
// for entirely browser-side requests (see https://crbug.com/958335). return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
return ComputeSameSiteContextForSet(url, site_for_cookies, if (is_main_frame_navigation && !site_for_cookies.IsNull()) {
force_ignore_site_for_cookies); // If the request is a main frame navigation, site_for_cookies must either
// be null (for opaque origins, e.g., data: origins) or same-site with the
// request URL (both schemefully and schemelessly), and the URL cannot be
// ws/wss (these schemes are not navigable).
DCHECK(site_for_cookies.IsFirstPartyWithSchemefulMode(url, true));
DCHECK(!url.SchemeIsWSOrWSS());
return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
}
return ComputeSameSiteContextForSet(url, site_for_cookies, initiator,
true /* is_http */,
is_main_frame_navigation);
} }
CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptSet( CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptSet(
const GURL& url, const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
bool force_ignore_site_for_cookies) { bool force_ignore_site_for_cookies) {
return ComputeSameSiteContextForSet(url, site_for_cookies, if (force_ignore_site_for_cookies)
force_ignore_site_for_cookies); return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
// It doesn't matter what initiator origin we pass here. Either way, the
// context will be considered same-site iff the site_for_cookies is same-site
// with the url.
return ComputeSameSiteContextForSet(
url, site_for_cookies, base::nullopt /* initiator */, false /* is_http */,
false /* is_main_frame_navigation */);
} }
CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSubresource( CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSubresource(

@ -140,25 +140,29 @@ NET_EXPORT void ParseRequestCookieLine(const std::string& header_value,
NET_EXPORT std::string SerializeRequestCookieLine( NET_EXPORT std::string SerializeRequestCookieLine(
const ParsedRequestCookies& parsed_cookies); const ParsedRequestCookies& parsed_cookies);
// Determines which of the cookies for |url| can be accessed, with respect to // Determines which of the cookies for `url` can be accessed, with respect to
// the SameSite attribute. This applies to looking up existing cookies; for // the SameSite attribute. This applies to looking up existing cookies for HTTP
// setting new ones, see ComputeSameSiteContextForResponse and // requests. For looking up cookies for non-HTTP APIs (i.e., JavaScript), see
// ComputeSameSiteContextForScriptSet. // ComputeSameSiteContextForScriptGet. For setting new cookies, see
// ComputeSameSiteContextForResponse and ComputeSameSiteContextForScriptSet.
// //
// |site_for_cookies| is the currently navigated to site that should be // `site_for_cookies` is the currently navigated to site that should be
// considered "first-party" for cookies. // considered "first-party" for cookies.
// //
// |initiator| is the origin ultimately responsible for getting the request // `initiator` is the origin ultimately responsible for getting the request
// issued; it may be different from |site_for_cookies| in that it may be some // issued. It may be different from `site_for_cookies`.
// other website that caused the navigation to |site_for_cookies| to occur.
// //
// base::nullopt for |initiator| denotes that the navigation was initiated by // base::nullopt for `initiator` denotes that the navigation was initiated by
// the user directly interacting with the browser UI, e.g. entering a URL // the user directly interacting with the browser UI, e.g. entering a URL
// or selecting a bookmark. // or selecting a bookmark.
// //
// If |force_ignore_site_for_cookies| is specified, all SameSite cookies will be // `is_main_frame_navigation` is whether the request is for a navigation that
// targets the main frame or top-level browsing context. These requests may
// sometimes send SameSite=Lax cookies but not SameSite=Strict cookies.
//
// 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 // 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, // when the `site_for_cookies` is a chrome:// URL embedding a secure origin,
// among other scenarios. // among other scenarios.
// This is *not* set when the *initiator* is chrome-extension://, // This is *not* set when the *initiator* is chrome-extension://,
// which is intentional, since it would be bad to let an extension arbitrarily // which is intentional, since it would be bad to let an extension arbitrarily
@ -166,7 +170,7 @@ NET_EXPORT std::string SerializeRequestCookieLine(
// //
// See also documentation for corresponding methods on net::URLRequest. // See also documentation for corresponding methods on net::URLRequest.
// //
// |http_method| is used to enforce the requirement that, in a context that's // `http_method` is used to enforce the requirement that, in a context that's
// lax same-site but not strict same-site, SameSite=lax cookies be only sent // lax same-site but not strict same-site, SameSite=lax cookies be only sent
// when the method is "safe" in the RFC7231 section 4.2.1 sense. // when the method is "safe" in the RFC7231 section 4.2.1 sense.
NET_EXPORT CookieOptions::SameSiteCookieContext NET_EXPORT CookieOptions::SameSiteCookieContext
@ -174,33 +178,38 @@ ComputeSameSiteContextForRequest(const std::string& http_method,
const GURL& url, const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool is_main_frame_navigation,
bool force_ignore_site_for_cookies); bool force_ignore_site_for_cookies);
// As above, but applying for scripts. |initiator| here should be the initiator // As above, but applying for scripts. `initiator` here should be the initiator
// used when fetching the document. // used when fetching the document.
// If |force_ignore_site_for_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 NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForScriptGet(const GURL& url, ComputeSameSiteContextForScriptGet(const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool force_ignore_site_for_cookies); bool force_ignore_site_for_cookies);
// Determines which of the cookies for |url| can be set from a network response, // 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 // 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 // SAME_SITE_LAX (cookie sets of SameSite=strict cookies are permitted in same
// contexts that sets of SameSite=lax cookies are). // contexts that sets of SameSite=lax cookies are).
// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_LAX. // `is_main_frame_navigation` is whether the request was for a navigation that
// targets the main frame or top-level browsing context. Both SameSite=Lax and
// SameSite=Strict cookies may be set by any main frame navigation.
// If `force_ignore_site_for_cookies` is true, this returns SAME_SITE_LAX.
NET_EXPORT CookieOptions::SameSiteCookieContext NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForResponse(const GURL& url, ComputeSameSiteContextForResponse(const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,
const base::Optional<url::Origin>& initiator, const base::Optional<url::Origin>& initiator,
bool is_main_frame_navigation,
bool force_ignore_site_for_cookies); bool force_ignore_site_for_cookies);
// Determines which of the cookies for |url| can be set from a script context, // 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 // 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 // SAME_SITE_LAX (cookie sets of SameSite=strict cookies are permitted in same
// contexts that sets of SameSite=lax cookies are). // contexts that sets of SameSite=lax cookies are).
// If |force_ignore_site_for_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 NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForScriptSet(const GURL& url, ComputeSameSiteContextForScriptSet(const GURL& url,
const SiteForCookies& site_for_cookies, const SiteForCookies& site_for_cookies,

@ -351,18 +351,29 @@ MATCHER_P2(ContextTypeIsWithSchemefulMode, context_type, schemeful, "") {
return context_type == (schemeful ? arg.schemeful_context() : arg.context()); return context_type == (schemeful ? arg.schemeful_context() : arg.context());
} }
// Tests for the various ComputeSameSiteContextFor*() functions. The test param // Tests for the various ComputeSameSiteContextFor*() functions. The first
// is whether the results of the computations are evaluated schemefully. // boolean test param is whether the results of the computations are evaluated
// schemefully. The second boolean test param is whether the fix for
// crbug.com/1166211 is enabled.
class CookieUtilComputeSameSiteContextTest class CookieUtilComputeSameSiteContextTest
: public ::testing::TestWithParam<bool> { : public ::testing::TestWithParam<std::tuple<bool, bool>> {
public: public:
CookieUtilComputeSameSiteContextTest() = default; CookieUtilComputeSameSiteContextTest() {
// No need to explicitly enable the feature because it is enabled by
// default.
if (!IsBugfix1166211Enabled()) {
feature_list_.InitAndDisableFeature(
features::kSameSiteCookiesBugfix1166211);
}
}
~CookieUtilComputeSameSiteContextTest() override = default; ~CookieUtilComputeSameSiteContextTest() override = default;
using SameSiteCookieContext = CookieOptions::SameSiteCookieContext; using SameSiteCookieContext = CookieOptions::SameSiteCookieContext;
using ContextType = CookieOptions::SameSiteCookieContext::ContextType; using ContextType = CookieOptions::SameSiteCookieContext::ContextType;
bool IsSchemeful() const { return GetParam(); } bool IsSchemeful() const { return std::get<0>(GetParam()); }
bool IsBugfix1166211Enabled() const { return std::get<1>(GetParam()); }
// Returns the proper gtest matcher to use for the schemeless/schemeful mode. // Returns the proper gtest matcher to use for the schemeless/schemeful mode.
auto ContextTypeIs(ContextType context_type) const { auto ContextTypeIs(ContextType context_type) const {
@ -456,6 +467,23 @@ class CookieUtilComputeSameSiteContextTest
return cross_site_initiators; return cross_site_initiators;
} }
// Computes possible values of is_main_frame_navigation that are consistent
// with the DCHECKs.
bool CanBeMainFrameNavigation(const GURL& url,
const SiteForCookies& site_for_cookies) const {
return (site_for_cookies.IsNull() ||
site_for_cookies.IsFirstPartyWithSchemefulMode(url, true)) &&
!url.SchemeIsWSOrWSS();
}
std::vector<bool> IsMainFrameNavigationPossibleValues(
const GURL& url,
const SiteForCookies& site_for_cookies) const {
return CanBeMainFrameNavigation(url, site_for_cookies)
? std::vector<bool>{false, true}
: std::vector<bool>{false};
}
// Request URL. // Request URL.
const GURL kSiteUrl{"http://example.test/"}; const GURL kSiteUrl{"http://example.test/"};
const GURL kSiteUrlWithPath{"http://example.test/path"}; const GURL kSiteUrlWithPath{"http://example.test/path"};
@ -493,6 +521,9 @@ class CookieUtilComputeSameSiteContextTest
base::make_optional(url::Origin::Create(kSecureSubdomainUrl)); base::make_optional(url::Origin::Create(kSecureSubdomainUrl));
const base::Optional<url::Origin> kUnrelatedInitiator = const base::Optional<url::Origin> kUnrelatedInitiator =
base::make_optional(url::Origin::Create(GURL("https://unrelated.test/"))); base::make_optional(url::Origin::Create(GURL("https://unrelated.test/")));
protected:
base::test::ScopedFeatureList feature_list_;
}; };
TEST_P(CookieUtilComputeSameSiteContextTest, UrlAndSiteForCookiesCrossSite) { TEST_P(CookieUtilComputeSameSiteContextTest, UrlAndSiteForCookiesCrossSite) {
@ -511,14 +542,19 @@ TEST_P(CookieUtilComputeSameSiteContextTest, UrlAndSiteForCookiesCrossSite) {
url, site_for_cookies, url, site_for_cookies,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE)); ContextTypeIs(ContextType::CROSS_SITE));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( for (bool is_main_frame_navigation :
method, url, site_for_cookies, initiator, IsMainFrameNavigationPossibleValues(url, site_for_cookies)) {
false /* force_ignore_site_for_cookies */), EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
ContextTypeIs(ContextType::CROSS_SITE)); method, url, site_for_cookies, initiator,
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse( is_main_frame_navigation,
url, site_for_cookies, initiator, false /* force_ignore_site_for_cookies */),
false /* force_ignore_site_for_cookies */), ContextTypeIs(ContextType::CROSS_SITE));
ContextTypeIs(ContextType::CROSS_SITE)); EXPECT_THAT(
cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator, is_main_frame_navigation,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE));
}
EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource( EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, url, site_for_cookies,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
@ -552,14 +588,20 @@ TEST_P(CookieUtilComputeSameSiteContextTest, SiteForCookiesNotSchemefullySame) {
url, site_for_cookies, url, site_for_cookies,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE)); ContextTypeIs(ContextType::CROSS_SITE));
// If the site-for-cookies isn't schemefully_same, this cannot be a
// main frame navigation.
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator, method, url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE)); ContextTypeIs(ContextType::CROSS_SITE));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse( EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator, url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE)); ContextTypeIs(ContextType::CROSS_SITE));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource( EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, url, site_for_cookies,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
@ -653,30 +695,68 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForRequest) {
for (const base::Optional<url::Origin>& initiator : for (const base::Optional<url::Origin>& initiator :
GetSameSiteInitiators()) { GetSameSiteInitiators()) {
for (const std::string& method : {"GET", "POST", "PUT", "HEAD"}) { for (const std::string& method : {"GET", "POST", "PUT", "HEAD"}) {
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( for (bool is_main_frame_navigation :
method, url, site_for_cookies, initiator, IsMainFrameNavigationPossibleValues(url, site_for_cookies)) {
false /* force_ignore_site_for_cookies */), EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
ContextTypeIs(ContextType::SAME_SITE_STRICT)); method, url, site_for_cookies, initiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_STRICT));
}
} }
} }
// Cross-Site initiator -> it's same-site lax iff the method is safe. // Cross-Site initiator -> it's same-site lax iff the method is safe.
// TODO(crbug.com/1166211): This should take into account whether the
// request is a main frame navigation.
for (const base::Optional<url::Origin>& initiator : for (const base::Optional<url::Origin>& initiator :
GetCrossSiteInitiators()) { GetCrossSiteInitiators()) {
// For main frame navigations, the context is Lax (or Lax-unsafe).
for (const std::string& method : {"GET", "HEAD"}) { for (const std::string& method : {"GET", "HEAD"}) {
if (!CanBeMainFrameNavigation(url, site_for_cookies))
break;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator, method, url, site_for_cookies, initiator,
true /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(ContextType::SAME_SITE_LAX));
} }
for (const std::string& method : {"POST", "PUT"}) { for (const std::string& method : {"POST", "PUT"}) {
if (!CanBeMainFrameNavigation(url, site_for_cookies))
break;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator, method, url, site_for_cookies, initiator,
true /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX_METHOD_UNSAFE)); ContextTypeIs(ContextType::SAME_SITE_LAX_METHOD_UNSAFE));
} }
// For non-main-frame-navigation requests, the context should be
// cross-site, or (if the old incorrect behavior is in effect) lax or
// lax-unsafe.
if (IsBugfix1166211Enabled()) {
for (const std::string& method : {"GET", "POST", "PUT", "HEAD"}) {
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::CROSS_SITE));
}
} else {
for (const std::string& method : {"GET", "HEAD"}) {
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX));
}
for (const std::string& method : {"POST", "PUT"}) {
EXPECT_THAT(
cookie_util::ComputeSameSiteContextForRequest(
method, url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX_METHOD_UNSAFE));
}
}
} }
} }
} }
@ -686,85 +766,125 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForRequest_SchemefulDowngrade) {
// Some test cases where the context is downgraded when computed schemefully. // Some test cases where the context is downgraded when computed schemefully.
// (Should already be covered above, but just to be explicit.) // (Should already be covered above, but just to be explicit.)
// Strict -> cross downgrade. // Cross-scheme URL and site-for-cookies with (schemelessly) same-site
// initiator.
// (The request cannot be a main frame navigation if the site-for-cookies is
// not schemefully same-site).
for (const std::string& method : {"GET", "POST"}) { for (const std::string& method : {"GET", "POST"}) {
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT, EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
ContextType::CROSS_SITE), ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
method, kSecureSiteUrl, kSiteForCookies, kSiteInitiator, method, kSecureSiteUrl, kSiteForCookies, kSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */)); false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT, EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
ContextType::CROSS_SITE), ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
method, kSiteUrl, kSecureSiteForCookies, kSiteInitiator, method, kSiteUrl, kSecureSiteForCookies, kSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */)); false /* force_ignore_site_for_cookies */));
} }
// Strict -> lax downgrade. // Schemefully same-site URL and site-for-cookies with cross-scheme
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT, // initiator.
ContextType::SAME_SITE_LAX), for (bool is_main_frame_navigation : {false, true}) {
cookie_util::ComputeSameSiteContextForRequest( ContextType lax_if_main_frame =
"GET", kSecureSiteUrl, kSecureSiteForCookies, kSiteInitiator, is_main_frame_navigation || !IsBugfix1166211Enabled()
false /* force_ignore_site_for_cookies */)); ? ContextType::SAME_SITE_LAX
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT, : ContextType::CROSS_SITE;
ContextType::SAME_SITE_LAX), ContextType lax_unsafe_if_main_frame =
cookie_util::ComputeSameSiteContextForRequest( is_main_frame_navigation || !IsBugfix1166211Enabled()
"GET", kSiteUrl, kSiteForCookies, kSecureSiteInitiator, ? ContextType::SAME_SITE_LAX_METHOD_UNSAFE
false /* force_ignore_site_for_cookies */)); : ContextType::CROSS_SITE;
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
ContextType::SAME_SITE_LAX_METHOD_UNSAFE),
cookie_util::ComputeSameSiteContextForRequest(
"POST", kSecureSiteUrl, kSecureSiteForCookies, kSiteInitiator,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
ContextType::SAME_SITE_LAX_METHOD_UNSAFE),
cookie_util::ComputeSameSiteContextForRequest(
"POST", kSiteUrl, kSiteForCookies, kSecureSiteInitiator,
false /* force_ignore_site_for_cookies */));
// Lax -> cross downgrade. EXPECT_EQ(
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX, SameSiteCookieContext(ContextType::SAME_SITE_STRICT, lax_if_main_frame),
ContextType::CROSS_SITE), cookie_util::ComputeSameSiteContextForRequest(
"GET", kSecureSiteUrl, kSecureSiteForCookies, kSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(
SameSiteCookieContext(ContextType::SAME_SITE_STRICT, lax_if_main_frame),
cookie_util::ComputeSameSiteContextForRequest(
"GET", kSiteUrl, kSiteForCookies, kSecureSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
lax_unsafe_if_main_frame),
cookie_util::ComputeSameSiteContextForRequest(
"POST", kSecureSiteUrl, kSecureSiteForCookies, kSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
lax_unsafe_if_main_frame),
cookie_util::ComputeSameSiteContextForRequest(
"POST", kSiteUrl, kSiteForCookies, kSecureSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
}
// Cross-scheme URL and site-for-cookies with cross-site initiator.
// (The request cannot be a main frame navigation if the site-for-cookies is
// not schemefully same-site).
ContextType incorrectly_lax = IsBugfix1166211Enabled()
? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX;
ContextType incorrectly_lax_unsafe =
IsBugfix1166211Enabled() ? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX_METHOD_UNSAFE;
EXPECT_EQ(SameSiteCookieContext(incorrectly_lax, ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
"GET", kSiteUrl, kSecureSiteForCookies, kCrossSiteInitiator, "GET", kSiteUrl, kSecureSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */)); false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX, EXPECT_EQ(SameSiteCookieContext(incorrectly_lax, ContextType::CROSS_SITE),
ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
"GET", kSecureSiteUrl, kSiteForCookies, kCrossSiteInitiator, "GET", kSecureSiteUrl, kSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */)); false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX_METHOD_UNSAFE, EXPECT_EQ(
ContextType::CROSS_SITE), SameSiteCookieContext(incorrectly_lax_unsafe, ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
"POST", kSiteUrl, kSecureSiteForCookies, kCrossSiteInitiator, "POST", kSiteUrl, kSecureSiteForCookies, kCrossSiteInitiator,
false /* force_ignore_site_for_cookies */)); false /* is_main_frame_navigation */,
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX_METHOD_UNSAFE, false /* force_ignore_site_for_cookies */));
ContextType::CROSS_SITE), EXPECT_EQ(
cookie_util::ComputeSameSiteContextForRequest( SameSiteCookieContext(incorrectly_lax_unsafe, ContextType::CROSS_SITE),
"POST", kSecureSiteUrl, kSiteForCookies, kCrossSiteInitiator, cookie_util::ComputeSameSiteContextForRequest(
false /* force_ignore_site_for_cookies */)); "POST", kSecureSiteUrl, kSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */));
} }
TEST_P(CookieUtilComputeSameSiteContextTest, ForRequest_WebSocketSchemes) { TEST_P(CookieUtilComputeSameSiteContextTest, ForRequest_WebSocketSchemes) {
// wss/https and http/ws are considered the same for schemeful purposes. // wss/https and http/ws are considered the same for schemeful purposes.
// (ws/wss requests cannot be main frame navigations.)
ContextType incorrectly_lax = IsBugfix1166211Enabled()
? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
"GET", kWssUrl, kSecureSiteForCookies, kSecureSiteInitiator, "GET", kWssUrl, kSecureSiteForCookies, kSecureSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_STRICT)); ContextTypeIs(ContextType::SAME_SITE_STRICT));
EXPECT_THAT( EXPECT_THAT(
cookie_util::ComputeSameSiteContextForRequest( cookie_util::ComputeSameSiteContextForRequest(
"GET", kWssUrl, kSecureSiteForCookies, kSecureCrossSiteInitiator, "GET", kWssUrl, kSecureSiteForCookies, kSecureCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(incorrectly_lax));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
"GET", kWsUrl, kSiteForCookies, kSiteInitiator, "GET", kWsUrl, kSiteForCookies, kSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_STRICT)); ContextTypeIs(ContextType::SAME_SITE_STRICT));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
"GET", kWsUrl, kSiteForCookies, kCrossSiteInitiator, "GET", kWsUrl, kSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(incorrectly_lax));
} }
TEST_P(CookieUtilComputeSameSiteContextTest, ForScriptSet) { TEST_P(CookieUtilComputeSameSiteContextTest, ForScriptSet) {
@ -814,16 +934,40 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse) {
// (Cross-site cases covered above in UrlAndSiteForCookiesCrosSite test.) // (Cross-site cases covered above in UrlAndSiteForCookiesCrosSite test.)
for (const SiteForCookies& site_for_cookies : for (const SiteForCookies& site_for_cookies :
GetSameSiteSitesForCookies()) { GetSameSiteSitesForCookies()) {
// The initiator doesn't matter. If the Site for cookies and URL are // For main frame navigations, setting all SameSite cookies is allowed
// same-site, then the context is Lax. // regardless of initiator.
// TODO(crbug.com/1166211): This should take the initiator into account
// when the request is not a main frame navigation.
for (const base::Optional<url::Origin>& initiator : GetAllInitiators()) { for (const base::Optional<url::Origin>& initiator : GetAllInitiators()) {
if (!CanBeMainFrameNavigation(url, site_for_cookies))
break;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse( EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator, url, site_for_cookies, initiator,
true /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(ContextType::SAME_SITE_LAX));
} }
// For non-main-frame-navigation requests, the context should be lax iff
// the initiator is same-site, and cross-site otherwise. If the old
// (incorrect) behavior is in effect, it is always lax.
for (const base::Optional<url::Origin>& initiator :
GetSameSiteInitiators()) {
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX));
}
for (const base::Optional<url::Origin>& initiator :
GetCrossSiteInitiators()) {
ContextType incorrectly_lax = IsBugfix1166211Enabled()
? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(incorrectly_lax));
}
} }
} }
} }
@ -831,34 +975,84 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse) {
TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse_SchemefulDowngrade) { TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse_SchemefulDowngrade) {
// Some test cases where the context is downgraded when computed schemefully. // Some test cases where the context is downgraded when computed schemefully.
// (Should already be covered above, but just to be explicit.) // (Should already be covered above, but just to be explicit.)
// TODO(crbug.com/1166211): This should take the initiator into account when
// the request is not a main frame navigation. // URL and site-for-cookies are cross-scheme.
for (const base::Optional<url::Origin>& initiator : GetAllInitiators()) { // (If the URL and site-for-cookies are not schemefully same-site, this cannot
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX, // be a main frame navigation.)
ContextType::CROSS_SITE), // With same-site initiator:
cookie_util::ComputeSameSiteContextForResponse( EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX,
kSiteUrl, kSecureSiteForCookies, initiator, ContextType::CROSS_SITE),
false /* force_ignore_site_for_cookies */)); cookie_util::ComputeSameSiteContextForResponse(
EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX, kSiteUrl, kSecureSiteForCookies, kSiteInitiator,
ContextType::CROSS_SITE), false /* is_main_frame_navigation */,
cookie_util::ComputeSameSiteContextForResponse( false /* force_ignore_site_for_cookies */));
kSecureSiteUrl, kSiteForCookies, initiator, EXPECT_EQ(SameSiteCookieContext(ContextType::SAME_SITE_LAX,
false /* force_ignore_site_for_cookies */)); ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForResponse(
kSecureSiteUrl, kSiteForCookies, kSecureSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */));
// With cross-site initiator:
ContextType incorrectly_lax = IsBugfix1166211Enabled()
? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX;
EXPECT_EQ(SameSiteCookieContext(incorrectly_lax, ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForResponse(
kSiteUrl, kSecureSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(SameSiteCookieContext(incorrectly_lax, ContextType::CROSS_SITE),
cookie_util::ComputeSameSiteContextForResponse(
kSecureSiteUrl, kSiteForCookies, kCrossSiteInitiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */));
// Schemefully same-site URL and site-for-cookies with cross-scheme
// initiator.
for (bool is_main_frame_navigation : {false, true}) {
ContextType lax_if_main_frame =
is_main_frame_navigation || !IsBugfix1166211Enabled()
? ContextType::SAME_SITE_LAX
: ContextType::CROSS_SITE;
EXPECT_EQ(
SameSiteCookieContext(ContextType::SAME_SITE_LAX, lax_if_main_frame),
cookie_util::ComputeSameSiteContextForResponse(
kSiteUrl, kSiteForCookies, kSecureSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
EXPECT_EQ(
SameSiteCookieContext(ContextType::SAME_SITE_LAX, lax_if_main_frame),
cookie_util::ComputeSameSiteContextForResponse(
kSecureSiteUrl, kSecureSiteForCookies, kSiteInitiator,
is_main_frame_navigation,
false /* force_ignore_site_for_cookies */));
} }
} }
TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse_WebSocketSchemes) { TEST_P(CookieUtilComputeSameSiteContextTest, ForResponse_WebSocketSchemes) {
// wss/https and http/ws are considered the same for schemeful purposes. // wss/https and http/ws are considered the same for schemeful purposes.
for (const base::Optional<url::Origin>& initiator : GetAllInitiators()) { // (ws/wss requests cannot be main frame navigations.)
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
kWssUrl, kSecureSiteForCookies, initiator, // Same-site initiators.
false /* force_ignore_site_for_cookies */), for (const base::Optional<url::Origin>& initiator : GetSameSiteInitiators()) {
ContextTypeIs(ContextType::SAME_SITE_LAX));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse( EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
kWsUrl, kSiteForCookies, initiator, kWsUrl, kSiteForCookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */), false /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(ContextType::SAME_SITE_LAX));
} }
// Cross-site initiators.
for (const base::Optional<url::Origin>& initiator :
GetCrossSiteInitiators()) {
ContextType incorrectly_lax = IsBugfix1166211Enabled()
? ContextType::CROSS_SITE
: ContextType::SAME_SITE_LAX;
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse(
kWsUrl, kSiteForCookies, initiator,
false /* is_main_frame_navigation */,
false /* force_ignore_site_for_cookies */),
ContextTypeIs(incorrectly_lax));
}
} }
TEST_P(CookieUtilComputeSameSiteContextTest, ForSubresource) { TEST_P(CookieUtilComputeSameSiteContextTest, ForSubresource) {
@ -918,14 +1112,19 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForceIgnoreSiteForCookies) {
url, site_for_cookies, url, site_for_cookies,
true /* force_ignore_site_for_cookies */), true /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX)); ContextTypeIs(ContextType::SAME_SITE_LAX));
EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest( for (bool is_main_frame_navigation :
method, url, site_for_cookies, initiator, IsMainFrameNavigationPossibleValues(url, site_for_cookies)) {
true /* force_ignore_site_for_cookies */), EXPECT_THAT(cookie_util::ComputeSameSiteContextForRequest(
ContextTypeIs(ContextType::SAME_SITE_STRICT)); method, url, site_for_cookies, initiator,
EXPECT_THAT(cookie_util::ComputeSameSiteContextForResponse( is_main_frame_navigation,
url, site_for_cookies, initiator, true /* force_ignore_site_for_cookies */),
true /* force_ignore_site_for_cookies */), ContextTypeIs(ContextType::SAME_SITE_STRICT));
ContextTypeIs(ContextType::SAME_SITE_LAX)); EXPECT_THAT(
cookie_util::ComputeSameSiteContextForResponse(
url, site_for_cookies, initiator, is_main_frame_navigation,
true /* force_ignore_site_for_cookies */),
ContextTypeIs(ContextType::SAME_SITE_LAX));
}
EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource( EXPECT_THAT(cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, url, site_for_cookies,
true /* force_ignore_site_for_cookies */), true /* force_ignore_site_for_cookies */),
@ -938,7 +1137,8 @@ TEST_P(CookieUtilComputeSameSiteContextTest, ForceIgnoreSiteForCookies) {
INSTANTIATE_TEST_SUITE_P(/* no label */, INSTANTIATE_TEST_SUITE_P(/* no label */,
CookieUtilComputeSameSiteContextTest, CookieUtilComputeSameSiteContextTest,
::testing::Bool()); ::testing::Combine(::testing::Bool(),
::testing::Bool()));
TEST(CookieUtilTest, AdaptCookieAccessResultToBool) { TEST(CookieUtilTest, AdaptCookieAccessResultToBool) {
bool result_out = true; bool result_out = true;

@ -548,10 +548,13 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
request_->site_for_cookies())) { request_->site_for_cookies())) {
force_ignore_site_for_cookies = true; force_ignore_site_for_cookies = true;
} }
bool is_main_frame_navigation = IsolationInfo::RequestType::kMainFrame ==
request_->isolation_info().request_type();
CookieOptions::SameSiteCookieContext same_site_context = CookieOptions::SameSiteCookieContext same_site_context =
net::cookie_util::ComputeSameSiteContextForRequest( net::cookie_util::ComputeSameSiteContextForRequest(
request_->method(), request_->url(), request_->site_for_cookies(), request_->method(), request_->url(), request_->site_for_cookies(),
request_->initiator(), force_ignore_site_for_cookies); request_->initiator(), is_main_frame_navigation,
force_ignore_site_for_cookies);
net::SchemefulSite request_site(request_->url()); net::SchemefulSite request_site(request_->url());
@ -699,10 +702,12 @@ void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete(int result) {
request_->url(), request_->site_for_cookies())) { request_->url(), request_->site_for_cookies())) {
force_ignore_site_for_cookies = true; force_ignore_site_for_cookies = true;
} }
bool is_main_frame_navigation = IsolationInfo::RequestType::kMainFrame ==
request_->isolation_info().request_type();
CookieOptions::SameSiteCookieContext same_site_context = CookieOptions::SameSiteCookieContext same_site_context =
net::cookie_util::ComputeSameSiteContextForResponse( net::cookie_util::ComputeSameSiteContextForResponse(
request_->url(), request_->site_for_cookies(), request_->initiator(), request_->url(), request_->site_for_cookies(), request_->initiator(),
force_ignore_site_for_cookies); is_main_frame_navigation, force_ignore_site_for_cookies);
const CookieAccessDelegate* delegate = cookie_store->cookie_access_delegate(); const CookieAccessDelegate* delegate = cookie_store->cookie_access_delegate();
net::SchemefulSite request_site(request_->url()); net::SchemefulSite request_site(request_->url());

@ -2194,6 +2194,15 @@ TEST_F(URLRequestTest, SameSiteCookies) {
const std::string kHost = "example.test"; const std::string kHost = "example.test";
const std::string kSubHost = "subdomain.example.test"; const std::string kSubHost = "subdomain.example.test";
const std::string kCrossHost = "cross-origin.test"; const std::string kCrossHost = "cross-origin.test";
const url::Origin kOrigin =
url::Origin::Create(test_server.GetURL(kHost, "/"));
const url::Origin kSubOrigin =
url::Origin::Create(test_server.GetURL(kSubHost, "/"));
const url::Origin kCrossOrigin =
url::Origin::Create(test_server.GetURL(kCrossHost, "/"));
const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin);
const SiteForCookies kCrossSiteForCookies =
SiteForCookies::FromOrigin(kCrossOrigin);
// Set up two 'SameSite' cookies on 'example.test' // Set up two 'SameSite' cookies on 'example.test'
{ {
@ -2203,9 +2212,8 @@ TEST_F(URLRequestTest, SameSiteCookies) {
"/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&"
"LaxSameSiteCookie=1;SameSite=Lax"), "LaxSameSiteCookie=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(kSiteForCookies);
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); req->set_initiator(kOrigin);
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/")));
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
@ -2213,15 +2221,21 @@ TEST_F(URLRequestTest, SameSiteCookies) {
EXPECT_EQ(2, network_delegate.set_cookie_count()); EXPECT_EQ(2, network_delegate.set_cookie_count());
} }
// Verify that both cookies are sent for same-site requests. // Verify that both cookies are sent for same-site requests, whether they are
{ // subresource requests, subframe navigations, or main frame navigations.
for (IsolationInfo::RequestType request_type :
{IsolationInfo::RequestType::kMainFrame,
IsolationInfo::RequestType::kSubFrame,
IsolationInfo::RequestType::kOther}) {
TestDelegate d; TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_isolation_info(IsolationInfo::Create(request_type, kOrigin,
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); kOrigin, kSiteForCookies,
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/"))); {} /* party_context */));
req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kOrigin);
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2239,8 +2253,7 @@ TEST_F(URLRequestTest, SameSiteCookies) {
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(kSiteForCookies);
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2259,7 +2272,7 @@ TEST_F(URLRequestTest, SameSiteCookies) {
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/")));
req->set_initiator(url::Origin::Create(test_server.GetURL(kSubHost, "/"))); req->set_initiator(kSubOrigin);
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2276,10 +2289,8 @@ TEST_F(URLRequestTest, SameSiteCookies) {
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(kCrossSiteForCookies);
SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/"))); req->set_initiator(kCrossOrigin);
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2291,16 +2302,17 @@ TEST_F(URLRequestTest, SameSiteCookies) {
} }
// Verify that the lax cookie is sent for cross-site initiators when the // Verify that the lax cookie is sent for cross-site initiators when the
// method is "safe". // method is "safe" and the request is a main frame navigation.
{ {
TestDelegate d; TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_isolation_info(IsolationInfo::Create(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin,
req->set_initiator( kSiteForCookies, {} /* party_context */));
url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kCrossOrigin);
req->set_method("GET"); req->set_method("GET");
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2313,16 +2325,18 @@ TEST_F(URLRequestTest, SameSiteCookies) {
} }
// Verify that neither cookie is sent for cross-site initiators when the // Verify that neither cookie is sent for cross-site initiators when the
// method is unsafe (e.g. POST). // method is unsafe (e.g. POST), even if the request is a main frame
// navigation.
{ {
TestDelegate d; TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS)); TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_isolation_info(IsolationInfo::Create(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin,
req->set_initiator( kSiteForCookies, {} /* party_context */));
url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kCrossOrigin);
req->set_method("POST"); req->set_method("POST");
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
@ -2333,6 +2347,30 @@ TEST_F(URLRequestTest, SameSiteCookies) {
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
} }
// Verify that neither cookie is sent for cross-site initiators when the
// method is safe and the site-for-cookies is same-site, but the request is
// not a main frame navigation.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(IsolationInfo::Create(
IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin,
kSiteForCookies, {} /* party_context */));
req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kCrossOrigin);
req->set_method("GET");
req->Start();
d.RunUntilComplete();
EXPECT_EQ(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
} }
TEST_F(URLRequestTest, SettingSameSiteCookies) { TEST_F(URLRequestTest, SettingSameSiteCookies) {
@ -2345,6 +2383,15 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
const std::string kHost = "example.test"; const std::string kHost = "example.test";
const std::string kSubHost = "subdomain.example.test"; const std::string kSubHost = "subdomain.example.test";
const std::string kCrossHost = "cross-origin.test"; const std::string kCrossHost = "cross-origin.test";
const url::Origin kOrigin =
url::Origin::Create(test_server.GetURL(kHost, "/"));
const url::Origin kSubOrigin =
url::Origin::Create(test_server.GetURL(kSubHost, "/"));
const url::Origin kCrossOrigin =
url::Origin::Create(test_server.GetURL(kCrossHost, "/"));
const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin);
const SiteForCookies kCrossSiteForCookies =
SiteForCookies::FromOrigin(kCrossOrigin);
int expected_cookies = 0; int expected_cookies = 0;
@ -2355,9 +2402,8 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
"/set-cookie?Strict1=1;SameSite=Strict&" "/set-cookie?Strict1=1;SameSite=Strict&"
"Lax1=1;SameSite=Lax"), "Lax1=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(kSiteForCookies);
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); req->set_initiator(kOrigin);
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/")));
// 'SameSite' cookies are settable from strict same-site contexts // 'SameSite' cookies are settable from strict same-site contexts
// (same-origin site_for_cookies, same-origin initiator), so this request // (same-origin site_for_cookies, same-origin initiator), so this request
@ -2378,14 +2424,15 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
"/set-cookie?Strict2=1;SameSite=Strict&" "/set-cookie?Strict2=1;SameSite=Strict&"
"Lax2=1;SameSite=Lax"), "Lax2=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_isolation_info(IsolationInfo::Create(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin,
req->set_initiator( kSiteForCookies, {} /* party_context */));
url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kCrossOrigin);
// 'SameSite' cookies are settable from lax same-site contexts (same-origin // 'SameSite' cookies are settable from lax same-site contexts (same-origin
// site_for_cookies, cross-site initiator), so this request should result in // site_for_cookies, cross-site initiator, main frame navigation), so this
// two cookies being set. // request should result in two cookies being set.
expected_cookies += 2; expected_cookies += 2;
req->Start(); req->Start();
@ -2402,14 +2449,16 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
"/set-cookie?Strict3=1;SameSite=Strict&" "/set-cookie?Strict3=1;SameSite=Strict&"
"Lax3=1;SameSite=Lax"), "Lax3=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(IsolationInfo::Create(
IsolationInfo::RequestType::kMainFrame, kSubOrigin, kSubOrigin,
kSiteForCookies, {} /* party_context */));
req->set_site_for_cookies( req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/")));
req->set_initiator( req->set_initiator(kCrossOrigin);
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
// 'SameSite' cookies are settable from lax same-site contexts (same-site // 'SameSite' cookies are settable from lax same-site contexts (same-site
// site_for_cookies, cross-site initiator), so this request should result in // site_for_cookies, cross-site initiator, main frame navigation), so this
// two cookies being set. // request should result in two cookies being set.
expected_cookies += 2; expected_cookies += 2;
req->Start(); req->Start();
@ -2441,6 +2490,7 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count()); EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count());
} }
int expected_network_delegate_set_cookie_count;
{ {
TestDelegate d; TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest( std::unique_ptr<URLRequest> req(default_context().CreateRequest(
@ -2448,25 +2498,56 @@ TEST_F(URLRequestTest, SettingSameSiteCookies) {
"/set-cookie?Strict5=1;SameSite=Strict&" "/set-cookie?Strict5=1;SameSite=Strict&"
"Lax5=1;SameSite=Lax"), "Lax5=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies( req->set_site_for_cookies(kCrossSiteForCookies);
SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/"))); req->set_initiator(kCrossOrigin);
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
// 'SameSite' cookies are not settable from cross-site contexts, so this // 'SameSite' cookies are not settable from cross-site contexts, so this
// should not result in any new cookies being set. // should not result in any new cookies being set.
expected_cookies += 0; expected_cookies += 0;
// This counts the number of successful calls to CanSetCookie() when
// attempting to set a cookie. The two cookies above were created and
// attempted to be set, and were not rejected by the NetworkDelegate, so the
// count here is 2 more than the number of cookies actually set.
expected_network_delegate_set_cookie_count = expected_cookies + 2;
req->Start(); req->Start();
d.RunUntilComplete(); d.RunUntilComplete();
// This counts the number of cookies actually set. // This counts the number of cookies actually set.
EXPECT_EQ(expected_cookies, EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size())); static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_network_delegate_set_cookie_count,
network_delegate.set_cookie_count());
}
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict6=1;SameSite=Strict&"
"Lax6=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(IsolationInfo::Create(
IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin,
kSiteForCookies, {} /* party_context */));
req->set_site_for_cookies(kSiteForCookies);
req->set_initiator(kCrossOrigin);
// Same-site site-for-cookies, cross-site initiator, non main frame
// navigation -> context is considered cross-site so no SameSite cookies are
// set.
expected_cookies += 0;
// This counts the number of successful calls to CanSetCookie() when // This counts the number of successful calls to CanSetCookie() when
// attempting to set a cookie. The two cookies above were created and // attempting to set a cookie. The two cookies above were created and
// attempted to be set, and were not rejected by the NetworkDelegate, so the // attempted to be set, and were not rejected by the NetworkDelegate, so the
// count here is 2 more than the number of cookies actually set. // count here is 2 more than the number of cookies actually set.
EXPECT_EQ(expected_cookies + 2, network_delegate.set_cookie_count()); expected_network_delegate_set_cookie_count += 2;
req->Start();
d.RunUntilComplete();
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_network_delegate_set_cookie_count,
network_delegate.set_cookie_count());
} }
} }

@ -6474,6 +6474,14 @@ class NetworkContextSplitCacheTest : public NetworkContextTest {
request.trusted_params = ResourceRequest::TrustedParams(); request.trusted_params = ResourceRequest::TrustedParams();
request.trusted_params->isolation_info = isolation_info; request.trusted_params->isolation_info = isolation_info;
params->is_trusted = true; params->is_trusted = true;
// These params must be individually set, to be consistent with the
// IsolationInfo if its request type is a main frame navigation.
// TODO(crbug.com/1172314): Unify these to avoid inconsistencies.
if (isolation_info.request_type() ==
net::IsolationInfo::RequestType::kMainFrame) {
request.is_main_frame = true;
request.update_first_party_url_on_redirect = true;
}
} }
params->automatically_assign_isolation_info = params->automatically_assign_isolation_info =

@ -35,6 +35,12 @@ function redirectTo(origin, url) {
return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url); return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url);
} }
// Returns a URL on |origin| which navigates the window to the given URL (by
// setting window.location).
function navigateTo(origin, url) {
return origin + "/cookies/resources/navigate.html?location=" + encodeURIComponent(url);
}
// Asserts that `document.cookie` contains or does not contain (according to // Asserts that `document.cookie` contains or does not contain (according to
// the value of |present|) a cookie named |name| with a value of |value|. // the value of |present|) a cookie named |name| with a value of |value|.
function assert_dom_cookie(name, value, present) { function assert_dom_cookie(name, value, present) {

@ -0,0 +1,8 @@
<!DOCTYPE html>
<meta charset="utf-8">
<script>
// Navigates the window to a location specified via URL query param.
const params = new URLSearchParams(window.location.search);
const loc = params.get('location');
window.location = loc;
</script>

@ -50,7 +50,7 @@
create_test(SECURE_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain redirecting to same-host fetches are strictly same-site"); create_test(SECURE_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain redirecting to same-host fetches are strictly same-site");
create_test(SECURE_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Cross-site redirecting to same-host fetches are strictly same-site"); create_test(SECURE_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Cross-site redirecting to same-host fetches are strictly same-site");
// Redirect from {same-host,subdomain,cross-site} to same-host: // Redirect from {same-host,subdomain,cross-site} to subdomain:
create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Same-host redirecting to subdomain fetches are strictly same-site"); create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Same-host redirecting to subdomain fetches are strictly same-site");
create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain redirecting to subdomain fetches are strictly same-site"); create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain redirecting to subdomain fetches are strictly same-site");
create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Cross-site redirecting to subdomain fetches are strictly same-site"); create_test(SECURE_SUBDOMAIN_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Cross-site redirecting to subdomain fetches are strictly same-site");
@ -59,4 +59,19 @@
create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site"); create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site");
create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site"); create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site");
create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site"); create_test(SECURE_CROSS_SITE_ORIGIN, redirectTo(SECURE_CROSS_SITE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site");
// Navigate from {same-host,subdomain,cross-site} to same-host:
create_test(SECURE_ORIGIN, navigateTo(SECURE_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Same-host navigating to same-host fetches are strictly same-site");
create_test(SECURE_ORIGIN, navigateTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain navigating to same-host fetches are strictly same-site");
create_test(SECURE_ORIGIN, navigateTo(SECURE_CROSS_SITE_ORIGIN, SECURE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.SAME_SITE, "Cross-site navigating to same-host fetches are cross-site");
// Navigate from {same-host,subdomain,cross-site} to subdomain:
create_test(SECURE_SUBDOMAIN_ORIGIN, navigateTo(SECURE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Same-host navigating to subdomain fetches are strictly same-site");
create_test(SECURE_SUBDOMAIN_ORIGIN, navigateTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, DomSameSiteStatus.SAME_SITE, "Subdomain navigating to subdomain fetches are strictly same-site");
create_test(SECURE_SUBDOMAIN_ORIGIN, navigateTo(SECURE_CROSS_SITE_ORIGIN, SECURE_SUBDOMAIN_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.SAME_SITE, "Cross-site navigating to subdomain fetches are cross-site-site");
// Navigate from {same-host,subdomain,cross-site} to cross-site:
create_test(SECURE_CROSS_SITE_ORIGIN, navigateTo(SECURE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Same-host navigating to cross-site fetches are cross-site");
create_test(SECURE_CROSS_SITE_ORIGIN, navigateTo(SECURE_SUBDOMAIN_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Subdomain navigating to cross-site fetches are cross-site");
create_test(SECURE_CROSS_SITE_ORIGIN, navigateTo(SECURE_CROSS_SITE_ORIGIN, SECURE_CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, DomSameSiteStatus.CROSS_SITE, "Cross-site navigating to cross-site fetches are cross-site");
</script> </script>

@ -1,5 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<meta charset="utf-8"> <meta charset="utf-8">
<script> <script>
window.opener.postMessage({ type: 'COOKIES_SET', cookies: document.cookie }, '*'); if (window.opener)
window.opener.postMessage({ type: 'COOKIES_SET', cookies: document.cookie }, '*');
if (window.parent !== window)
window.parent.postMessage({ type: 'FRAME_COOKIES_SET', cookies: document.cookie }, '*');
</script> </script>

@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<script src="/cookies/resources/cookie-helper.sub.js"></script>
<script>
window.addEventListener('load', function() {
window.opener.postMessage({ type: 'LOADED' }, '*');
});
window.addEventListener('message', function(e) {
if (SECURE_ORIGIN !== window.location.origin)
return;
if (e.data.type === "initialize-iframe")
window.frames[0].location = e.data.url;
if (e.data.type === "navigate-iframe")
window.frames[0].postMessage({ type: 'navigate', url: e.data.url }, '*');
// Relay messages sent by the subframe to the opener.
if (e.data.type === 'FRAME_READY')
window.opener.postMessage({ type: 'FRAME_READY' }, '*');
if (e.data.type === 'FRAME_COOKIES_SET')
window.opener.postMessage({ type: 'FRAME_COOKIES_SET', cookies: e.data.cookies }, '*');
});
</script>
<iframe></iframe>

@ -3,15 +3,13 @@
<script src="/cookies/resources/cookie-helper.sub.js"></script> <script src="/cookies/resources/cookie-helper.sub.js"></script>
<script> <script>
window.addEventListener('load', function() { window.addEventListener('load', function() {
window.opener.postMessage({ type: 'READY' }, '*'); if (window.opener)
window.opener.postMessage({ type: 'READY' }, '*');
if (window.parent !== window)
window.parent.postMessage({ type: 'FRAME_READY' }, '*');
}); });
window.addEventListener('message', function(e) { window.addEventListener('message', function(e) {
if (SECURE_ORIGIN !== window.location.origin)
return;
if (window.location.origin !== e.origin)
return;
if (e.data.type === "navigate") { if (e.data.type === "navigate") {
window.location = e.data.url; window.location = e.data.url;
} }

@ -7,11 +7,13 @@
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/cookies/resources/cookie-helper.sub.js"></script> <script src="/cookies/resources/cookie-helper.sub.js"></script>
<script> <script>
function assert_samesite_cookies_present(cookies, value) { // Asserts that cookies are present or not present (according to `expectation`)
let samesite_cookie_names = ["samesite_strict", "samesite_lax", "samesite_none", "samesite_unspecified"]; // in the cookie string `cookies` with the correct names and value.
for (name of samesite_cookie_names) { function assert_cookies_present(cookies, value, expected_cookie_names, expectation) {
for (name of expected_cookie_names) {
let re = new RegExp("(?:^|; )" + name + "=" + value + "(?:$|;)"); let re = new RegExp("(?:^|; )" + name + "=" + value + "(?:$|;)");
assert_true(re.test(cookies), "`" + name + "=" + value + "` in cookies"); let assertion = expectation ? assert_true : assert_false;
assertion(re.test(cookies), "`" + name + "=" + value + "` in cookies");
} }
} }
@ -32,7 +34,40 @@
let command = (method === "POST") ? "post-form" : "navigate"; let command = (method === "POST") ? "post-form" : "navigate";
w.postMessage({ type: command, url: url_to }, "*"); w.postMessage({ type: command, url: url_to }, "*");
let message = await wait_for_message('COOKIES_SET', origin_to); let message = await wait_for_message('COOKIES_SET', origin_to);
assert_samesite_cookies_present(message.data.cookies, value); let samesite_cookie_names = ['samesite_strict', 'samesite_lax', 'samesite_none', 'samesite_unspecified'];
assert_cookies_present(message.data.cookies, value, samesite_cookie_names, true);
w.close();
}, title);
}
// Opens a page on origin SECURE_ORIGIN containing an iframe on `iframe_origin_from`,
// then navigates that iframe to `iframe_origin_to`. Expects that navigation to set
// some subset of SameSite cookies.
function navigate_iframe_test(iframe_origin_from, iframe_origin_to, cross_site, title) {
promise_test(async function(t) {
// The cookies don't need to be cleared on each run because |value| is
// a new random value on each run, so on each run we are overwriting and
// checking for a cookie with a different random value.
let value = "" + Math.random();
let parent_url = SECURE_ORIGIN + "/cookies/samesite/resources/navigate-iframe.html";
let iframe_url_from = iframe_origin_from + "/cookies/samesite/resources/navigate.html";
let iframe_url_to = iframe_origin_to + "/cookies/resources/setSameSite.py?" + value;
var w = window.open(parent_url);
await wait_for_message('LOADED', SECURE_ORIGIN);
assert_equals(SECURE_ORIGIN, window.origin);
assert_equals(SECURE_ORIGIN, w.origin);
// Navigate the frame to its starting location.
w.postMessage({ type: 'initialize-iframe', url: iframe_url_from }, '*');
await wait_for_message('FRAME_READY', SECURE_ORIGIN);
// Have the frame navigate itself, possibly cross-site.
w.postMessage({ type: 'navigate-iframe', url: iframe_url_to }, '*');
let message = await wait_for_message('FRAME_COOKIES_SET', SECURE_ORIGIN);
// Check for the proper cookies.
let samesite_none_cookies = ['samesite_none'];
let samesite_cookies = ['samesite_strict', 'samesite_lax'];
(isLegacySameSite() ? samesite_none_cookies : samesite_cookies).push('samesite_unspecified');
assert_cookies_present(message.data.cookies, value, samesite_none_cookies, true);
assert_cookies_present(message.data.cookies, value, samesite_cookies, !cross_site);
w.close(); w.close();
}, title); }, title);
} }
@ -41,4 +76,9 @@
navigate_test("GET", SECURE_CROSS_SITE_ORIGIN, "Cross-site top-level navigation should be able to set SameSite=* cookies."); navigate_test("GET", SECURE_CROSS_SITE_ORIGIN, "Cross-site top-level navigation should be able to set SameSite=* cookies.");
navigate_test("POST", SECURE_ORIGIN, "Same-site top-level POST should be able to set SameSite=* cookies."); navigate_test("POST", SECURE_ORIGIN, "Same-site top-level POST should be able to set SameSite=* cookies.");
navigate_test("POST", SECURE_CROSS_SITE_ORIGIN, "Cross-site top-level POST should be able to set SameSite=* cookies."); navigate_test("POST", SECURE_CROSS_SITE_ORIGIN, "Cross-site top-level POST should be able to set SameSite=* cookies.");
navigate_iframe_test(SECURE_ORIGIN, SECURE_ORIGIN, false, "Same-site to same-site iframe navigation should be able to set SameSite=* cookies.");
navigate_iframe_test(SECURE_CROSS_SITE_ORIGIN, SECURE_ORIGIN, true, "Cross-site to same-site iframe navigation should only be able to set SameSite=None cookies.");
navigate_iframe_test(SECURE_ORIGIN, SECURE_CROSS_SITE_ORIGIN, true, "Same-site to cross-site-site iframe navigation should only be able to set SameSite=None cookies.");
navigate_iframe_test(SECURE_CROSS_SITE_ORIGIN, SECURE_CROSS_SITE_ORIGIN, true, "Cross-site to cross-site iframe navigation should only be able to set SameSite=None cookies.");
</script> </script>