Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 5 | #include <optional> |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 6 | #include <string> |
| 7 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 8 | #include "base/command_line.h" |
| 9 | #include "base/files/file_path.h" |
Ari Chivukula | 0be980cd | 2025-01-30 03:32:41 | [diff] [blame] | 10 | #include "base/files/file_util.h" |
Avi Drissman | adac2199 | 2023-01-11 23:46:39 | [diff] [blame] | 11 | #include "base/functional/bind.h" |
| 12 | #include "base/functional/callback_helpers.h" |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 13 | #include "base/strings/strcat.h" |
Guido Urdaneta | ef4e9194 | 2020-11-09 15:06:24 | [diff] [blame] | 14 | #include "base/test/bind.h" |
Chris Fredrickson | b4cf840 | 2022-09-23 22:59:59 | [diff] [blame] | 15 | #include "base/test/scoped_feature_list.h" |
Olivier Li | 140ade5 | 2025-02-06 20:36:07 | [diff] [blame] | 16 | #include "base/threading/platform_thread.h" |
| 17 | #include "base/time/time.h" |
nick | 462f928 | 2015-12-04 18:47:45 | [diff] [blame] | 18 | #include "content/browser/bad_message.h" |
danakj | c492bf8 | 2020-09-09 20:02:44 | [diff] [blame] | 19 | #include "content/browser/renderer_host/frame_tree.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 20 | #include "content/browser/web_contents/web_contents_impl.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 21 | #include "content/public/browser/browser_context.h" |
Eric Seckler | 8652dcd5 | 2018-09-20 10:42:28 | [diff] [blame] | 22 | #include "content/public/browser/browser_task_traits.h" |
Gabriel Charette | 790754c | 2018-03-16 21:32:59 | [diff] [blame] | 23 | #include "content/public/browser/browser_thread.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 24 | #include "content/public/browser/render_process_host.h" |
W. James MacLean | 693869ee | 2024-06-25 17:23:58 | [diff] [blame] | 25 | #include "content/public/browser/site_isolation_policy.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 26 | #include "content/public/browser/storage_partition.h" |
Hans Wennborg | 5ffd139 | 2019-10-16 11:00:02 | [diff] [blame] | 27 | #include "content/public/common/content_client.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 28 | #include "content/public/common/content_switches.h" |
Peter Kasting | 919ce65 | 2020-05-07 10:22:36 | [diff] [blame] | 29 | #include "content/public/test/browser_test.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 30 | #include "content/public/test/browser_test_utils.h" |
| 31 | #include "content/public/test/content_browser_test.h" |
Scott Violet | 9986199 | 2023-02-08 01:20:12 | [diff] [blame] | 32 | #include "content/public/test/content_browser_test_content_browser_client.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 33 | #include "content/public/test/content_browser_test_utils.h" |
Joshua Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 34 | #include "content/public/test/test_devtools_protocol_client.h" |
carlosk | 15fb0f0 | 2015-07-29 17:07:51 | [diff] [blame] | 35 | #include "content/public/test/test_utils.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 36 | #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 Fredrickson | b4cf840 | 2022-09-23 22:59:59 | [diff] [blame] | 39 | #include "net/base/features.h" |
Ari Chivukula | 0be980cd | 2025-01-30 03:32:41 | [diff] [blame] | 40 | #include "net/base/filename_util.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 41 | #include "net/cookies/canonical_cookie.h" |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 42 | #include "net/cookies/canonical_cookie_test_helpers.h" |
Ayu Ishii | f3966ca | 2020-07-08 17:35:12 | [diff] [blame] | 43 | #include "net/cookies/cookie_access_result.h" |
Lily Chen | f068a76 | 2019-08-21 21:10:50 | [diff] [blame] | 44 | #include "net/cookies/cookie_util.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 45 | #include "net/dns/mock_host_resolver.h" |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 46 | #include "net/http/alternative_service.h" |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 47 | #include "net/storage_access_api/status.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 48 | #include "net/test/embedded_test_server/embedded_test_server.h" |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 49 | #include "services/network/public/cpp/features.h" |
Chris Fredrickson | f9de345 | 2022-09-12 21:46:56 | [diff] [blame] | 50 | #include "services/network/public/cpp/network_switches.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 51 | #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" |
nick | 462f928 | 2015-12-04 18:47:45 | [diff] [blame] | 54 | #include "testing/gmock/include/gmock/gmock.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 55 | #include "testing/gtest/include/gtest/gtest.h" |
Aldo Culquicondor | 5bf8bc23 | 2025-03-20 14:51:39 | [diff] [blame] | 56 | #include "third_party/blink/public/common/features_generated.h" |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 57 | #include "url/gurl.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 58 | |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 59 | using testing::IsEmpty; |
| 60 | using testing::Key; |
| 61 | using testing::UnorderedElementsAre; |
| 62 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 63 | namespace content { |
| 64 | |
| 65 | namespace { |
| 66 | |
Joshua Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 67 | void 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 Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 78 | void SetCookieFromJS(RenderFrameHost* frame, std::string cookie) { |
| 79 | EvalJsResult result = EvalJs(frame, "document.cookie = '" + cookie + "'"); |
| 80 | EXPECT_TRUE(result.error.empty()) << result.error; |
| 81 | } |
| 82 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 83 | std::string GetCookieFromJS(RenderFrameHost* frame) { |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 84 | return EvalJs(frame, "document.cookie;").ExtractString(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 85 | } |
| 86 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 87 | void SetCookieDirect(WebContentsImpl* tab, |
| 88 | const GURL& url, |
| 89 | const std::string& cookie_line) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 90 | net::CookieOptions options; |
| 91 | // Allow setting SameSite cookies. |
| 92 | options.set_same_site_cookie_context( |
Steven Bingler | 8d76c2a4 | 2020-03-24 17:13:32 | [diff] [blame] | 93 | net::CookieOptions::SameSiteCookieContext::MakeInclusive()); |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 94 | |
Ari Chivukula | 92851c3 | 2024-04-09 12:37:44 | [diff] [blame] | 95 | auto cookie_obj = net::CanonicalCookie::CreateForTesting(url, cookie_line, |
| 96 | base::Time::Now()); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 97 | |
| 98 | base::RunLoop run_loop; |
Lukasz Anforowicz | b9a969a | 2021-04-29 15:26:25 | [diff] [blame] | 99 | tab->GetBrowserContext() |
| 100 | ->GetDefaultStoragePartition() |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 101 | ->GetCookieManagerForBrowserProcess() |
| 102 | ->SetCanonicalCookie( |
Lily Chen | 96f29a13 | 2020-04-15 17:59:36 | [diff] [blame] | 103 | *cookie_obj, url, options, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 104 | base::BindLambdaForTesting( |
Ayu Ishii | f3966ca | 2020-07-08 17:35:12 | [diff] [blame] | 105 | [&](net::CookieAccessResult status) { run_loop.Quit(); })); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 106 | run_loop.Run(); |
| 107 | } |
| 108 | |
| 109 | std::string GetCookiesDirect(WebContentsImpl* tab, const GURL& url) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 110 | net::CookieOptions options; |
| 111 | // Allow setting SameSite cookies. |
| 112 | options.set_same_site_cookie_context( |
Steven Bingler | 8d76c2a4 | 2020-03-24 17:13:32 | [diff] [blame] | 113 | net::CookieOptions::SameSiteCookieContext::MakeInclusive()); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 114 | net::CookieList result; |
| 115 | base::RunLoop run_loop; |
Lukasz Anforowicz | b9a969a | 2021-04-29 15:26:25 | [diff] [blame] | 116 | tab->GetBrowserContext() |
| 117 | ->GetDefaultStoragePartition() |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 118 | ->GetCookieManagerForBrowserProcess() |
Ayu Ishii | bc6fdb0a | 2020-06-08 22:59:19 | [diff] [blame] | 119 | ->GetCookieList( |
Aykut Bulut | 244341e | 2021-12-09 15:57:25 | [diff] [blame] | 120 | url, options, net::CookiePartitionKeyCollection(), |
Ayu Ishii | bc6fdb0a | 2020-06-08 22:59:19 | [diff] [blame] | 121 | 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 Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 127 | run_loop.Run(); |
| 128 | return net::CanonicalCookie::BuildCookieLine(result); |
rockot | 6a2d66a | 2016-08-04 00:15:25 | [diff] [blame] | 129 | } |
| 130 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 131 | } // namespace |
| 132 | |
Aldo Culquicondor | 5bf8bc23 | 2025-03-20 14:51:39 | [diff] [blame] | 133 | class CookieBrowserTest |
| 134 | : public ContentBrowserTest, |
| 135 | public ::testing::WithParamInterface<std::tuple<bool, bool>> { |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 136 | public: |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 137 | CookieBrowserTest() |
| 138 | : https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) { |
Aldo Culquicondor | 5bf8bc23 | 2025-03-20 14:51:39 | [diff] [blame] | 139 | scoped_feature_list_.InitWithFeatureStates( |
| 140 | {{network::features::kGetCookiesOnSet, GetCookiesOnSetEnabled()}, |
| 141 | {blink::features::kAsyncSetCookie, AsyncSetCookieEnabled()}}); |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 142 | } |
| 143 | ~CookieBrowserTest() override = default; |
| 144 | |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 145 | protected: |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 146 | void SetUpCommandLine(base::CommandLine* command_line) override { |
| 147 | command_line->AppendSwitch( |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 148 | switches::kEnableExperimentalWebPlatformFeatures); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 149 | } |
jam | 8f34ea7 | 2017-04-26 17:48:55 | [diff] [blame] | 150 | |
| 151 | void SetUpOnMainThread() override { |
| 152 | // Support multiple sites on the test server. |
| 153 | host_resolver()->AddRule("*", "127.0.0.1"); |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 154 | https_server_.AddDefaultHandlers(GetTestDataFilePath()); |
| 155 | https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
| 156 | SetupCrossSiteRedirector(&https_server_); |
| 157 | ASSERT_TRUE(https_server_.Start()); |
jam | 8f34ea7 | 2017-04-26 17:48:55 | [diff] [blame] | 158 | } |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 159 | |
Aldo Culquicondor | 5bf8bc23 | 2025-03-20 14:51:39 | [diff] [blame] | 160 | bool GetCookiesOnSetEnabled() { return std::get<0>(GetParam()); } |
| 161 | |
| 162 | bool AsyncSetCookieEnabled() { return std::get<1>(GetParam()); } |
| 163 | |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 164 | net::test_server::EmbeddedTestServer https_server_; |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 165 | base::test::ScopedFeatureList scoped_feature_list_; |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 166 | }; |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 167 | |
Aldo Culquicondor | 5bf8bc23 | 2025-03-20 14:51:39 | [diff] [blame] | 168 | INSTANTIATE_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 Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 179 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 180 | // Exercises basic cookie operations via javascript, including an http page |
| 181 | // interacting with secure cookies. |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 182 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, Cookies) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 183 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 184 | ASSERT_TRUE(embedded_test_server()->Start()); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 185 | |
Lily Chen | 15b3ff88 | 2021-05-21 01:27:41 | [diff] [blame] | 186 | // The server sends a HttpOnly cookie. The RestrictedCookieManager should |
nick | 462f928 | 2015-12-04 18:47:45 | [diff] [blame] | 187 | // never allow this to be sent to any renderer process. |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 188 | GURL https_url = |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 189 | https_server_.GetURL("a.test", "/set-cookie?notforjs=1;HttpOnly"); |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 190 | GURL http_url = |
| 191 | embedded_test_server()->GetURL("a.test", "/frame_with_load_event.html"); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 192 | |
| 193 | Shell* shell2 = CreateBrowser(); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 194 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 195 | EXPECT_TRUE(NavigateToURL(shell2, https_url)); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 196 | |
| 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 Moshchuk | 4bc5dfc | 2025-06-06 17:23:00 | [diff] [blame] | 201 | if (AreStrictSiteInstancesEnabled()) { |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 202 | EXPECT_EQ("http://a.test/", |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 203 | web_contents_http->GetSiteInstance()->GetSiteURL().spec()); |
W. James MacLean | 03da9224 | 2024-06-14 13:46:04 | [diff] [blame] | 204 | // Create expected site url, including port if origin isolation is enabled. |
| 205 | std::string expected_site_url = |
W. James MacLean | 693869ee | 2024-06-25 17:23:58 | [diff] [blame] | 206 | SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault() |
W. James MacLean | 03da9224 | 2024-06-14 13:46:04 | [diff] [blame] | 207 | ? url::Origin::Create(https_url).GetURL().spec() |
| 208 | : std::string("https://a.test/"); |
| 209 | EXPECT_EQ(expected_site_url, |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 210 | web_contents_https->GetSiteInstance()->GetSiteURL().spec()); |
Sharon Yang | 0227922 | 2025-01-15 19:09:19 | [diff] [blame] | 211 | } 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 Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 221 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 222 | |
| 223 | EXPECT_NE(web_contents_http->GetSiteInstance()->GetProcess(), |
| 224 | web_contents_https->GetSiteInstance()->GetProcess()); |
| 225 | |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 226 | EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 227 | EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 228 | |
| 229 | // Non-TLS page writes secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 230 | EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(), |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 231 | "document.cookie = 'A=1; secure;';")); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 232 | EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 233 | EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 234 | |
Joshua Hood | f54ef85 | 2022-02-23 17:33:58 | [diff] [blame] | 235 | // Non-TLS page writes not-secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 236 | EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(), |
| 237 | "document.cookie = 'B=2';")); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 238 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 239 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 240 | |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 241 | // TLS page writes secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 242 | EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(), |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 243 | "document.cookie = 'C=3;secure;';")); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 244 | EXPECT_EQ("B=2; C=3", |
| 245 | GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 246 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 247 | |
| 248 | // TLS page writes not-secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 249 | EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(), |
| 250 | "document.cookie = 'D=4';")); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 251 | EXPECT_EQ("B=2; C=3; D=4", |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 252 | GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 253 | EXPECT_EQ("B=2; D=4", |
| 254 | GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 255 | } |
| 256 | |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 257 | // Ensure "priority" cookie option is settable via document.cookie. |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 258 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookiePriority) { |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 259 | 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 Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 272 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 273 | 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 | |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 283 | // SameSite cookies (that aren't marked as http-only) should be available to |
| 284 | // JavaScript. |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 285 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, SameSiteCookies) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 286 | // Must use HTTPS because SameSite=None cookies must be Secure. |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 287 | |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 288 | // 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'. |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 290 | std::string cookies_to_set = |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 291 | "/set-cookie?none=1;SameSite=None;Secure" // SameSite=None must be |
| 292 | // Secure. |
| 293 | "&none-insecure=1;SameSite=None" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 294 | "&strict=1;SameSite=Strict" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 295 | "&unspecified=1" // unspecified SameSite should be treated as Lax. |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 296 | "&lax=1;SameSite=Lax" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 297 | "&none-http=1;SameSite=None;Secure;httponly" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 298 | "&strict-http=1;SameSite=Strict;httponly" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 299 | "&unspecified-http=1;httponly" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 300 | "&lax-http=1;SameSite=Lax;httponly"; |
| 301 | |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 302 | GURL url = https_server_.GetURL("a.test", cookies_to_set); |
Alex Moshchuk | aeb20fe3 | 2019-09-25 17:40:01 | [diff] [blame] | 303 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 304 | url = https_server_.GetURL("b.test", cookies_to_set); |
Alex Moshchuk | aeb20fe3 | 2019-09-25 17:40:01 | [diff] [blame] | 305 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 306 | url = https_server_.GetURL( |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 307 | "a.test", "/cross_site_iframe_factory.html?a.test(a.test(),b.test())"); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 308 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 309 | |
| 310 | WebContentsImpl* web_contents = |
| 311 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 312 | RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame(); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 313 | 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(); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 321 | |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 322 | // 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)); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 325 | |
| 326 | // Same-site cookies will be delievered to the 'a.com' frame, as it is same- |
| 327 | // site with its ancestors. |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 328 | EXPECT_EQ("none=1; strict=1; unspecified=1; lax=1", |
| 329 | GetCookieFromJS(a_iframe)); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 330 | |
| 331 | // Same-site cookies should not be delievered to the 'b.com' frame, as it |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 332 | // isn't same-site with its ancestors. The SameSite=None but insecure cookie |
| 333 | // is rejected. |
| 334 | EXPECT_EQ("none=1", GetCookieFromJS(b_iframe)); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 335 | } |
| 336 | |
Chris Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 337 | // Prefixed cookies (that aren't marked as http-only) should be available to |
| 338 | // JavaScript. |
| 339 | IN_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 | |
| 360 | IN_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. |
| 379 | IN_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 | |
| 404 | IN_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 Versari | 7ac4050 | 2025-05-14 18:48:06 | [diff] [blame] | 425 | // embedded_test_server() uses http, which is insecure, but localhost is |
| 426 | // allowed to set prefixed cookies anyway. |
| 427 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Write_Localhost) { |
Luca Versari | 7ac4050 | 2025-05-14 18:48:06 | [diff] [blame] | 428 | 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 Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 450 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, |
| 451 | CookieJarInvalidatesCacheWithNewDevtoolsControls) { |
| 452 | // Must use HTTPS because SameSite=None cookies must be Secure. |
Joshua Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 453 | |
| 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 Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 459 | GURL url = https_server_.GetURL("b.test", cookies_to_set); |
Joshua Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 460 | 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 Fredrickson | 34237ce | 2025-05-13 16:36:14 | [diff] [blame] | 470 | url = https_server_.GetURL( |
| 471 | "a.test", "/cross_site_iframe_factory.html?a.test(b.test())"); |
Joshua Thomas | 1400836 | 2025-03-26 14:06:47 | [diff] [blame] | 472 | 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 Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 506 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieTruncatingCharFromJavascript) { |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 507 | 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 Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 524 | std::string cookie_string = base::StrCat({ctl_string, "foo1=bar"}); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 525 | SetCookieFromJS(frame, cookie_string); |
| 526 | |
| 527 | // Control char in the middle of the string. |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 528 | cookie_string = base::StrCat({"foo2=bar;", ctl_string, "httponly"}); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 529 | SetCookieFromJS(frame, cookie_string); |
| 530 | |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 531 | cookie_string = base::StrCat({"foo3=ba", ctl_string, "r; httponly"}); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 532 | SetCookieFromJS(frame, cookie_string); |
| 533 | |
| 534 | // Control char at the end of the string. |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 535 | cookie_string = base::StrCat({"foo4=bar;", ctl_string}); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 536 | SetCookieFromJS(frame, cookie_string); |
| 537 | } |
| 538 | |
Andrew Williams | b19527f | 2024-05-17 14:05:14 | [diff] [blame] | 539 | EXPECT_EQ("", GetCookieFromJS(frame)); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 540 | } |
| 541 | |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 542 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieTruncatingCharFromHeaders) { |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 543 | 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 Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 555 | |
| 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 Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 564 | cookie_string = base::StrCat({ctl_string, "foo=bar"}); |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 565 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 566 | |
| 567 | // ctrl char at middle of string |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 568 | cookie_string = base::StrCat({"foo=bar;", ctl_string, "httponly"}); |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 569 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 570 | |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 571 | cookie_string = base::StrCat({"foo=ba", ctl_string, "r; httponly"}); |
Andrew Williams | 74887461 | 2023-07-24 19:55:17 | [diff] [blame] | 572 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 573 | |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 574 | // ctrl char at end of string |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 575 | cookie_string = base::StrCat({"foo=bar;", "httponly;", ctl_string}); |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 576 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 577 | } |
| 578 | // Test if there are multiple control characters that terminate. |
Daniel Cheng | 3a350ed | 2024-09-05 00:29:39 | [diff] [blame] | 579 | cookie_string = "foo=bar;\xA\xDhttponly"; |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 580 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 581 | } |
| 582 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 583 | class RestrictedCookieManagerInterceptor |
| 584 | : public network::mojom::RestrictedCookieManagerInterceptorForTesting { |
| 585 | public: |
| 586 | RestrictedCookieManagerInterceptor( |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 587 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver, |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 588 | mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm) |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 589 | : receiver_(this, std::move(receiver)), real_rcm_(std::move(real_rcm)) {} |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 590 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 591 | void set_override_url(std::optional<std::string> maybe_url) { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 592 | override_url_ = std::move(maybe_url); |
| 593 | } |
| 594 | |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 595 | 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 Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 600 | bool get_version_shared_memory, |
Aldo Culquicondor | 262e999 | 2025-03-24 16:02:24 | [diff] [blame] | 601 | bool is_ad_tagged, |
Joshua Thomas | d362203f | 2025-01-21 18:15:57 | [diff] [blame] | 602 | bool apply_devtools_overrides, |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 603 | const std::string& cookie, |
| 604 | SetCookieFromStringCallback callback) override { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 605 | GetForwardingInterface()->SetCookieFromString( |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 606 | URLToUse(url), site_for_cookies, top_frame_origin, |
Aldo Culquicondor | 262e999 | 2025-03-24 16:02:24 | [diff] [blame] | 607 | storage_access_api_status, get_version_shared_memory, is_ad_tagged, |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 608 | apply_devtools_overrides, std::move(cookie), std::move(callback)); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 609 | } |
| 610 | |
| 611 | void GetCookiesString(const GURL& url, |
Maks Orlovich | ab27e24 | 2020-01-07 18:10:39 | [diff] [blame] | 612 | const net::SiteForCookies& site_for_cookies, |
Christian Dullweber | 10d62c1 | 2019-08-19 12:08:19 | [diff] [blame] | 613 | const url::Origin& top_frame_origin, |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 614 | net::StorageAccessApiStatus storage_access_api_status, |
Olivier Li | c0c14102 | 2023-07-05 16:25:51 | [diff] [blame] | 615 | bool get_version_shared_memory, |
John Delaney | 637f6ea | 2023-11-06 17:14:27 | [diff] [blame] | 616 | bool is_ad_tagged, |
Rupert Ben Wiser | 5fbe73e | 2024-02-28 19:15:31 | [diff] [blame] | 617 | bool force_disable_third_party_cookies, |
Joshua Thomas | d362203f | 2025-01-21 18:15:57 | [diff] [blame] | 618 | bool apply_devtools_overrides, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 619 | GetCookiesStringCallback callback) override { |
| 620 | GetForwardingInterface()->GetCookiesString( |
Chris Fredrickson | 9ffdf5b | 2024-07-09 20:05:09 | [diff] [blame] | 621 | URLToUse(url), site_for_cookies, top_frame_origin, |
| 622 | storage_access_api_status, get_version_shared_memory, is_ad_tagged, |
Joshua Thomas | d362203f | 2025-01-21 18:15:57 | [diff] [blame] | 623 | force_disable_third_party_cookies, apply_devtools_overrides, |
| 624 | std::move(callback)); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 625 | } |
| 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 Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 636 | std::optional<std::string> override_url_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 637 | |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 638 | mojo::Receiver<network::mojom::RestrictedCookieManager> receiver_; |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 639 | mojo::Remote<network::mojom::RestrictedCookieManager> real_rcm_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 640 | }; |
| 641 | |
Scott Violet | 9986199 | 2023-02-08 01:20:12 | [diff] [blame] | 642 | class CookieStoreContentBrowserClient |
| 643 | : public ContentBrowserTestContentBrowserClient { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 644 | public: |
Chris Fredrickson | 5d0cb4f | 2022-09-27 16:48:42 | [diff] [blame] | 645 | ~CookieStoreContentBrowserClient() override = default; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 646 | |
| 647 | bool WillCreateRestrictedCookieManager( |
Maks Orlovich | e7db7a2 | 2019-07-25 01:47:46 | [diff] [blame] | 648 | network::mojom::RestrictedCookieManagerRole role, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 649 | content::BrowserContext* browser_context, |
Shuran Huang | bd4d169 | 2021-01-26 17:03:41 | [diff] [blame] | 650 | const url::Origin& origin, |
Shuran Huang | 92b415d7 | 2021-01-12 20:48:15 | [diff] [blame] | 651 | const net::IsolationInfo& isolation_info, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 652 | bool is_service_worker, |
| 653 | int process_id, |
| 654 | int routing_id, |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 655 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager>* receiver) |
| 656 | override { |
| 657 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager> |
| 658 | orig_receiver = std::move(*receiver); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 659 | |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 660 | mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm; |
| 661 | *receiver = real_rcm.InitWithNewPipeAndPassReceiver(); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 662 | |
| 663 | rcm_interceptor_ = std::make_unique<RestrictedCookieManagerInterceptor>( |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 664 | std::move(orig_receiver), std::move(real_rcm)); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 665 | 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 Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 670 | void set_override_url(std::optional<std::string> maybe_url) { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 671 | override_url_ = maybe_url; |
| 672 | if (rcm_interceptor_) |
| 673 | rcm_interceptor_->set_override_url(override_url_); |
| 674 | } |
| 675 | |
| 676 | private: |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 677 | std::optional<std::string> override_url_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 678 | 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 Gale | 923a33e | 2024-04-22 23:34:28 | [diff] [blame] | 683 | // TODO(crbug.com/41453892): This should actually result in renderer |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 684 | // kills. |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 685 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CrossSiteCookieSecurityEnforcement) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 686 | // The code under test is only active under site isolation. |
nick | d30fd96 | 2015-07-27 21:51:08 | [diff] [blame] | 687 | if (!AreAllSitesIsolatedForTesting()) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 688 | return; |
| 689 | } |
| 690 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 691 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 692 | ASSERT_TRUE(embedded_test_server()->Start()); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 693 | EXPECT_TRUE(NavigateToURL( |
| 694 | shell(), embedded_test_server()->GetURL("/frame_with_load_event.html"))); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 695 | |
| 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 705 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 706 | |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 707 | RenderFrameHost* main_frame = tab->GetPrimaryMainFrame(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 708 | RenderFrameHost* iframe = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 709 | tab->GetPrimaryFrameTree().root()->child_at(0)->current_frame_host(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 710 | |
| 711 | EXPECT_NE(iframe->GetProcess(), main_frame->GetProcess()); |
| 712 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 713 | 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 Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 721 | { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 722 | CookieStoreContentBrowserClient browser_client; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 723 | browser_client.set_override_url("http://127.0.0.1/"); |
| 724 | EXPECT_EQ("", GetCookieFromJS(iframe)); |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 725 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 726 | |
| 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 Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 731 | " B = http://baz.com/", |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 732 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 733 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 734 | // Now set a cross-site cookie from the main frame's process. |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 735 | { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 736 | CookieStoreContentBrowserClient browser_client; |
rockot | 1587e33 | 2016-07-27 17:44:14 | [diff] [blame] | 737 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 738 | browser_client.set_override_url("https://baz.com/"); |
| 739 | SetCookieFromJS(iframe, "pwn=ed"); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 740 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 741 | EXPECT_EQ("B_cookie=child", GetCookiesDirect(tab, GURL("http://baz.com/"))); |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 742 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 743 | |
| 744 | EXPECT_EQ( |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 745 | " 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 749 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 750 | } |
| 751 | |
Aldo Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 752 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, CookieNotReadableAfterExpiry) { |
Olivier Li | 140ade5 | 2025-02-06 20:36:07 | [diff] [blame] | 753 | 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. Mitchell | 1ef929a | 2023-03-20 15:11:36 | [diff] [blame] | 776 | // 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 Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 778 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, ETldDomainCookies) { |
Dustin J. Mitchell | 1ef929a | 2023-03-20 15:11:36 | [diff] [blame] | 779 | 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 Culquicondor | cfb369e | 2025-03-18 19:19:04 | [diff] [blame] | 811 | IN_PROC_BROWSER_TEST_P(CookieBrowserTest, ETldDomainCookiesHeader) { |
Dustin J. Mitchell | 1ef929a | 2023-03-20 15:11:36 | [diff] [blame] | 812 | 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 Chivukula | 0be980cd | 2025-01-30 03:32:41 | [diff] [blame] | 863 | enum class CookieFileMode { kDefault, kEnabled, kDisabled }; |
| 864 | |
| 865 | class 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 | |
| 911 | INSTANTIATE_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. |
| 918 | IN_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 Chivukula | ba509b3 | 2025-05-23 14:11:50 | [diff] [blame] | 923 | // 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 Chivukula | 0be980cd | 2025-01-30 03:32:41 | [diff] [blame] | 933 | EXPECT_TRUE(EvalJs(frame, "navigator.cookieEnabled").ExtractBool()); |
Ari Chivukula | ba509b3 | 2025-05-23 14:11:50 | [diff] [blame] | 934 | #if !BUILDFLAG(IS_ANDROID) |
| 935 | ASSERT_TRUE(console_observer.Wait()); |
| 936 | #endif |
Ari Chivukula | 0be980cd | 2025-01-30 03:32:41 | [diff] [blame] | 937 | |
| 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 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 956 | } // namespace content |