blob: 8ccad0493e0dea31e88a104f268630d8e5e4c500 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2015 The Chromium Authors
nick5f3d76072015-06-09 00:24:002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Arthur Sonzognic686e8f2024-01-11 08:36:375#include <optional>
nick5f3d76072015-06-09 00:24:006#include <string>
7
nick5f3d76072015-06-09 00:24:008#include "base/command_line.h"
9#include "base/files/file_path.h"
Ari Chivukula0be980cd2025-01-30 03:32:4110#include "base/files/file_util.h"
Avi Drissmanadac21992023-01-11 23:46:3911#include "base/functional/bind.h"
12#include "base/functional/callback_helpers.h"
Daniel Cheng3a350ed2024-09-05 00:29:3913#include "base/strings/strcat.h"
Guido Urdanetaef4e91942020-11-09 15:06:2414#include "base/test/bind.h"
Chris Fredricksonb4cf8402022-09-23 22:59:5915#include "base/test/scoped_feature_list.h"
Olivier Li140ade52025-02-06 20:36:0716#include "base/threading/platform_thread.h"
17#include "base/time/time.h"
nick462f9282015-12-04 18:47:4518#include "content/browser/bad_message.h"
danakjc492bf82020-09-09 20:02:4419#include "content/browser/renderer_host/frame_tree.h"
nick5f3d76072015-06-09 00:24:0020#include "content/browser/web_contents/web_contents_impl.h"
Maks Orlovich710d5e32019-07-09 20:16:4521#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2822#include "content/public/browser/browser_task_traits.h"
Gabriel Charette790754c2018-03-16 21:32:5923#include "content/public/browser/browser_thread.h"
nick5f3d76072015-06-09 00:24:0024#include "content/public/browser/render_process_host.h"
W. James MacLean693869ee2024-06-25 17:23:5825#include "content/public/browser/site_isolation_policy.h"
Maks Orlovich710d5e32019-07-09 20:16:4526#include "content/public/browser/storage_partition.h"
Hans Wennborg5ffd1392019-10-16 11:00:0227#include "content/public/common/content_client.h"
nick5f3d76072015-06-09 00:24:0028#include "content/public/common/content_switches.h"
Peter Kasting919ce652020-05-07 10:22:3629#include "content/public/test/browser_test.h"
nick5f3d76072015-06-09 00:24:0030#include "content/public/test/browser_test_utils.h"
31#include "content/public/test/content_browser_test.h"
Scott Violet99861992023-02-08 01:20:1232#include "content/public/test/content_browser_test_content_browser_client.h"
nick5f3d76072015-06-09 00:24:0033#include "content/public/test/content_browser_test_utils.h"
Joshua Thomas14008362025-03-26 14:06:4734#include "content/public/test/test_devtools_protocol_client.h"
carlosk15fb0f02015-07-29 17:07:5135#include "content/public/test/test_utils.h"
nick5f3d76072015-06-09 00:24:0036#include "content/shell/browser/shell.h"
37#include "content/test/content_browser_test_utils_internal.h"
38#include "ipc/ipc_security_test_util.h"
Chris Fredricksonb4cf8402022-09-23 22:59:5939#include "net/base/features.h"
Ari Chivukula0be980cd2025-01-30 03:32:4140#include "net/base/filename_util.h"
Maks Orlovich710d5e32019-07-09 20:16:4541#include "net/cookies/canonical_cookie.h"
Chris Fredrickson34237ce2025-05-13 16:36:1442#include "net/cookies/canonical_cookie_test_helpers.h"
Ayu Ishiif3966ca2020-07-08 17:35:1243#include "net/cookies/cookie_access_result.h"
Lily Chenf068a762019-08-21 21:10:5044#include "net/cookies/cookie_util.h"
nick5f3d76072015-06-09 00:24:0045#include "net/dns/mock_host_resolver.h"
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:0046#include "net/http/alternative_service.h"
Chris Fredrickson9ffdf5b2024-07-09 20:05:0947#include "net/storage_access_api/status.h"
nick5f3d76072015-06-09 00:24:0048#include "net/test/embedded_test_server/embedded_test_server.h"
Aldo Culquicondorcfb369e2025-03-18 19:19:0449#include "services/network/public/cpp/features.h"
Chris Fredricksonf9de3452022-09-12 21:46:5650#include "services/network/public/cpp/network_switches.h"
Maks Orlovich710d5e32019-07-09 20:16:4551#include "services/network/public/mojom/restricted_cookie_manager.mojom-test-utils.h"
52#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
53#include "services/service_manager/public/cpp/interface_provider.h"
nick462f9282015-12-04 18:47:4554#include "testing/gmock/include/gmock/gmock.h"
nick5f3d76072015-06-09 00:24:0055#include "testing/gtest/include/gtest/gtest.h"
Aldo Culquicondor5bf8bc232025-03-20 14:51:3956#include "third_party/blink/public/common/features_generated.h"
mkwstdf1821c62016-02-24 13:07:2057#include "url/gurl.h"
nick5f3d76072015-06-09 00:24:0058
Chris Fredrickson34237ce2025-05-13 16:36:1459using testing::IsEmpty;
60using testing::Key;
61using testing::UnorderedElementsAre;
62
nick5f3d76072015-06-09 00:24:0063namespace content {
64
65namespace {
66
Joshua Thomas14008362025-03-26 14:06:4767void EnableDevtoolsThirdPartyCookieRestriction(
68 TestDevToolsProtocolClient& frame_devtools_client) {
69 base::Value::Dict command_params;
70 frame_devtools_client.SendCommandSync("Network.enable");
71 command_params.Set("enableThirdPartyCookieRestriction", true);
72 command_params.Set("disableThirdPartyCookieMetadata", false);
73 command_params.Set("disableThirdPartyCookieHeuristics", false);
74 frame_devtools_client.SendCommandAsync("Network.setCookieControls",
75 std::move(command_params));
76}
77
Maks Orlovich710d5e32019-07-09 20:16:4578void SetCookieFromJS(RenderFrameHost* frame, std::string cookie) {
79 EvalJsResult result = EvalJs(frame, "document.cookie = '" + cookie + "'");
80 EXPECT_TRUE(result.error.empty()) << result.error;
81}
82
nick5f3d76072015-06-09 00:24:0083std::string GetCookieFromJS(RenderFrameHost* frame) {
Avi Drissman49ecc702021-04-15 20:37:4184 return EvalJs(frame, "document.cookie;").ExtractString();
nick5f3d76072015-06-09 00:24:0085}
86
Maks Orlovich710d5e32019-07-09 20:16:4587void SetCookieDirect(WebContentsImpl* tab,
88 const GURL& url,
89 const std::string& cookie_line) {
Lily Chene1d272a52019-08-12 17:00:5690 net::CookieOptions options;
91 // Allow setting SameSite cookies.
92 options.set_same_site_cookie_context(
Steven Bingler8d76c2a42020-03-24 17:13:3293 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
Lily Chene1d272a52019-08-12 17:00:5694
Ari Chivukula92851c32024-04-09 12:37:4495 auto cookie_obj = net::CanonicalCookie::CreateForTesting(url, cookie_line,
96 base::Time::Now());
Maks Orlovich710d5e32019-07-09 20:16:4597
98 base::RunLoop run_loop;
Lukasz Anforowiczb9a969a2021-04-29 15:26:2599 tab->GetBrowserContext()
100 ->GetDefaultStoragePartition()
Maks Orlovich710d5e32019-07-09 20:16:45101 ->GetCookieManagerForBrowserProcess()
102 ->SetCanonicalCookie(
Lily Chen96f29a132020-04-15 17:59:36103 *cookie_obj, url, options,
Maks Orlovich710d5e32019-07-09 20:16:45104 base::BindLambdaForTesting(
Ayu Ishiif3966ca2020-07-08 17:35:12105 [&](net::CookieAccessResult status) { run_loop.Quit(); }));
Maks Orlovich710d5e32019-07-09 20:16:45106 run_loop.Run();
107}
108
109std::string GetCookiesDirect(WebContentsImpl* tab, const GURL& url) {
Lily Chene1d272a52019-08-12 17:00:56110 net::CookieOptions options;
111 // Allow setting SameSite cookies.
112 options.set_same_site_cookie_context(
Steven Bingler8d76c2a42020-03-24 17:13:32113 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
Maks Orlovich710d5e32019-07-09 20:16:45114 net::CookieList result;
115 base::RunLoop run_loop;
Lukasz Anforowiczb9a969a2021-04-29 15:26:25116 tab->GetBrowserContext()
117 ->GetDefaultStoragePartition()
Maks Orlovich710d5e32019-07-09 20:16:45118 ->GetCookieManagerForBrowserProcess()
Ayu Ishiibc6fdb0a2020-06-08 22:59:19119 ->GetCookieList(
Aykut Bulut244341e2021-12-09 15:57:25120 url, options, net::CookiePartitionKeyCollection(),
Ayu Ishiibc6fdb0a2020-06-08 22:59:19121 base::BindLambdaForTesting(
122 [&](const net::CookieAccessResultList& cookie_list,
123 const net::CookieAccessResultList& excluded_cookies) {
124 result = net::cookie_util::StripAccessResults(cookie_list);
125 run_loop.Quit();
126 }));
Maks Orlovich710d5e32019-07-09 20:16:45127 run_loop.Run();
128 return net::CanonicalCookie::BuildCookieLine(result);
rockot6a2d66a2016-08-04 00:15:25129}
130
nick5f3d76072015-06-09 00:24:00131} // namespace
132
Aldo Culquicondor5bf8bc232025-03-20 14:51:39133class CookieBrowserTest
134 : public ContentBrowserTest,
135 public ::testing::WithParamInterface<std::tuple<bool, bool>> {
Aldo Culquicondorcfb369e2025-03-18 19:19:04136 public:
Chris Fredrickson34237ce2025-05-13 16:36:14137 CookieBrowserTest()
138 : https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
Aldo Culquicondor5bf8bc232025-03-20 14:51:39139 scoped_feature_list_.InitWithFeatureStates(
140 {{network::features::kGetCookiesOnSet, GetCookiesOnSetEnabled()},
141 {blink::features::kAsyncSetCookie, AsyncSetCookieEnabled()}});
Aldo Culquicondorcfb369e2025-03-18 19:19:04142 }
143 ~CookieBrowserTest() override = default;
144
mkwstf71d0bd2016-03-21 14:15:24145 protected:
Andrew Williams748874612023-07-24 19:55:17146 void SetUpCommandLine(base::CommandLine* command_line) override {
147 command_line->AppendSwitch(
mkwstf71d0bd2016-03-21 14:15:24148 switches::kEnableExperimentalWebPlatformFeatures);
mkwstf71d0bd2016-03-21 14:15:24149 }
jam8f34ea72017-04-26 17:48:55150
151 void SetUpOnMainThread() override {
152 // Support multiple sites on the test server.
153 host_resolver()->AddRule("*", "127.0.0.1");
Chris Fredrickson34237ce2025-05-13 16:36:14154 https_server_.AddDefaultHandlers(GetTestDataFilePath());
155 https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
156 SetupCrossSiteRedirector(&https_server_);
157 ASSERT_TRUE(https_server_.Start());
jam8f34ea72017-04-26 17:48:55158 }
Aldo Culquicondorcfb369e2025-03-18 19:19:04159
Aldo Culquicondor5bf8bc232025-03-20 14:51:39160 bool GetCookiesOnSetEnabled() { return std::get<0>(GetParam()); }
161
162 bool AsyncSetCookieEnabled() { return std::get<1>(GetParam()); }
163
Chris Fredrickson34237ce2025-05-13 16:36:14164 net::test_server::EmbeddedTestServer https_server_;
Aldo Culquicondorcfb369e2025-03-18 19:19:04165 base::test::ScopedFeatureList scoped_feature_list_;
mkwstf71d0bd2016-03-21 14:15:24166};
nick5f3d76072015-06-09 00:24:00167
Aldo Culquicondor5bf8bc232025-03-20 14:51:39168INSTANTIATE_TEST_SUITE_P(
169 ,
170 CookieBrowserTest,
171 testing::Combine(testing::Bool(), testing::Bool()),
172 [](const testing::TestParamInfo<std::tuple<bool, bool>>& info) {
173 std::string name =
174 std::get<0>(info.param) ? "GetOnSetEnabled" : "GetOnSetDisabled";
175 name += "_";
176 name += std::get<1>(info.param) ? "Async" : "Sync";
177 return name;
178 });
Aldo Culquicondorcfb369e2025-03-18 19:19:04179
nick5f3d76072015-06-09 00:24:00180// Exercises basic cookie operations via javascript, including an http page
181// interacting with secure cookies.
Aldo Culquicondorcfb369e2025-03-18 19:19:04182IN_PROC_BROWSER_TEST_P(CookieBrowserTest, Cookies) {
nick5f3d76072015-06-09 00:24:00183 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:27184 ASSERT_TRUE(embedded_test_server()->Start());
nick5f3d76072015-06-09 00:24:00185
Lily Chen15b3ff882021-05-21 01:27:41186 // The server sends a HttpOnly cookie. The RestrictedCookieManager should
nick462f9282015-12-04 18:47:45187 // never allow this to be sent to any renderer process.
Maks Orlovichbd04d782020-11-17 21:23:34188 GURL https_url =
Chris Fredrickson34237ce2025-05-13 16:36:14189 https_server_.GetURL("a.test", "/set-cookie?notforjs=1;HttpOnly");
Maks Orlovichbd04d782020-11-17 21:23:34190 GURL http_url =
191 embedded_test_server()->GetURL("a.test", "/frame_with_load_event.html");
nick5f3d76072015-06-09 00:24:00192
193 Shell* shell2 = CreateBrowser();
Alex Moshchukaa95adf52019-08-13 00:02:02194 EXPECT_TRUE(NavigateToURL(shell(), http_url));
195 EXPECT_TRUE(NavigateToURL(shell2, https_url));
nick5f3d76072015-06-09 00:24:00196
197 WebContentsImpl* web_contents_https =
198 static_cast<WebContentsImpl*>(shell2->web_contents());
199 WebContentsImpl* web_contents_http =
200 static_cast<WebContentsImpl*>(shell()->web_contents());
Alex Moshchuk4bc5dfc2025-06-06 17:23:00201 if (AreStrictSiteInstancesEnabled()) {
Maks Orlovichbd04d782020-11-17 21:23:34202 EXPECT_EQ("http://a.test/",
Aaron Colwelle953e562019-07-24 16:47:36203 web_contents_http->GetSiteInstance()->GetSiteURL().spec());
W. James MacLean03da92242024-06-14 13:46:04204 // Create expected site url, including port if origin isolation is enabled.
205 std::string expected_site_url =
W. James MacLean693869ee2024-06-25 17:23:58206 SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault()
W. James MacLean03da92242024-06-14 13:46:04207 ? url::Origin::Create(https_url).GetURL().spec()
208 : std::string("https://a.test/");
209 EXPECT_EQ(expected_site_url,
Aaron Colwelle953e562019-07-24 16:47:36210 web_contents_https->GetSiteInstance()->GetSiteURL().spec());
Sharon Yang02279222025-01-15 19:09:19211 } else {
212 // Note: Both use the default SiteInstance because the URLs don't require
213 // a dedicated process, but these default SiteInstances are not the same
214 // object because they come from different BrowsingInstances.
215 EXPECT_TRUE(web_contents_http->GetSiteInstance()->IsDefaultSiteInstance());
216 EXPECT_TRUE(web_contents_https->GetSiteInstance()->IsDefaultSiteInstance());
217 EXPECT_NE(web_contents_http->GetSiteInstance(),
218 web_contents_https->GetSiteInstance());
219 EXPECT_FALSE(web_contents_http->GetSiteInstance()->IsRelatedSiteInstance(
220 web_contents_https->GetSiteInstance()));
Aaron Colwelle953e562019-07-24 16:47:36221 }
nick5f3d76072015-06-09 00:24:00222
223 EXPECT_NE(web_contents_http->GetSiteInstance()->GetProcess(),
224 web_contents_https->GetSiteInstance()->GetProcess());
225
Dave Tapuska327c06c92022-06-13 20:31:51226 EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame()));
227 EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame()));
nick5f3d76072015-06-09 00:24:00228
229 // Non-TLS page writes secure cookie.
Dave Tapuska327c06c92022-06-13 20:31:51230 EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(),
Avi Drissman49ecc702021-04-15 20:37:41231 "document.cookie = 'A=1; secure;';"));
Dave Tapuska327c06c92022-06-13 20:31:51232 EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame()));
233 EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame()));
nick5f3d76072015-06-09 00:24:00234
Joshua Hoodf54ef852022-02-23 17:33:58235 // Non-TLS page writes not-secure cookie.
Dave Tapuska327c06c92022-06-13 20:31:51236 EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(),
237 "document.cookie = 'B=2';"));
Dave Tapuska327c06c92022-06-13 20:31:51238 EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame()));
Aldo Culquicondorcfb369e2025-03-18 19:19:04239 EXPECT_EQ("B=2", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame()));
nick5f3d76072015-06-09 00:24:00240
mkwstf71d0bd2016-03-21 14:15:24241 // TLS page writes secure cookie.
Dave Tapuska327c06c92022-06-13 20:31:51242 EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(),
Avi Drissman49ecc702021-04-15 20:37:41243 "document.cookie = 'C=3;secure;';"));
Dave Tapuska327c06c92022-06-13 20:31:51244 EXPECT_EQ("B=2; C=3",
245 GetCookieFromJS(web_contents_https->GetPrimaryMainFrame()));
246 EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame()));
nick5f3d76072015-06-09 00:24:00247
248 // TLS page writes not-secure cookie.
Dave Tapuska327c06c92022-06-13 20:31:51249 EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(),
250 "document.cookie = 'D=4';"));
mkwstf71d0bd2016-03-21 14:15:24251 EXPECT_EQ("B=2; C=3; D=4",
Dave Tapuska327c06c92022-06-13 20:31:51252 GetCookieFromJS(web_contents_https->GetPrimaryMainFrame()));
253 EXPECT_EQ("B=2; D=4",
254 GetCookieFromJS(web_contents_http->GetPrimaryMainFrame()));
nick5f3d76072015-06-09 00:24:00255}
256
Joshua Bell1d432be2018-06-27 19:59:48257// Ensure "priority" cookie option is settable via document.cookie.
Aldo Culquicondorcfb369e2025-03-18 19:19:04258IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookiePriority) {
Joshua Bell1d432be2018-06-27 19:59:48259 ASSERT_TRUE(embedded_test_server()->Start());
260
261 struct {
262 std::string param;
263 net::CookiePriority priority;
264 } cases[] = {{"name=value", net::COOKIE_PRIORITY_DEFAULT},
265 {"name=value;priority=Low", net::COOKIE_PRIORITY_LOW},
266 {"name=value;priority=Medium", net::COOKIE_PRIORITY_MEDIUM},
267 {"name=value;priority=High", net::COOKIE_PRIORITY_HIGH}};
268
269 for (auto test_case : cases) {
270 GURL url = embedded_test_server()->GetURL("/set_document_cookie.html?" +
271 test_case.param);
Alex Moshchukaa95adf52019-08-13 00:02:02272 EXPECT_TRUE(NavigateToURL(shell(), url));
Joshua Bell1d432be2018-06-27 19:59:48273 std::vector<net::CanonicalCookie> cookies =
274 GetCanonicalCookies(shell()->web_contents()->GetBrowserContext(), url);
275
276 EXPECT_EQ(1u, cookies.size());
277 EXPECT_EQ("name", cookies[0].Name());
278 EXPECT_EQ("value", cookies[0].Value());
279 EXPECT_EQ(test_case.priority, cookies[0].Priority());
280 }
281}
282
mkwstdf1821c62016-02-24 13:07:20283// SameSite cookies (that aren't marked as http-only) should be available to
284// JavaScript.
Aldo Culquicondorcfb369e2025-03-18 19:19:04285IN_PROC_BROWSER_TEST_P(CookieBrowserTest, SameSiteCookies) {
Lily Chene1d272a52019-08-12 17:00:56286 // Must use HTTPS because SameSite=None cookies must be Secure.
mkwstdf1821c62016-02-24 13:07:20287
David Benjaminddf9671c32019-12-03 16:54:12288 // The server sets eight cookies on 'a.test' and on 'b.test', then loads
289 // a page that frames both 'a.test' and 'b.test' under 'a.test'.
mkwstf71d0bd2016-03-21 14:15:24290 std::string cookies_to_set =
Lily Chene1d272a52019-08-12 17:00:56291 "/set-cookie?none=1;SameSite=None;Secure" // SameSite=None must be
292 // Secure.
293 "&none-insecure=1;SameSite=None"
mkwstf71d0bd2016-03-21 14:15:24294 "&strict=1;SameSite=Strict"
Lily Chene1d272a52019-08-12 17:00:56295 "&unspecified=1" // unspecified SameSite should be treated as Lax.
mkwstf71d0bd2016-03-21 14:15:24296 "&lax=1;SameSite=Lax"
Lily Chene1d272a52019-08-12 17:00:56297 "&none-http=1;SameSite=None;Secure;httponly"
mkwstf71d0bd2016-03-21 14:15:24298 "&strict-http=1;SameSite=Strict;httponly"
Lily Chene1d272a52019-08-12 17:00:56299 "&unspecified-http=1;httponly"
mkwstf71d0bd2016-03-21 14:15:24300 "&lax-http=1;SameSite=Lax;httponly";
301
Chris Fredrickson34237ce2025-05-13 16:36:14302 GURL url = https_server_.GetURL("a.test", cookies_to_set);
Alex Moshchukaeb20fe32019-09-25 17:40:01303 EXPECT_TRUE(NavigateToURL(shell(), url));
Chris Fredrickson34237ce2025-05-13 16:36:14304 url = https_server_.GetURL("b.test", cookies_to_set);
Alex Moshchukaeb20fe32019-09-25 17:40:01305 EXPECT_TRUE(NavigateToURL(shell(), url));
Chris Fredrickson34237ce2025-05-13 16:36:14306 url = https_server_.GetURL(
David Benjaminddf9671c32019-12-03 16:54:12307 "a.test", "/cross_site_iframe_factory.html?a.test(a.test(),b.test())");
Alex Moshchukaa95adf52019-08-13 00:02:02308 EXPECT_TRUE(NavigateToURL(shell(), url));
mkwstdf1821c62016-02-24 13:07:20309
310 WebContentsImpl* web_contents =
311 static_cast<WebContentsImpl*>(shell()->web_contents());
Dave Tapuska327c06c92022-06-13 20:31:51312 RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();
Carlos Caballero15caeeb2021-10-27 09:57:55313 RenderFrameHost* a_iframe = web_contents->GetPrimaryFrameTree()
314 .root()
315 ->child_at(0)
316 ->current_frame_host();
317 RenderFrameHost* b_iframe = web_contents->GetPrimaryFrameTree()
318 .root()
319 ->child_at(1)
320 ->current_frame_host();
mkwstdf1821c62016-02-24 13:07:20321
Lily Chene1d272a52019-08-12 17:00:56322 // The top-level frame should get all same-site cookies.
323 EXPECT_EQ("none=1; strict=1; unspecified=1; lax=1",
324 GetCookieFromJS(main_frame));
mkwstf71d0bd2016-03-21 14:15:24325
326 // Same-site cookies will be delievered to the 'a.com' frame, as it is same-
327 // site with its ancestors.
Lily Chene1d272a52019-08-12 17:00:56328 EXPECT_EQ("none=1; strict=1; unspecified=1; lax=1",
329 GetCookieFromJS(a_iframe));
mkwstf71d0bd2016-03-21 14:15:24330
331 // Same-site cookies should not be delievered to the 'b.com' frame, as it
Lily Chene1d272a52019-08-12 17:00:56332 // isn't same-site with its ancestors. The SameSite=None but insecure cookie
333 // is rejected.
334 EXPECT_EQ("none=1", GetCookieFromJS(b_iframe));
mkwstdf1821c62016-02-24 13:07:20335}
336
Chris Fredrickson34237ce2025-05-13 16:36:14337// Prefixed cookies (that aren't marked as http-only) should be available to
338// JavaScript.
339IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Read) {
340 // Must use HTTPS because prefixed cookies must be Secure.
341
342 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
343 https_server_.GetURL("a.test", "/"),
344 "__Host-cookie=1;Secure;Path=/"));
345 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
346 https_server_.GetURL("a.test", "/"),
347 "__Secure-cookie=1;Secure"));
348 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
349 https_server_.GetURL("a.test", "/"),
350 "__Secure-http-cookie=1;Secure;HttpOnly"));
351
352 EXPECT_TRUE(
353 NavigateToURL(shell(), https_server_.GetURL("a.test", "/empty.html")));
354
355 EXPECT_THAT(EvalJs(shell(), "document.cookie").ExtractString(),
356 net::CookieStringIs(UnorderedElementsAre(
357 Key("__Host-cookie"), Key("__Secure-cookie"))));
358}
359
360IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Read_Insecure) {
361 ASSERT_TRUE(embedded_test_server()->Start());
362 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
363 https_server_.GetURL("a.test", "/"),
364 "__Host-cookie=1;Secure;Path=/"));
365 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
366 https_server_.GetURL("a.test", "/"),
367 "__Secure-cookie=1;Secure"));
368 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
369 https_server_.GetURL("a.test", "/"),
370 "__Secure-http-cookie=1;Secure;HttpOnly"));
371
372 EXPECT_TRUE(NavigateToURL(
373 shell(), embedded_test_server()->GetURL("a.test", "/empty.html")));
374
375 EXPECT_EQ(EvalJs(shell(), "document.cookie"), "");
376}
377
378// Prefixed cookies should be writable by JavaScript.
379IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Write) {
380 // Must use HTTPS because prefixed cookies must be Secure.
381
382 EXPECT_TRUE(
383 NavigateToURL(shell(), https_server_.GetURL("a.test", "/empty.html")));
384
385 EXPECT_TRUE(ExecJs(shell(), R"js(
386 // Valid cookies:
387 document.cookie = "__Host-cookie=1;Secure;Path=/";
388 document.cookie = "__Secure-cookie=1;Secure";
389 // Invalid cookies:
390 document.cookie = "__Secure-http-cookie=1;Secure;HttpOnly";
391 document.cookie = "__Secure-missing-attr=1";
392 document.cookie = "__Host-wrong-path=1;Secure;";
393 document.cookie = "__Host-wrong-domain=1;Secure;Domain=a.test";
394 document.cookie = "__Host-wrong-secure=1;Path=/";
395 )js"));
396
397 EXPECT_THAT(GetCanonicalCookies(shell()->web_contents()->GetBrowserContext(),
398 https_server_.GetURL("a.test", "/")),
399 UnorderedElementsAre(
400 net::MatchesCookieNameValue("__Host-cookie", "1"),
401 net::MatchesCookieNameValue("__Secure-cookie", "1")));
402}
403
404IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Write_Insecure) {
405 ASSERT_TRUE(embedded_test_server()->Start());
406 EXPECT_TRUE(NavigateToURL(
407 shell(), embedded_test_server()->GetURL("a.test", "/empty.html")));
408
409 EXPECT_TRUE(ExecJs(shell(), R"js(
410 document.cookie = "__Host-cookie=1;Secure;Path=/";
411 document.cookie = "__Secure-cookie=1;Secure";
412 document.cookie = "__Secure-http-cookie=1;Secure;HttpOnly";
413 document.cookie = "__Secure-missing-attr=1";
414 document.cookie = "__Host-wrong-path=1;Secure;";
415 document.cookie = "__Host-wrong-domain=1;Secure;Domain=a.test";
416 document.cookie = "__Host-wrong-secure=1;Path=/";
417 )js"));
418
419 EXPECT_THAT(
420 GetCanonicalCookies(shell()->web_contents()->GetBrowserContext(),
421 embedded_test_server()->GetURL("a.test", "/")),
422 IsEmpty());
423}
424
Luca Versari7ac40502025-05-14 18:48:06425// embedded_test_server() uses http, which is insecure, but localhost is
426// allowed to set prefixed cookies anyway.
427IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Write_Localhost) {
Luca Versari7ac40502025-05-14 18:48:06428 ASSERT_TRUE(embedded_test_server()->Start());
429 EXPECT_TRUE(NavigateToURL(
430 shell(), embedded_test_server()->GetURL("localhost", "/empty.html")));
431
432 EXPECT_TRUE(ExecJs(shell(), R"js(
433 document.cookie = "__Host-cookie=1;Secure;Path=/";
434 document.cookie = "__Secure-cookie=1;Secure";
435 document.cookie = "__Secure-http-cookie=1;Secure;HttpOnly";
436 document.cookie = "__Secure-missing-attr=1";
437 document.cookie = "__Host-wrong-path=1;Secure;";
438 document.cookie = "__Host-wrong-domain=1;Secure;Domain=a.test";
439 document.cookie = "__Host-wrong-secure=1;Path=/";
440 )js"));
441
442 EXPECT_THAT(
443 GetCanonicalCookies(shell()->web_contents()->GetBrowserContext(),
444 embedded_test_server()->GetURL("localhost", "/")),
445 UnorderedElementsAre(
446 net::MatchesCookieNameValue("__Host-cookie", "1"),
447 net::MatchesCookieNameValue("__Secure-cookie", "1")));
448}
449
Joshua Thomas14008362025-03-26 14:06:47450IN_PROC_BROWSER_TEST_P(CookieBrowserTest,
451 CookieJarInvalidatesCacheWithNewDevtoolsControls) {
452 // Must use HTTPS because SameSite=None cookies must be Secure.
Joshua Thomas14008362025-03-26 14:06:47453
454 // Set a single cookie that we'll access from a third-party context
455 std::string cookies_to_set =
456 "/set-cookie?none=1;SameSite=None;Secure"; // SameSite=None must be
457 // Secure
458
Chris Fredrickson34237ce2025-05-13 16:36:14459 GURL url = https_server_.GetURL("b.test", cookies_to_set);
Joshua Thomas14008362025-03-26 14:06:47460 EXPECT_TRUE(NavigateToURL(shell(), url));
461
462 WebContentsImpl* web_contents =
463 static_cast<WebContentsImpl*>(shell()->web_contents());
464 // Turn on third-party cookie restriction from devtools. This needs to happen
465 // from a top level client
466 TestDevToolsProtocolClient devtools_client;
467 devtools_client.AttachToWebContents(web_contents);
468 EnableDevtoolsThirdPartyCookieRestriction(devtools_client);
469
Chris Fredrickson34237ce2025-05-13 16:36:14470 url = https_server_.GetURL(
471 "a.test", "/cross_site_iframe_factory.html?a.test(b.test())");
Joshua Thomas14008362025-03-26 14:06:47472 EXPECT_TRUE(NavigateToURL(shell(), url));
473
474 RenderFrameHost* oop_iframe = web_contents->GetPrimaryFrameTree()
475 .root()
476 ->child_at(0)
477 ->current_frame_host();
478
479 // Attach devtools client to the sub frame, but disable the controls at first
480 devtools_client.DetachProtocolClient();
481 devtools_client.AttachToFrameTreeHost(oop_iframe);
482 devtools_client.SendCommandSync("Network.disable");
483
484 // Check Get->Get
485 // Overrides should not apply after disabling the controls
486 EXPECT_EQ("none=1", GetCookieFromJS(oop_iframe));
487
488 // Confirm cache is invalidated by observing new value from document.cookie
489 // when re-enabling devtools
490 devtools_client.SendCommandSync("Network.enable");
491 EXPECT_EQ("", GetCookieFromJS(oop_iframe));
492
493 // Check Set->Get
494 // Set a cookie with devtools disabled
495 devtools_client.SendCommandSync("Network.disable");
496 SetCookieFromJS(oop_iframe, "none=2; SameSite=None; Secure");
497
498 // Confirm cache is invalidated by observing no cookie from document.cookie
499 // when re-enabling devtools
500 devtools_client.SendCommandSync("Network.enable");
501 EXPECT_EQ("", GetCookieFromJS(oop_iframe));
502
503 devtools_client.DetachProtocolClient();
504}
505
Aldo Culquicondorcfb369e2025-03-18 19:19:04506IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieTruncatingCharFromJavascript) {
Andrew Williams748874612023-07-24 19:55:17507 ASSERT_TRUE(embedded_test_server()->Start());
508
509 ASSERT_TRUE(
510 NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
511
512 WebContentsImpl* tab = static_cast<WebContentsImpl*>(shell()->web_contents());
513 RenderFrameHost* frame = tab->GetPrimaryMainFrame();
514
515 // Test scenarios where a control char may appear at start, middle and end of
516 // a cookie line. Control char array with NULL (\x0), CR (\xD), and LF (xA).
517 const std::string kTestChars[] = {"\\x00", "\\x0D", "\\x0A"};
518
519 for (const std::string& ctl_string : kTestChars) {
520 // Control char at the start of the string.
521 // Note that when truncation of this cookie string occurs, no histogram
522 // entries get recorded because the code bails out early on the resulting
523 // empty cookie string.
Daniel Cheng3a350ed2024-09-05 00:29:39524 std::string cookie_string = base::StrCat({ctl_string, "foo1=bar"});
Andrew Williams748874612023-07-24 19:55:17525 SetCookieFromJS(frame, cookie_string);
526
527 // Control char in the middle of the string.
Daniel Cheng3a350ed2024-09-05 00:29:39528 cookie_string = base::StrCat({"foo2=bar;", ctl_string, "httponly"});
Andrew Williams748874612023-07-24 19:55:17529 SetCookieFromJS(frame, cookie_string);
530
Daniel Cheng3a350ed2024-09-05 00:29:39531 cookie_string = base::StrCat({"foo3=ba", ctl_string, "r; httponly"});
Andrew Williams748874612023-07-24 19:55:17532 SetCookieFromJS(frame, cookie_string);
533
534 // Control char at the end of the string.
Daniel Cheng3a350ed2024-09-05 00:29:39535 cookie_string = base::StrCat({"foo4=bar;", ctl_string});
Andrew Williams748874612023-07-24 19:55:17536 SetCookieFromJS(frame, cookie_string);
537 }
538
Andrew Williamsb19527f2024-05-17 14:05:14539 EXPECT_EQ("", GetCookieFromJS(frame));
Andrew Williams748874612023-07-24 19:55:17540}
541
Aldo Culquicondorcfb369e2025-03-18 19:19:04542IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieTruncatingCharFromHeaders) {
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00543 std::string cookie_string;
544 embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
545 [&](const net::test_server::HttpRequest& request)
546 -> std::unique_ptr<net::test_server::HttpResponse> {
547 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
548 response->AddCustomHeader("Set-Cookie", cookie_string);
549 return std::move(response);
550 }));
551
552 ASSERT_TRUE(embedded_test_server()->Start());
553
554 GURL http_url = embedded_test_server()->GetURL("/");
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00555
556 // Test scenarios where a control char may appear at start, middle and end of
557 // a cookie line. Control char array with NULL (\x0), CR (\xD), and LF (xA)
558 char kTestChars[] = {'\x0', '\xD', '\xA'};
559
560 for (const auto& test : kTestChars) {
561 std::string ctl_string(1, test);
562
563 // ctrl char at start of string
Daniel Cheng3a350ed2024-09-05 00:29:39564 cookie_string = base::StrCat({ctl_string, "foo=bar"});
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00565 EXPECT_TRUE(NavigateToURL(shell(), http_url));
566
567 // ctrl char at middle of string
Daniel Cheng3a350ed2024-09-05 00:29:39568 cookie_string = base::StrCat({"foo=bar;", ctl_string, "httponly"});
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00569 EXPECT_TRUE(NavigateToURL(shell(), http_url));
570
Daniel Cheng3a350ed2024-09-05 00:29:39571 cookie_string = base::StrCat({"foo=ba", ctl_string, "r; httponly"});
Andrew Williams748874612023-07-24 19:55:17572 EXPECT_TRUE(NavigateToURL(shell(), http_url));
573
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00574 // ctrl char at end of string
Daniel Cheng3a350ed2024-09-05 00:29:39575 cookie_string = base::StrCat({"foo=bar;", "httponly;", ctl_string});
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00576 EXPECT_TRUE(NavigateToURL(shell(), http_url));
577 }
578 // Test if there are multiple control characters that terminate.
Daniel Cheng3a350ed2024-09-05 00:29:39579 cookie_string = "foo=bar;\xA\xDhttponly";
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00580 EXPECT_TRUE(NavigateToURL(shell(), http_url));
Aaron Tagliaboschi6ed3c0692022-02-27 18:54:00581}
582
Maks Orlovich710d5e32019-07-09 20:16:45583class RestrictedCookieManagerInterceptor
584 : public network::mojom::RestrictedCookieManagerInterceptorForTesting {
585 public:
586 RestrictedCookieManagerInterceptor(
Julie Jeongeun Kimd20f64b2019-08-26 04:13:03587 mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver,
Julie Jeongeun Kimf42b07c2019-08-27 15:38:36588 mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm)
Julie Jeongeun Kimd20f64b2019-08-26 04:13:03589 : receiver_(this, std::move(receiver)), real_rcm_(std::move(real_rcm)) {}
Maks Orlovich710d5e32019-07-09 20:16:45590
Arthur Sonzognic686e8f2024-01-11 08:36:37591 void set_override_url(std::optional<std::string> maybe_url) {
Maks Orlovich710d5e32019-07-09 20:16:45592 override_url_ = std::move(maybe_url);
593 }
594
Chris Fredrickson9ffdf5b2024-07-09 20:05:09595 void SetCookieFromString(
596 const GURL& url,
597 const net::SiteForCookies& site_for_cookies,
598 const url::Origin& top_frame_origin,
599 net::StorageAccessApiStatus storage_access_api_status,
Aldo Culquicondorcfb369e2025-03-18 19:19:04600 bool get_version_shared_memory,
Aldo Culquicondor262e9992025-03-24 16:02:24601 bool is_ad_tagged,
Joshua Thomasd362203f2025-01-21 18:15:57602 bool apply_devtools_overrides,
Chris Fredrickson9ffdf5b2024-07-09 20:05:09603 const std::string& cookie,
604 SetCookieFromStringCallback callback) override {
Maks Orlovich710d5e32019-07-09 20:16:45605 GetForwardingInterface()->SetCookieFromString(
Chris Fredrickson9ffdf5b2024-07-09 20:05:09606 URLToUse(url), site_for_cookies, top_frame_origin,
Aldo Culquicondor262e9992025-03-24 16:02:24607 storage_access_api_status, get_version_shared_memory, is_ad_tagged,
Aldo Culquicondorcfb369e2025-03-18 19:19:04608 apply_devtools_overrides, std::move(cookie), std::move(callback));
Maks Orlovich710d5e32019-07-09 20:16:45609 }
610
611 void GetCookiesString(const GURL& url,
Maks Orlovichab27e242020-01-07 18:10:39612 const net::SiteForCookies& site_for_cookies,
Christian Dullweber10d62c12019-08-19 12:08:19613 const url::Origin& top_frame_origin,
Chris Fredrickson9ffdf5b2024-07-09 20:05:09614 net::StorageAccessApiStatus storage_access_api_status,
Olivier Lic0c141022023-07-05 16:25:51615 bool get_version_shared_memory,
John Delaney637f6ea2023-11-06 17:14:27616 bool is_ad_tagged,
Rupert Ben Wiser5fbe73e2024-02-28 19:15:31617 bool force_disable_third_party_cookies,
Joshua Thomasd362203f2025-01-21 18:15:57618 bool apply_devtools_overrides,
Maks Orlovich710d5e32019-07-09 20:16:45619 GetCookiesStringCallback callback) override {
620 GetForwardingInterface()->GetCookiesString(
Chris Fredrickson9ffdf5b2024-07-09 20:05:09621 URLToUse(url), site_for_cookies, top_frame_origin,
622 storage_access_api_status, get_version_shared_memory, is_ad_tagged,
Joshua Thomasd362203f2025-01-21 18:15:57623 force_disable_third_party_cookies, apply_devtools_overrides,
624 std::move(callback));
Maks Orlovich710d5e32019-07-09 20:16:45625 }
626
627 private:
628 network::mojom::RestrictedCookieManager* GetForwardingInterface() override {
629 return real_rcm_.get();
630 }
631
632 GURL URLToUse(const GURL& url_in) {
633 return override_url_ ? GURL(override_url_.value()) : url_in;
634 }
635
Arthur Sonzognic686e8f2024-01-11 08:36:37636 std::optional<std::string> override_url_;
Maks Orlovich710d5e32019-07-09 20:16:45637
Julie Jeongeun Kimd20f64b2019-08-26 04:13:03638 mojo::Receiver<network::mojom::RestrictedCookieManager> receiver_;
Julie Jeongeun Kimf42b07c2019-08-27 15:38:36639 mojo::Remote<network::mojom::RestrictedCookieManager> real_rcm_;
Maks Orlovich710d5e32019-07-09 20:16:45640};
641
Scott Violet99861992023-02-08 01:20:12642class CookieStoreContentBrowserClient
643 : public ContentBrowserTestContentBrowserClient {
Maks Orlovich710d5e32019-07-09 20:16:45644 public:
Chris Fredrickson5d0cb4f2022-09-27 16:48:42645 ~CookieStoreContentBrowserClient() override = default;
Maks Orlovich710d5e32019-07-09 20:16:45646
647 bool WillCreateRestrictedCookieManager(
Maks Orloviche7db7a22019-07-25 01:47:46648 network::mojom::RestrictedCookieManagerRole role,
Maks Orlovich710d5e32019-07-09 20:16:45649 content::BrowserContext* browser_context,
Shuran Huangbd4d1692021-01-26 17:03:41650 const url::Origin& origin,
Shuran Huang92b415d72021-01-12 20:48:15651 const net::IsolationInfo& isolation_info,
Maks Orlovich710d5e32019-07-09 20:16:45652 bool is_service_worker,
653 int process_id,
654 int routing_id,
Julie Jeongeun Kimd20f64b2019-08-26 04:13:03655 mojo::PendingReceiver<network::mojom::RestrictedCookieManager>* receiver)
656 override {
657 mojo::PendingReceiver<network::mojom::RestrictedCookieManager>
658 orig_receiver = std::move(*receiver);
Maks Orlovich710d5e32019-07-09 20:16:45659
Julie Jeongeun Kimf42b07c2019-08-27 15:38:36660 mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm;
661 *receiver = real_rcm.InitWithNewPipeAndPassReceiver();
Maks Orlovich710d5e32019-07-09 20:16:45662
663 rcm_interceptor_ = std::make_unique<RestrictedCookieManagerInterceptor>(
Julie Jeongeun Kimd20f64b2019-08-26 04:13:03664 std::move(orig_receiver), std::move(real_rcm));
Maks Orlovich710d5e32019-07-09 20:16:45665 rcm_interceptor_->set_override_url(override_url_);
666
667 return false; // only made a proxy, still need the actual impl to be made.
668 }
669
Arthur Sonzognic686e8f2024-01-11 08:36:37670 void set_override_url(std::optional<std::string> maybe_url) {
Maks Orlovich710d5e32019-07-09 20:16:45671 override_url_ = maybe_url;
672 if (rcm_interceptor_)
673 rcm_interceptor_->set_override_url(override_url_);
674 }
675
676 private:
Arthur Sonzognic686e8f2024-01-11 08:36:37677 std::optional<std::string> override_url_;
Maks Orlovich710d5e32019-07-09 20:16:45678 std::unique_ptr<RestrictedCookieManagerInterceptor> rcm_interceptor_;
679};
680
681// Cookie access in loader is locked to a particular origin, so messages
682// for wrong URLs are rejected.
Alison Gale923a33e2024-04-22 23:34:28683// TODO(crbug.com/41453892): This should actually result in renderer
Maks Orlovich710d5e32019-07-09 20:16:45684// kills.
Aldo Culquicondorcfb369e2025-03-18 19:19:04685IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CrossSiteCookieSecurityEnforcement) {
nick5f3d76072015-06-09 00:24:00686 // The code under test is only active under site isolation.
nickd30fd962015-07-27 21:51:08687 if (!AreAllSitesIsolatedForTesting()) {
nick5f3d76072015-06-09 00:24:00688 return;
689 }
690
nick5f3d76072015-06-09 00:24:00691 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:27692 ASSERT_TRUE(embedded_test_server()->Start());
Alex Moshchukaa95adf52019-08-13 00:02:02693 EXPECT_TRUE(NavigateToURL(
694 shell(), embedded_test_server()->GetURL("/frame_with_load_event.html")));
nick5f3d76072015-06-09 00:24:00695
696 WebContentsImpl* tab = static_cast<WebContentsImpl*>(shell()->web_contents());
697
698 // The iframe on the http page should get its own process.
699 FrameTreeVisualizer v;
700 EXPECT_EQ(
701 " Site A ------------ proxies for B\n"
702 " +--Site B ------- proxies for A\n"
703 "Where A = http://127.0.0.1/\n"
704 " B = http://baz.com/",
Carlos Caballero15caeeb2021-10-27 09:57:55705 v.DepictFrameTree(tab->GetPrimaryFrameTree().root()));
nick5f3d76072015-06-09 00:24:00706
Dave Tapuska327c06c92022-06-13 20:31:51707 RenderFrameHost* main_frame = tab->GetPrimaryMainFrame();
nick5f3d76072015-06-09 00:24:00708 RenderFrameHost* iframe =
Carlos Caballero15caeeb2021-10-27 09:57:55709 tab->GetPrimaryFrameTree().root()->child_at(0)->current_frame_host();
nick5f3d76072015-06-09 00:24:00710
711 EXPECT_NE(iframe->GetProcess(), main_frame->GetProcess());
712
Maks Orlovich710d5e32019-07-09 20:16:45713 SetCookieDirect(tab, GURL("http://127.0.0.1/"), "A_cookie = parent");
714 SetCookieDirect(tab, GURL("http://baz.com/"), "B_cookie = child");
715 EXPECT_EQ("A_cookie=parent",
716 GetCookiesDirect(tab, GURL("http://127.0.0.1/")));
717
718 EXPECT_EQ("B_cookie=child", GetCookiesDirect(tab, GURL("http://baz.com/")));
719
720 // Try to get cross-site cookies from the subframe's process.
Lukasz Anforowicz6f746282018-01-04 23:24:51721 {
Maks Orlovich710d5e32019-07-09 20:16:45722 CookieStoreContentBrowserClient browser_client;
Maks Orlovich710d5e32019-07-09 20:16:45723 browser_client.set_override_url("http://127.0.0.1/");
724 EXPECT_EQ("", GetCookieFromJS(iframe));
Lukasz Anforowicz6f746282018-01-04 23:24:51725 }
nick5f3d76072015-06-09 00:24:00726
727 EXPECT_EQ(
728 " Site A ------------ proxies for B\n"
729 " +--Site B ------- proxies for A\n"
730 "Where A = http://127.0.0.1/\n"
Maks Orlovich710d5e32019-07-09 20:16:45731 " B = http://baz.com/",
Carlos Caballero15caeeb2021-10-27 09:57:55732 v.DepictFrameTree(tab->GetPrimaryFrameTree().root()));
nick5f3d76072015-06-09 00:24:00733
Maks Orlovich710d5e32019-07-09 20:16:45734 // Now set a cross-site cookie from the main frame's process.
Lukasz Anforowicz6f746282018-01-04 23:24:51735 {
Maks Orlovich710d5e32019-07-09 20:16:45736 CookieStoreContentBrowserClient browser_client;
rockot1587e332016-07-27 17:44:14737
Maks Orlovich710d5e32019-07-09 20:16:45738 browser_client.set_override_url("https://baz.com/");
739 SetCookieFromJS(iframe, "pwn=ed");
nick5f3d76072015-06-09 00:24:00740
Maks Orlovich710d5e32019-07-09 20:16:45741 EXPECT_EQ("B_cookie=child", GetCookiesDirect(tab, GURL("http://baz.com/")));
Lukasz Anforowicz6f746282018-01-04 23:24:51742 }
nick5f3d76072015-06-09 00:24:00743
744 EXPECT_EQ(
Maks Orlovich710d5e32019-07-09 20:16:45745 " Site A ------------ proxies for B\n"
746 " +--Site B ------- proxies for A\n"
747 "Where A = http://127.0.0.1/\n"
748 " B = http://baz.com/",
Carlos Caballero15caeeb2021-10-27 09:57:55749 v.DepictFrameTree(tab->GetPrimaryFrameTree().root()));
nick5f3d76072015-06-09 00:24:00750}
751
Aldo Culquicondorcfb369e2025-03-18 19:19:04752IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieNotReadableAfterExpiry) {
Olivier Li140ade52025-02-06 20:36:07753 ASSERT_TRUE(embedded_test_server()->Start());
754
755 GURL http_url = embedded_test_server()->GetURL("example.test", "/empty.html");
756 EXPECT_TRUE(NavigateToURL(shell(), http_url));
757
758 WebContentsImpl* web_contents_http =
759 static_cast<WebContentsImpl*>(shell()->web_contents());
760 RenderFrameHost* frame = web_contents_http->GetPrimaryMainFrame();
761
762 SetCookieFromJS(frame, "c=1;Max-Age=1");
763 SetCookieFromJS(frame, "d=1;Max-Age=7200");
764 EXPECT_EQ("c=1; d=1", GetCookieFromJS(frame));
765
766 // If cookies properly expire and become unavailable this test will terminate.
767 // If they do not the test will time out. The earliest expiry from the cookies
768 // is used so the short expiry from c is expected to be used.
769 std::string cookie;
770 do {
771 cookie = GetCookieFromJS(frame);
772 base::PlatformThread::Sleep(base::Milliseconds(100));
773 } while (cookie != "d=1");
774}
775
Dustin J. Mitchell1ef929a2023-03-20 15:11:36776// Cookies for an eTLD should be stored (via JS) if they match the URL host,
777// even if they begin with `.` or have non-canonical capitalization.
Aldo Culquicondorcfb369e2025-03-18 19:19:04778IN_PROC_BROWSER_TEST_P(CookieBrowserTest, ETldDomainCookies) {
Dustin J. Mitchell1ef929a2023-03-20 15:11:36779 ASSERT_TRUE(embedded_test_server()->Start());
780
781 // This test uses `gov.br` as an example of an eTLD.
782 GURL http_url = embedded_test_server()->GetURL("gov.br", "/empty.html");
783 EXPECT_TRUE(NavigateToURL(shell(), http_url));
784
785 WebContentsImpl* web_contents_http =
786 static_cast<WebContentsImpl*>(shell()->web_contents());
787 RenderFrameHost* frame = web_contents_http->GetPrimaryMainFrame();
788
789 const char* kCases[] = {
790 // A host cookie.
791 "c=1",
792 // A cookie for this domain.
793 "c=1; domain=gov.br",
794 // Same, but with a preceding dot. This dot should be ignored.
795 "c=1; domain=.gov.br",
796 // Same, but with non-canonical case. This should be canonicalized.
797 "c=1; domain=gOv.bR",
798 };
799
800 for (const char* set_cookie : kCases) {
801 SCOPED_TRACE(set_cookie);
802 SetCookieFromJS(frame, set_cookie);
803 EXPECT_EQ("c=1", GetCookieFromJS(frame));
804 SetCookieFromJS(frame, "c=;expires=Thu, 01 Jan 1970 00:00:00 GMT");
805 EXPECT_EQ("", GetCookieFromJS(frame));
806 }
807}
808
809// Cookies for an eTLD should be stored (via header) if they match the URL host,
810// even if they begin with `.` or have non-canonical capitalization.
Aldo Culquicondorcfb369e2025-03-18 19:19:04811IN_PROC_BROWSER_TEST_P(CookieBrowserTest, ETldDomainCookiesHeader) {
Dustin J. Mitchell1ef929a2023-03-20 15:11:36812 std::string got_cookie_on_request;
813 std::string set_cookie_on_response;
814 embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
815 [&](const net::test_server::HttpRequest& request)
816 -> std::unique_ptr<net::test_server::HttpResponse> {
817 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
818 if (request.headers.contains("Cookie")) {
819 got_cookie_on_request = request.headers.at("Cookie");
820 } else {
821 got_cookie_on_request = "";
822 }
823 if (set_cookie_on_response.size() != 0) {
824 response->AddCustomHeader("Set-Cookie", set_cookie_on_response);
825 set_cookie_on_response = "";
826 }
827 return std::move(response);
828 }));
829
830 ASSERT_TRUE(embedded_test_server()->Start());
831
832 // This test uses `gov.br` as an example of an eTLD.
833 GURL http_url = embedded_test_server()->GetURL("gov.br", "/empty.html");
834
835 const char* kCases[] = {
836 // A host cookie.
837 "c=1",
838 // A cookie for this domain.
839 "c=1; domain=gov.br",
840 // Same, but with a preceding dot. This dot should be ignored.
841 "c=1; domain=.gov.br",
842 // Same, but with non-canonical case. This should be canonicalized.
843 "c=1; domain=gOv.bR",
844 };
845
846 for (const char* set_cookie : kCases) {
847 SCOPED_TRACE(set_cookie);
848
849 set_cookie_on_response = set_cookie;
850 EXPECT_TRUE(NavigateToURL(shell(), http_url));
851
852 EXPECT_TRUE(NavigateToURL(shell(), http_url));
853 EXPECT_EQ("c=1", got_cookie_on_request);
854
855 set_cookie_on_response = "c=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
856 EXPECT_TRUE(NavigateToURL(shell(), http_url));
857
858 EXPECT_TRUE(NavigateToURL(shell(), http_url));
859 EXPECT_EQ("", got_cookie_on_request);
860 }
861}
862
Ari Chivukula0be980cd2025-01-30 03:32:41863enum class CookieFileMode { kDefault, kEnabled, kDisabled };
864
865class CookieFileBrowserTest
866 : public ContentBrowserTest,
867 public ::testing::WithParamInterface<CookieFileMode> {
868 protected:
869 void SetUpOnMainThread() override {
870 // Setup file url.
871 base::ScopedAllowBlockingForTesting allow_blocking;
872 EXPECT_TRUE(file_directory_.CreateUniqueTempDir());
873 base::FilePath file_path =
874 file_directory_.GetPath().AppendASCII("index.html");
875 EXPECT_TRUE(base::WriteFile(file_path, ""));
876 file_url_ = net::FilePathToFileURL(file_path);
877
878 // Setup cookie manager.
879 bool file_cookie_enabled;
880 switch (GetParam()) {
881 case CookieFileMode::kDefault:
882 // Nothing to do.
883 return;
884 case CookieFileMode::kEnabled:
885 file_cookie_enabled = true;
886 break;
887 case CookieFileMode::kDisabled:
888 file_cookie_enabled = false;
889 break;
890 }
891 base::RunLoop run_loop;
892 shell()
893 ->web_contents()
894 ->GetBrowserContext()
895 ->GetDefaultStoragePartition()
896 ->GetCookieManagerForBrowserProcess()
897 ->AllowFileSchemeCookies(file_cookie_enabled,
898 base::BindLambdaForTesting([&](bool success) {
899 EXPECT_TRUE(success);
900 run_loop.Quit();
901 }));
902 run_loop.Run();
903 }
904
905 GURL file_url_;
906
907 private:
908 base::ScopedTempDir file_directory_;
909};
910
911INSTANTIATE_TEST_SUITE_P(,
912 CookieFileBrowserTest,
913 ::testing::Values(CookieFileMode::kDefault,
914 CookieFileMode::kEnabled,
915 CookieFileMode::kDisabled));
916
917// Try to set and get cookies on a file URL.
918IN_PROC_BROWSER_TEST_P(CookieFileBrowserTest, SetAndGetCookie) {
919 // Navigate to file.
920 EXPECT_TRUE(NavigateToURL(shell(), file_url_));
921 RenderFrameHost* frame = shell()->web_contents()->GetPrimaryMainFrame();
922
Ari Chivukulaba509b32025-05-23 14:11:50923 // File cookies always appear to be writable. On non-Android platforms a
924 // warning is printed when this occurs.
925#if !BUILDFLAG(IS_ANDROID)
926 WebContentsConsoleObserver console_observer(shell()->web_contents());
927 console_observer.SetPattern(
928 "While navigator.cookieEnabled does return true for this file:// "
929 "URL, this is done for web compatability reasons. Cookies will not "
930 "actually be stored for file:// URLs. If you want this to change "
931 "please leave feedback on crbug.com/378604901.");
932#endif
Ari Chivukula0be980cd2025-01-30 03:32:41933 EXPECT_TRUE(EvalJs(frame, "navigator.cookieEnabled").ExtractBool());
Ari Chivukulaba509b32025-05-23 14:11:50934#if !BUILDFLAG(IS_ANDROID)
935 ASSERT_TRUE(console_observer.Wait());
936#endif
Ari Chivukula0be980cd2025-01-30 03:32:41937
938 // File cookies can only be set if they are enabled.
939 bool can_set_cookies;
940 switch (GetParam()) {
941 case CookieFileMode::kDefault:
942 // TODO(crbug.com/378604901): Perhapse this should be allowed by default.
943 can_set_cookies = false;
944 return;
945 case CookieFileMode::kEnabled:
946 can_set_cookies = true;
947 break;
948 case CookieFileMode::kDisabled:
949 can_set_cookies = false;
950 break;
951 }
952 SetCookieFromJS(frame, "test=1");
953 EXPECT_EQ(can_set_cookies ? "test=1" : "", GetCookieFromJS(frame));
954}
955
nick5f3d76072015-06-09 00:24:00956} // namespace content