Commit dbacc14f authored by Shuran Huang's avatar Shuran Huang Committed by Chromium LUCI CQ

Compute SameParty cookie context in RestrictedCookieManager.

Use the passed-in isolation_info to calculate the SameParty cookie
context of CookieOptions.

Bug: 1136102
Change-Id: I31d95d5f93c66622fbc516b1e956d0cc571ee2a0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2615998
Commit-Queue: Shuran Huang <shuuran@chromium.org>
Reviewed-by: default avatarLily Chen <chlily@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarMaksim Orlovich <morlovich@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843728}
parent 8c988624
...@@ -443,4 +443,110 @@ IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CrossSiteCookieSecurityEnforcement) { ...@@ -443,4 +443,110 @@ IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CrossSiteCookieSecurityEnforcement) {
v.DepictFrameTree(tab->GetFrameTree()->root())); v.DepictFrameTree(tab->GetFrameTree()->root()));
} }
class SamePartyEnabledCookieBrowserTest : public CookieBrowserTest {
public:
~SamePartyEnabledCookieBrowserTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
CookieBrowserTest::SetUpCommandLine(command_line);
// Set up First-Party Sets and also enables net::Feature::kFirstPartySets.
// a.test, b.test and c.test are in the same FPS.
command_line->AppendSwitchASCII(
"use-first-party-set", "https://a.test,https://b.test,https://c.test");
}
};
// SameParty cookies (that aren't marked as http-only) should be available to
// JavaScript.
IN_PROC_BROWSER_TEST_F(SamePartyEnabledCookieBrowserTest, SamePartyCookies) {
// Must use HTTPS because SameParty cookies must be Secure.
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
server.AddDefaultHandlers(GetTestDataFilePath());
SetupCrossSiteRedirector(&server);
ASSERT_TRUE(server.Start());
// The server sets five cookies on a.test/b.test/c.test/d.test.
// d.test is not a member site from the same First-Party Sets with others.
std::string cookies_to_set =
"/set-cookie?unspecified=1;Secure"
"&samesite-none-sameparty=1;SameSite=None;SameParty;Secure"
"&samesite-lax-sameparty=1;SameSite=Lax;SameParty;Secure"
"&sameparty=1;SameParty;Secure"
"&sameparty-http=1;SameParty;Secure;httponly";
GURL url = server.GetURL("a.test", cookies_to_set);
EXPECT_TRUE(NavigateToURL(shell(), url));
url = server.GetURL("b.test", cookies_to_set);
EXPECT_TRUE(NavigateToURL(shell(), url));
url = server.GetURL("c.test", cookies_to_set);
EXPECT_TRUE(NavigateToURL(shell(), url));
// d.test is not in the FPS.
url = server.GetURL("d.test", cookies_to_set);
EXPECT_TRUE(NavigateToURL(shell(), url));
url = server.GetURL("a.test",
"/cross_site_iframe_factory.html?a.test(a.test(),b.test("
"c.test),d.test(b.test(c.test)))");
EXPECT_TRUE(NavigateToURL(shell(), url));
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
RenderFrameHostImpl* main_frame = web_contents->GetMainFrame();
ASSERT_EQ(3u, main_frame->child_count());
RenderFrameHostImpl* a_iframe = main_frame->child_at(0)->current_frame_host();
RenderFrameHostImpl* b_iframe = main_frame->child_at(1)->current_frame_host();
RenderFrameHostImpl* d_iframe = main_frame->child_at(2)->current_frame_host();
EXPECT_EQ("a.test", a_iframe->GetLastCommittedURL().host());
EXPECT_EQ("b.test", b_iframe->GetLastCommittedURL().host());
EXPECT_EQ("d.test", d_iframe->GetLastCommittedURL().host());
// The top-level frame should get all cookies.
EXPECT_EQ(
"unspecified=1; samesite-none-sameparty=1; samesite-lax-sameparty=1; "
"sameparty=1",
GetCookieFromJS(main_frame));
// All same-site/SameParty cookies will be delievered to the a.test -> a.test
// frame, as it is same-site with its ancestors.
EXPECT_EQ(
"unspecified=1; samesite-none-sameparty=1; samesite-lax-sameparty=1; "
"sameparty=1",
GetCookieFromJS(a_iframe));
// SameParty cookies will be delievered to the a.test -> b.test frame, as it
// is in the same First-Party Sets with its ancestors.
EXPECT_EQ("samesite-none-sameparty=1; samesite-lax-sameparty=1; sameparty=1",
GetCookieFromJS(b_iframe));
// samesite-none-sameparty cookie will be delivered to the a.test -> d.test
// frame, as d.test is not in any FPS.
EXPECT_EQ("samesite-none-sameparty=1", GetCookieFromJS(d_iframe));
ASSERT_EQ(1u, b_iframe->child_count());
RenderFrameHostImpl* bc_iframe = b_iframe->child_at(0)->current_frame_host();
EXPECT_EQ("c.test", bc_iframe->GetLastCommittedURL().host());
// SameParty cookies will be delievered to the a.test -> b.test -> c.test
// frame, as it is in the same First-Party Sets with its ancestors.
EXPECT_EQ("samesite-none-sameparty=1; samesite-lax-sameparty=1; sameparty=1",
GetCookieFromJS(bc_iframe));
ASSERT_EQ(1u, d_iframe->child_count());
RenderFrameHostImpl* db_iframe = d_iframe->child_at(0)->current_frame_host();
EXPECT_EQ("b.test", db_iframe->GetLastCommittedURL().host());
// No cookies will be delievered to the a.test -> d.test -> b.test frame, as
// it is embedded in a non-member site.
EXPECT_EQ("", GetCookieFromJS(db_iframe));
ASSERT_EQ(1u, db_iframe->child_count());
RenderFrameHostImpl* dbc_iframe =
db_iframe->child_at(0)->current_frame_host();
EXPECT_EQ("c.test", dbc_iframe->GetLastCommittedURL().host());
// No cookies will be delievered to the a.test -> d.test -> b.test -> c.test
// frame, as it's ancestors has non-member site.
EXPECT_EQ("", GetCookieFromJS(dbc_iframe));
}
} // namespace content } // namespace content
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "net/base/features.h" #include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h" #include "net/base/url_util.h"
#include "net/cookies/cookie_access_delegate.h"
#include "net/http/http_util.h" #include "net/http/http_util.h"
#include "url/gurl.h" #include "url/gurl.h"
#include "url/url_constants.h" #include "url/url_constants.h"
...@@ -610,6 +612,25 @@ bool IsFirstPartySetsEnabled() { ...@@ -610,6 +612,25 @@ bool IsFirstPartySetsEnabled() {
return base::FeatureList::IsEnabled(features::kFirstPartySets); return base::FeatureList::IsEnabled(features::kFirstPartySets);
} }
// Return SamePartyCookieContextType::kCrossParty when:
// 1) `isolation_info` is not fully populated.
// 2) `isolation_info.party_context` is null.
// 3) `cookie_access_delegate.IsContextSamePartyWithSite` returns false.
CookieOptions::SamePartyCookieContextType ComputeSamePartyContext(
const net::SchemefulSite& request_site,
const IsolationInfo& isolation_info,
const CookieAccessDelegate* cookie_access_delegate) {
if (!isolation_info.IsEmpty() && isolation_info.party_context().has_value() &&
cookie_access_delegate &&
cookie_access_delegate->IsContextSamePartyWithSite(
request_site,
isolation_info.network_isolation_key().GetTopFrameSite().value(),
isolation_info.party_context().value())) {
return CookieOptions::SamePartyCookieContextType::kSameParty;
}
return CookieOptions::SamePartyCookieContextType::kCrossParty;
}
CookieSamePartyStatus GetSamePartyStatus(const CanonicalCookie& cookie, CookieSamePartyStatus GetSamePartyStatus(const CanonicalCookie& cookie,
const CookieOptions& options) { const CookieOptions& options) {
if (!IsFirstPartySetsEnabled() || !cookie.IsSameParty() || if (!IsFirstPartySetsEnabled() || !cookie.IsSameParty() ||
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
class GURL; class GURL;
namespace net { namespace net {
class IsolationInfo;
class SchemefulSite;
class CookieAccessDelegate;
namespace cookie_util { namespace cookie_util {
// Constants for use in VLOG // Constants for use in VLOG
...@@ -217,6 +222,16 @@ NET_EXPORT bool IsSchemefulSameSiteEnabled(); ...@@ -217,6 +222,16 @@ NET_EXPORT bool IsSchemefulSameSiteEnabled();
NET_EXPORT bool IsFirstPartySetsEnabled(); NET_EXPORT bool IsFirstPartySetsEnabled();
// Compute SameParty context, determines which of the cookies for `request_url`
// can be accessed. Returns either kCrossParty or kSameParty. `isolation_info`
// must be fully populated. In Chrome, all requests with credentials enabled
// have a fully populated IsolationInfo. But that might not be true for other
// embedders yet (including cast, WebView, etc). Also not sure about iOS.
NET_EXPORT CookieOptions::SamePartyCookieContextType ComputeSamePartyContext(
const net::SchemefulSite& request_url,
const IsolationInfo& isolation_info,
const CookieAccessDelegate* cookie_access_delegate);
// Get the SameParty inclusion status. If the cookie is not SameParty, returns // Get the SameParty inclusion status. If the cookie is not SameParty, returns
// kNoSamePartyEnforcement; if the cookie is SameParty but does not have a // kNoSamePartyEnforcement; if the cookie is SameParty but does not have a
// valid context, returns kEnforceSamePartyExclude. // valid context, returns kEnforceSamePartyExclude.
...@@ -241,6 +256,7 @@ StripAccessResults(const CookieAccessResultList& cookie_access_result_list); ...@@ -241,6 +256,7 @@ StripAccessResults(const CookieAccessResultList& cookie_access_result_list);
NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url); NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url);
} // namespace cookie_util } // namespace cookie_util
} // namespace net } // namespace net
#endif // NET_COOKIES_COOKIE_UTIL_H_ #endif // NET_COOKIES_COOKIE_UTIL_H_
...@@ -173,29 +173,6 @@ net::CookieOptions CreateCookieOptions( ...@@ -173,29 +173,6 @@ net::CookieOptions CreateCookieOptions(
return options; return options;
} }
// Return SamePartyCookieContextType::kCrossParty when:
// 1) `isolation_info` is not fully populated.
// 2) `isolation_info.party_context` is null.
// 3) `cookie_access_delegate.IsContextSamePartyWithSite` returns false.
//
// In Chrome, all requests with credentials enabled have a fully populated
// IsolationInfo. But that might not be true for other embedders yet
// (including cast, WebView, etc). Also not sure about iOS.
net::CookieOptions::SamePartyCookieContextType ComputeSamePartyContext(
const net::SchemefulSite& request_site,
const net::IsolationInfo& isolation_info,
const net::CookieAccessDelegate* cookie_access_delegate) {
if (!isolation_info.IsEmpty() && isolation_info.party_context().has_value() &&
cookie_access_delegate &&
cookie_access_delegate->IsContextSamePartyWithSite(
request_site,
isolation_info.network_isolation_key().GetTopFrameSite().value(),
isolation_info.party_context().value())) {
return net::CookieOptions::SamePartyCookieContextType::kSameParty;
}
return net::CookieOptions::SamePartyCookieContextType::kCrossParty;
}
} // namespace } // namespace
namespace net { namespace net {
...@@ -582,8 +559,8 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() { ...@@ -582,8 +559,8 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
cookie_store->cookie_access_delegate(); cookie_store->cookie_access_delegate();
CookieOptions::SamePartyCookieContextType same_party_context = CookieOptions::SamePartyCookieContextType same_party_context =
ComputeSamePartyContext(request_site, request_->isolation_info(), net::cookie_util::ComputeSamePartyContext(
delegate); request_site, request_->isolation_info(), delegate);
bool is_in_nontrivial_first_party_set = bool is_in_nontrivial_first_party_set =
delegate && delegate->IsInNontrivialFirstPartySet(request_site); delegate && delegate->IsInNontrivialFirstPartySet(request_site);
CookieOptions options = CreateCookieOptions( CookieOptions options = CreateCookieOptions(
...@@ -729,12 +706,12 @@ void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete(int result) { ...@@ -729,12 +706,12 @@ void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete(int result) {
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());
CookieOptions::SamePartyCookieContextType same_party_context = CookieOptions::SamePartyCookieContextType same_party_context =
ComputeSamePartyContext(request_site, request_->isolation_info(), net::cookie_util::ComputeSamePartyContext(
delegate); request_site, request_->isolation_info(), delegate);
bool is_in_nontrivial_first_party_set = bool is_in_nontrivial_first_party_set =
delegate && delegate->IsInNontrivialFirstPartySet(request_site); delegate && delegate->IsInNontrivialFirstPartySet(request_site);
CookieOptions options = CreateCookieOptions( CookieOptions options = CreateCookieOptions(
same_site_context, same_party_context, request_->isolation_info(), same_site_context, same_party_context, request_->isolation_info(),
is_in_nontrivial_first_party_set); is_in_nontrivial_first_party_set);
......
...@@ -40,7 +40,9 @@ net::CookieOptions MakeOptionsForSet( ...@@ -40,7 +40,9 @@ net::CookieOptions MakeOptionsForSet(
mojom::RestrictedCookieManagerRole role, mojom::RestrictedCookieManagerRole role,
const GURL& url, const GURL& url,
const net::SiteForCookies& site_for_cookies, const net::SiteForCookies& site_for_cookies,
const CookieSettings* cookie_settings) { const net::IsolationInfo& isolation_info,
const CookieSettings* cookie_settings,
const net::CookieAccessDelegate* cookie_access_delegate) {
net::CookieOptions options; net::CookieOptions options;
bool force_ignore_site_for_cookies = bool force_ignore_site_for_cookies =
cookie_settings->ShouldIgnoreSameSiteRestrictions( cookie_settings->ShouldIgnoreSameSiteRestrictions(
...@@ -57,6 +59,16 @@ net::CookieOptions MakeOptionsForSet( ...@@ -57,6 +59,16 @@ net::CookieOptions MakeOptionsForSet(
net::cookie_util::ComputeSameSiteContextForSubresource( net::cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, force_ignore_site_for_cookies)); url, site_for_cookies, force_ignore_site_for_cookies));
} }
net::SchemefulSite request_site(url);
options.set_same_party_cookie_context_type(
net::cookie_util::ComputeSamePartyContext(request_site, isolation_info,
cookie_access_delegate));
bool is_in_nontrivial_first_party_set =
cookie_access_delegate &&
cookie_access_delegate->IsInNontrivialFirstPartySet(request_site);
options.set_is_in_nontrivial_first_party_set(
is_in_nontrivial_first_party_set);
return options; return options;
} }
...@@ -64,7 +76,9 @@ net::CookieOptions MakeOptionsForGet( ...@@ -64,7 +76,9 @@ net::CookieOptions MakeOptionsForGet(
mojom::RestrictedCookieManagerRole role, mojom::RestrictedCookieManagerRole role,
const GURL& url, const GURL& url,
const net::SiteForCookies& site_for_cookies, const net::SiteForCookies& site_for_cookies,
const CookieSettings* cookie_settings) { const net::IsolationInfo& isolation_info,
const CookieSettings* cookie_settings,
const net::CookieAccessDelegate* cookie_access_delegate) {
// TODO(https://crbug.com/925311): Wire initiator here. // TODO(https://crbug.com/925311): Wire initiator here.
net::CookieOptions options; net::CookieOptions options;
bool force_ignore_site_for_cookies = bool force_ignore_site_for_cookies =
...@@ -83,6 +97,16 @@ net::CookieOptions MakeOptionsForGet( ...@@ -83,6 +97,16 @@ net::CookieOptions MakeOptionsForGet(
net::cookie_util::ComputeSameSiteContextForSubresource( net::cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, force_ignore_site_for_cookies)); url, site_for_cookies, force_ignore_site_for_cookies));
} }
net::SchemefulSite request_site(url);
options.set_same_party_cookie_context_type(
net::cookie_util::ComputeSamePartyContext(request_site, isolation_info,
cookie_access_delegate));
bool is_in_nontrivial_first_party_set =
cookie_access_delegate &&
cookie_access_delegate->IsInNontrivialFirstPartySet(request_site);
options.set_is_in_nontrivial_first_party_set(
is_in_nontrivial_first_party_set);
return options; return options;
} }
...@@ -202,6 +226,7 @@ RestrictedCookieManager::RestrictedCookieManager( ...@@ -202,6 +226,7 @@ RestrictedCookieManager::RestrictedCookieManager(
origin_(isolation_info.frame_origin().value()), origin_(isolation_info.frame_origin().value()),
site_for_cookies_(isolation_info.site_for_cookies()), site_for_cookies_(isolation_info.site_for_cookies()),
top_frame_origin_(isolation_info.top_frame_origin().value()), top_frame_origin_(isolation_info.top_frame_origin().value()),
isolation_info_(isolation_info),
cookie_observer_(std::move(cookie_observer)) { cookie_observer_(std::move(cookie_observer)) {
DCHECK(cookie_store); DCHECK(cookie_store);
} }
...@@ -233,8 +258,9 @@ void RestrictedCookieManager::GetAllForUrl( ...@@ -233,8 +258,9 @@ void RestrictedCookieManager::GetAllForUrl(
// TODO(morlovich): Try to validate site_for_cookies as well. // TODO(morlovich): Try to validate site_for_cookies as well.
net::CookieOptions net_options = net::CookieOptions net_options = MakeOptionsForGet(
MakeOptionsForGet(role_, url, site_for_cookies, cookie_settings()); role_, url, site_for_cookies, isolation_info_, cookie_settings(),
cookie_store_->cookie_access_delegate());
// TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once // TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once
// removing deprecation warnings. // removing deprecation warnings.
net_options.set_return_excluded_cookies(); net_options.set_return_excluded_cookies();
...@@ -376,8 +402,9 @@ void RestrictedCookieManager::SetCanonicalCookie( ...@@ -376,8 +402,9 @@ void RestrictedCookieManager::SetCanonicalCookie(
DCHECK(sanitized_cookie); DCHECK(sanitized_cookie);
net::CanonicalCookie cookie_copy = *sanitized_cookie; net::CanonicalCookie cookie_copy = *sanitized_cookie;
net::CookieOptions options = net::CookieOptions options = MakeOptionsForSet(
MakeOptionsForSet(role_, url, site_for_cookies, cookie_settings()); role_, url, site_for_cookies, isolation_info_, cookie_settings(),
cookie_store_->cookie_access_delegate());
// TODO(chlily): |url| is validated to be the same origin as |origin_|, but // TODO(chlily): |url| is validated to be the same origin as |origin_|, but
// the path is not checked. If we ever decide to enforce the path constraint // the path is not checked. If we ever decide to enforce the path constraint
// for setting a cookie, we would need to validate the path of |url| somehow // for setting a cookie, we would need to validate the path of |url| somehow
...@@ -425,8 +452,9 @@ void RestrictedCookieManager::AddChangeListener( ...@@ -425,8 +452,9 @@ void RestrictedCookieManager::AddChangeListener(
return; return;
} }
net::CookieOptions net_options = net::CookieOptions net_options = MakeOptionsForGet(
MakeOptionsForGet(role_, url, site_for_cookies, cookie_settings()); role_, url, site_for_cookies, isolation_info_, cookie_settings(),
cookie_store_->cookie_access_delegate());
auto listener = std::make_unique<Listener>( auto listener = std::make_unique<Listener>(
cookie_store_, this, url, site_for_cookies, top_frame_origin, net_options, cookie_store_, this, url, site_for_cookies, top_frame_origin, net_options,
std::move(mojo_listener)); std::move(mojo_listener));
......
...@@ -61,6 +61,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager ...@@ -61,6 +61,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager
const url::Origin& new_top_frame_origin) { const url::Origin& new_top_frame_origin) {
top_frame_origin_ = new_top_frame_origin; top_frame_origin_ = new_top_frame_origin;
} }
void OverrideIsolationInfoForTesting(
const net::IsolationInfo& new_isolation_info) {
site_for_cookies_ = new_isolation_info.site_for_cookies();
origin_ = new_isolation_info.frame_origin().value();
top_frame_origin_ = new_isolation_info.top_frame_origin().value();
isolation_info_ = new_isolation_info;
}
const CookieSettings* cookie_settings() const { return cookie_settings_; } const CookieSettings* cookie_settings() const { return cookie_settings_; }
...@@ -144,9 +151,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager ...@@ -144,9 +151,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager
const mojom::RestrictedCookieManagerRole role_; const mojom::RestrictedCookieManagerRole role_;
net::CookieStore* const cookie_store_; net::CookieStore* const cookie_store_;
const CookieSettings* const cookie_settings_; const CookieSettings* const cookie_settings_;
// TODO(https://crbug/1166215): Consolidate these three fields since
// `isolation_info_` holds copy of those values.
url::Origin origin_; url::Origin origin_;
net::SiteForCookies site_for_cookies_; net::SiteForCookies site_for_cookies_;
url::Origin top_frame_origin_; url::Origin top_frame_origin_;
net::IsolationInfo isolation_info_;
mojo::Remote<mojom::CookieAccessObserver> cookie_observer_; mojo::Remote<mojom::CookieAccessObserver> cookie_observer_;
base::LinkedList<Listener> listeners_; base::LinkedList<Listener> listeners_;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "services/network/restricted_cookie_manager.h" #include "services/network/restricted_cookie_manager.h"
#include <algorithm> #include <algorithm>
#include <set>
#include "base/bind.h" #include "base/bind.h"
#include "base/run_loop.h" #include "base/run_loop.h"
...@@ -26,7 +27,9 @@ ...@@ -26,7 +27,9 @@
#include "net/cookies/cookie_store_test_callbacks.h" #include "net/cookies/cookie_store_test_callbacks.h"
#include "net/cookies/cookie_util.h" #include "net/cookies/cookie_util.h"
#include "net/cookies/test_cookie_access_delegate.h" #include "net/cookies/test_cookie_access_delegate.h"
#include "services/network/cookie_access_delegate_impl.h"
#include "services/network/cookie_settings.h" #include "services/network/cookie_settings.h"
#include "services/network/first_party_sets/first_party_sets.h"
#include "services/network/public/mojom/cookie_access_observer.mojom.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/cookie_manager.mojom.h" #include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/test/test_network_context_client.h" #include "services/network/test/test_network_context_client.h"
...@@ -201,14 +204,26 @@ class RestrictedCookieManagerTest ...@@ -201,14 +204,26 @@ class RestrictedCookieManagerTest
} }
// Set a canonical cookie directly into the store. // Set a canonical cookie directly into the store.
// Uses a cross-site SameSite cookie context. // Default to be a cross-site, cross-party cookie context.
bool SetCanonicalCookie(const net::CanonicalCookie& cookie, bool SetCanonicalCookie(
std::string source_scheme, const net::CanonicalCookie& cookie,
bool can_modify_httponly) { std::string source_scheme,
bool can_modify_httponly,
const net::CookieOptions::SameSiteCookieContext::ContextType
same_site_cookie_context_type = net::CookieOptions::
SameSiteCookieContext::ContextType::CROSS_SITE,
const net::CookieOptions::SamePartyCookieContextType
same_party_cookie_context_type =
net::CookieOptions::SamePartyCookieContextType::kCrossParty) {
net::ResultSavingCookieCallback<net::CookieAccessResult> callback; net::ResultSavingCookieCallback<net::CookieAccessResult> callback;
net::CookieOptions options; net::CookieOptions options;
if (can_modify_httponly) if (can_modify_httponly)
options.set_include_httponly(); options.set_include_httponly();
net::CookieOptions::SameSiteCookieContext same_site_cookie_context(
same_site_cookie_context_type, same_site_cookie_context_type);
options.set_same_site_cookie_context(same_site_cookie_context);
options.set_same_party_cookie_context_type(same_party_cookie_context_type);
cookie_monster_.SetCanonicalCookieAsync( cookie_monster_.SetCanonicalCookieAsync(
std::make_unique<net::CanonicalCookie>(cookie), std::make_unique<net::CanonicalCookie>(cookie),
net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options,
...@@ -279,8 +294,51 @@ class RestrictedCookieManagerTest ...@@ -279,8 +294,51 @@ class RestrictedCookieManagerTest
bool received_bad_message_ = false; bool received_bad_message_ = false;
}; };
namespace { class SamePartyEnabledRestrictedCookieManagerTest
: public RestrictedCookieManagerTest {
public:
SamePartyEnabledRestrictedCookieManagerTest() {
feature_list_.InitAndEnableFeature(net::features::kFirstPartySets);
first_party_sets_.SetManuallySpecifiedSet(
"https://example.com,https://member1.com");
auto cookie_access_delegate = std::make_unique<CookieAccessDelegateImpl>(
mojom::CookieAccessDelegateType::USE_CONTENT_SETTINGS,
&first_party_sets_, &cookie_settings_);
cookie_monster_.SetCookieAccessDelegate(std::move(cookie_access_delegate));
}
~SamePartyEnabledRestrictedCookieManagerTest() override = default;
// Set a canonical cookie directly into the store, has both SameSite=lax and
// SameParty.
void SetSamePartyCookie(const char* name,
const char* value,
const char* domain,
const char* path,
bool secure = true) {
CHECK(SetCanonicalCookie(
*net::CanonicalCookie::CreateUnsafeCookieForTesting(
name, value, domain, path, base::Time(), base::Time(), base::Time(),
/* secure = */ secure,
/* httponly = */ false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT, /* same_party = */ true),
"https", /* can_modify_httponly = */ true,
net::CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX,
net::CookieOptions::SamePartyCookieContextType::kSameParty));
}
private:
base::test::ScopedFeatureList feature_list_;
FirstPartySets first_party_sets_;
};
INSTANTIATE_TEST_SUITE_P(
SameParty,
SamePartyEnabledRestrictedCookieManagerTest,
testing::Values(mojom::RestrictedCookieManagerRole::SCRIPT,
mojom::RestrictedCookieManagerRole::NETWORK));
namespace {
bool CompareCanonicalCookies(const net::CanonicalCookie& c1, bool CompareCanonicalCookies(const net::CanonicalCookie& c1,
const net::CanonicalCookie& c2) { const net::CanonicalCookie& c2) {
return c1.PartialCompare(c2); return c1.PartialCompare(c2);
...@@ -550,6 +608,74 @@ TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) { ...@@ -550,6 +608,74 @@ TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) {
{net::CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE})); {net::CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE}));
} }
TEST_P(SamePartyEnabledRestrictedCookieManagerTest, GetAllForUrlSameParty) {
SetSamePartyCookie("cookie-name", "cookie-value", "example.com", "/");
// Same Party
{
auto options = mojom::CookieManagerGetOptions::New();
options->name = "cookie-name";
options->match_type = mojom::CookieMatchType::STARTS_WITH;
std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl(
GURL("https://example.com/test/"), GURL("https://example.com"),
url::Origin::Create(GURL("https://example.com")), std::move(options));
ASSERT_THAT(cookies, testing::SizeIs(1));
EXPECT_EQ("cookie-name", cookies[0].Name());
EXPECT_EQ("cookie-value", cookies[0].Value());
}
// Same Party. `party_context` contains fps site.
{
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://example.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://member1.com"))}));
auto options = mojom::CookieManagerGetOptions::New();
options->name = "cookie-name";
options->match_type = mojom::CookieMatchType::STARTS_WITH;
std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl(
GURL("https://example.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com")), std::move(options));
ASSERT_THAT(cookies, testing::SizeIs(1));
EXPECT_EQ("cookie-name", cookies[0].Name());
EXPECT_EQ("cookie-value", cookies[0].Value());
}
// Cross Party
{
// `party_context` contains cross-party site.
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://example.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://nonexample.com"))}));
auto options = mojom::CookieManagerGetOptions::New();
options->name = "cookie-name";
options->match_type = mojom::CookieMatchType::STARTS_WITH;
std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl(
GURL("https://example.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com")), std::move(options));
EXPECT_THAT(cookies, testing::IsEmpty());
ASSERT_EQ(3u, recorded_activity().size());
EXPECT_TRUE(
recorded_activity()[2].status.HasExactlyExclusionReasonsForTesting(
{net::CookieInclusionStatus::
EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT}));
}
}
TEST_P(RestrictedCookieManagerTest, SetCanonicalCookie) { TEST_P(RestrictedCookieManagerTest, SetCanonicalCookie) {
EXPECT_TRUE(sync_service_->SetCanonicalCookie( EXPECT_TRUE(sync_service_->SetCanonicalCookie(
*net::CanonicalCookie::CreateUnsafeCookieForTesting( *net::CanonicalCookie::CreateUnsafeCookieForTesting(
...@@ -765,6 +891,70 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookiePolicyWarnActual) { ...@@ -765,6 +891,70 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookiePolicyWarnActual) {
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX}));
} }
TEST_P(SamePartyEnabledRestrictedCookieManagerTest,
SetCanonicalCookieSameParty) {
// Same Party. `party_context` contains fps site.
{
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://member1.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://member1.com"))}));
EXPECT_TRUE(sync_service_->SetCanonicalCookie(
*net::CanonicalCookie::CreateUnsafeCookieForTesting(
"new-name", "new-value", "member1.com", "/", base::Time(),
base::Time(), base::Time(), /* secure = */ true,
/* httponly = */ false, net::CookieSameSite::LAX_MODE,
net::COOKIE_PRIORITY_DEFAULT, /* same_party = */ true),
GURL("https://member1.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com"))));
auto options = mojom::CookieManagerGetOptions::New();
options->name = "new-name";
options->match_type = mojom::CookieMatchType::EQUALS;
std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl(
GURL("https://member1.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com")), std::move(options));
ASSERT_THAT(cookies, testing::SizeIs(1));
EXPECT_EQ("new-name", cookies[0].Name());
EXPECT_EQ("new-value", cookies[0].Value());
}
// Cross Party
{
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://example.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://not-example.com"))}));
EXPECT_FALSE(sync_service_->SetCanonicalCookie(
*net::CanonicalCookie::CreateUnsafeCookieForTesting(
"new-name", "new-value", "example.com", "/", base::Time(),
base::Time(), base::Time(), /* secure = */ true,
/* httponly = */ false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT, /* same_party = */ true),
GURL("https://example.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com"))));
ASSERT_EQ(3u, recorded_activity().size());
EXPECT_EQ(recorded_activity()[2].get, false);
EXPECT_EQ(recorded_activity()[2].url, "https://example.com/test/");
EXPECT_EQ(recorded_activity()[2].site_for_cookies, GURL());
EXPECT_THAT(recorded_activity()[2].cookie,
net::MatchesCookieLine("new-name=new-value"));
EXPECT_TRUE(
recorded_activity()[2].status.HasExactlyExclusionReasonsForTesting(
{net::CookieInclusionStatus::
EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT}));
}
}
TEST_P(RestrictedCookieManagerTest, CookiesEnabledFor) { TEST_P(RestrictedCookieManagerTest, CookiesEnabledFor) {
service_->OverrideSiteForCookiesForTesting( service_->OverrideSiteForCookiesForTesting(
net::SiteForCookies::FromUrl(GURL("https://notexample.com"))); net::SiteForCookies::FromUrl(GURL("https://notexample.com")));
...@@ -1004,6 +1194,61 @@ TEST_P(RestrictedCookieManagerTest, AddChangeListenerFromOpaqueOrigin) { ...@@ -1004,6 +1194,61 @@ TEST_P(RestrictedCookieManagerTest, AddChangeListenerFromOpaqueOrigin) {
ASSERT_THAT(bad_listener.observed_changes(), testing::SizeIs(0)); ASSERT_THAT(bad_listener.observed_changes(), testing::SizeIs(0));
} }
TEST_P(SamePartyEnabledRestrictedCookieManagerTest,
AddChangeListenerSameParty) {
// Same Party. `party_context` contains fps site.
{
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://example.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://member1.com"))}));
mojo::PendingRemote<network::mojom::CookieChangeListener> listener_remote;
mojo::PendingReceiver<network::mojom::CookieChangeListener> receiver =
listener_remote.InitWithNewPipeAndPassReceiver();
sync_service_->AddChangeListener(
GURL("https://example.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com")),
std::move(listener_remote));
TestCookieChangeListener listener(std::move(receiver));
ASSERT_THAT(listener.observed_changes(), testing::IsEmpty());
SetSamePartyCookie("cookie-name", "cookie-value", "example.com", "/");
listener.WaitForChange();
ASSERT_THAT(listener.observed_changes(), testing::SizeIs(1));
EXPECT_EQ(net::CookieChangeCause::INSERTED,
listener.observed_changes()[0].cause);
EXPECT_EQ("cookie-name", listener.observed_changes()[0].cookie.Name());
EXPECT_EQ("cookie-value", listener.observed_changes()[0].cookie.Value());
}
// Cross Party.
{
service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther,
url::Origin::Create(GURL("https://example.com")),
url::Origin::Create(GURL("https://example.com")), net::SiteForCookies(),
std::set<net::SchemefulSite>{
net::SchemefulSite(GURL("https://not-example.com"))}));
mojo::PendingRemote<network::mojom::CookieChangeListener> listener_remote;
mojo::PendingReceiver<network::mojom::CookieChangeListener> receiver =
listener_remote.InitWithNewPipeAndPassReceiver();
sync_service_->AddChangeListener(
GURL("https://example.com/test/"), GURL(),
url::Origin::Create(GURL("https://example.com")),
std::move(listener_remote));
TestCookieChangeListener listener(std::move(receiver));
EXPECT_THAT(listener.observed_changes(), testing::IsEmpty());
SetSamePartyCookie("cookie-name", "cookie-value", "example.com", "/");
EXPECT_THAT(listener.observed_changes(), testing::IsEmpty());
}
}
// Test that the Change listener receives the access semantics, and that they // Test that the Change listener receives the access semantics, and that they
// are taken into account when deciding when to dispatch the change. // are taken into account when deciding when to dispatch the change.
TEST_P(RestrictedCookieManagerTest, ChangeNotificationIncludesAccessSemantics) { TEST_P(RestrictedCookieManagerTest, ChangeNotificationIncludesAccessSemantics) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment