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 | |
| 5 | #include <string> |
| 6 | |
John Abd-El-Malek | b5f10a9 | 2017-11-01 14:50:33 | [diff] [blame] | 7 | #include "base/bind.h" |
danakj | db9ae794 | 2020-11-11 16:01:35 | [diff] [blame] | 8 | #include "base/callback_helpers.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 9 | #include "base/command_line.h" |
| 10 | #include "base/files/file_path.h" |
Guido Urdaneta | ef4e9194 | 2020-11-09 15:06:24 | [diff] [blame] | 11 | #include "base/test/bind.h" |
Devlin Cronin | 513398f | 2018-06-05 15:33:49 | [diff] [blame] | 12 | #include "base/test/metrics/histogram_tester.h" |
Chris Fredrickson | b4cf840 | 2022-09-23 22:59:59 | [diff] [blame] | 13 | #include "base/test/scoped_feature_list.h" |
nick | 462f9283 | 2015-12-04 18:47:45 | [diff] [blame] | 14 | #include "content/browser/bad_message.h" |
danakj | c492bf8 | 2020-09-09 20:02:44 | [diff] [blame] | 15 | #include "content/browser/renderer_host/frame_tree.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 16 | #include "content/browser/web_contents/web_contents_impl.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 17 | #include "content/public/browser/browser_context.h" |
Eric Seckler | 8652dcd5 | 2018-09-20 10:42:28 | [diff] [blame] | 18 | #include "content/public/browser/browser_task_traits.h" |
Gabriel Charette | 790754c | 2018-03-16 21:32:59 | [diff] [blame] | 19 | #include "content/public/browser/browser_thread.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 20 | #include "content/public/browser/render_process_host.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 21 | #include "content/public/browser/storage_partition.h" |
Hans Wennborg | 5ffd139 | 2019-10-16 11:00:02 | [diff] [blame] | 22 | #include "content/public/common/content_client.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 23 | #include "content/public/common/content_switches.h" |
Peter Kasting | 919ce65 | 2020-05-07 10:22:36 | [diff] [blame] | 24 | #include "content/public/test/browser_test.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 25 | #include "content/public/test/browser_test_utils.h" |
| 26 | #include "content/public/test/content_browser_test.h" |
| 27 | #include "content/public/test/content_browser_test_utils.h" |
carlosk | 15fb0f0 | 2015-07-29 17:07:51 | [diff] [blame] | 28 | #include "content/public/test/test_utils.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 29 | #include "content/shell/browser/shell.h" |
| 30 | #include "content/test/content_browser_test_utils_internal.h" |
| 31 | #include "ipc/ipc_security_test_util.h" |
Chris Fredrickson | b4cf840 | 2022-09-23 22:59:59 | [diff] [blame] | 32 | #include "net/base/features.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 33 | #include "net/cookies/canonical_cookie.h" |
Ayu Ishii | f3966ca | 2020-07-08 17:35:12 | [diff] [blame] | 34 | #include "net/cookies/cookie_access_result.h" |
Lily Chen | f068a76 | 2019-08-21 21:10:50 | [diff] [blame] | 35 | #include "net/cookies/cookie_util.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 36 | #include "net/dns/mock_host_resolver.h" |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 37 | #include "net/http/alternative_service.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 38 | #include "net/test/embedded_test_server/embedded_test_server.h" |
Chris Fredrickson | f9de345 | 2022-09-12 21:46:56 | [diff] [blame] | 39 | #include "services/network/public/cpp/network_switches.h" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 40 | #include "services/network/public/mojom/restricted_cookie_manager.mojom-test-utils.h" |
| 41 | #include "services/network/public/mojom/restricted_cookie_manager.mojom.h" |
| 42 | #include "services/service_manager/public/cpp/interface_provider.h" |
nick | 462f9283 | 2015-12-04 18:47:45 | [diff] [blame] | 43 | #include "testing/gmock/include/gmock/gmock.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 44 | #include "testing/gtest/include/gtest/gtest.h" |
Anton Bikineev | f62d1bf | 2021-05-15 17:56:07 | [diff] [blame] | 45 | #include "third_party/abseil-cpp/absl/types/optional.h" |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 46 | #include "url/gurl.h" |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 47 | |
| 48 | namespace content { |
| 49 | |
| 50 | namespace { |
| 51 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 52 | void SetCookieFromJS(RenderFrameHost* frame, std::string cookie) { |
| 53 | EvalJsResult result = EvalJs(frame, "document.cookie = '" + cookie + "'"); |
| 54 | EXPECT_TRUE(result.error.empty()) << result.error; |
| 55 | } |
| 56 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 57 | std::string GetCookieFromJS(RenderFrameHost* frame) { |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 58 | return EvalJs(frame, "document.cookie;").ExtractString(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 59 | } |
| 60 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 61 | void SetCookieDirect(WebContentsImpl* tab, |
| 62 | const GURL& url, |
| 63 | const std::string& cookie_line) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 64 | net::CookieOptions options; |
| 65 | // Allow setting SameSite cookies. |
| 66 | options.set_same_site_cookie_context( |
Steven Bingler | 8d76c2a4 | 2020-03-24 17:13:32 | [diff] [blame] | 67 | net::CookieOptions::SameSiteCookieContext::MakeInclusive()); |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 68 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 69 | auto cookie_obj = net::CanonicalCookie::Create( |
Dylan Cutler | f4a802b | 2021-08-03 16:38:54 | [diff] [blame] | 70 | url, cookie_line, base::Time::Now(), absl::nullopt /* server_time */, |
| 71 | absl::nullopt /* cookie_partition_key */); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 72 | |
| 73 | base::RunLoop run_loop; |
Lukasz Anforowicz | b9a969a | 2021-04-29 15:26:25 | [diff] [blame] | 74 | tab->GetBrowserContext() |
| 75 | ->GetDefaultStoragePartition() |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 76 | ->GetCookieManagerForBrowserProcess() |
| 77 | ->SetCanonicalCookie( |
Lily Chen | 96f29a13 | 2020-04-15 17:59:36 | [diff] [blame] | 78 | *cookie_obj, url, options, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 79 | base::BindLambdaForTesting( |
Ayu Ishii | f3966ca | 2020-07-08 17:35:12 | [diff] [blame] | 80 | [&](net::CookieAccessResult status) { run_loop.Quit(); })); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 81 | run_loop.Run(); |
| 82 | } |
| 83 | |
| 84 | std::string GetCookiesDirect(WebContentsImpl* tab, const GURL& url) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 85 | net::CookieOptions options; |
| 86 | // Allow setting SameSite cookies. |
| 87 | options.set_same_site_cookie_context( |
Steven Bingler | 8d76c2a4 | 2020-03-24 17:13:32 | [diff] [blame] | 88 | net::CookieOptions::SameSiteCookieContext::MakeInclusive()); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 89 | net::CookieList result; |
| 90 | base::RunLoop run_loop; |
Lukasz Anforowicz | b9a969a | 2021-04-29 15:26:25 | [diff] [blame] | 91 | tab->GetBrowserContext() |
| 92 | ->GetDefaultStoragePartition() |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 93 | ->GetCookieManagerForBrowserProcess() |
Ayu Ishii | bc6fdb0a | 2020-06-08 22:59:19 | [diff] [blame] | 94 | ->GetCookieList( |
Aykut Bulut | 244341e | 2021-12-09 15:57:25 | [diff] [blame] | 95 | url, options, net::CookiePartitionKeyCollection(), |
Ayu Ishii | bc6fdb0a | 2020-06-08 22:59:19 | [diff] [blame] | 96 | base::BindLambdaForTesting( |
| 97 | [&](const net::CookieAccessResultList& cookie_list, |
| 98 | const net::CookieAccessResultList& excluded_cookies) { |
| 99 | result = net::cookie_util::StripAccessResults(cookie_list); |
| 100 | run_loop.Quit(); |
| 101 | })); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 102 | run_loop.Run(); |
| 103 | return net::CanonicalCookie::BuildCookieLine(result); |
rockot | 6a2d66a | 2016-08-04 00:15:25 | [diff] [blame] | 104 | } |
| 105 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 106 | } // namespace |
| 107 | |
Thomas Phillips | 7c0c3a5 | 2020-11-19 02:01:23 | [diff] [blame] | 108 | class CookieBrowserTest : public ContentBrowserTest { |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 109 | protected: |
| 110 | void SetUp() override { |
| 111 | base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| 112 | switches::kEnableExperimentalWebPlatformFeatures); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 113 | ContentBrowserTest::SetUp(); |
| 114 | } |
jam | 8f34ea7 | 2017-04-26 17:48:55 | [diff] [blame] | 115 | |
| 116 | void SetUpOnMainThread() override { |
| 117 | // Support multiple sites on the test server. |
| 118 | host_resolver()->AddRule("*", "127.0.0.1"); |
| 119 | } |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 120 | }; |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 121 | |
| 122 | // Exercises basic cookie operations via javascript, including an http page |
| 123 | // interacting with secure cookies. |
Thomas Phillips | 7c0c3a5 | 2020-11-19 02:01:23 | [diff] [blame] | 124 | IN_PROC_BROWSER_TEST_F(CookieBrowserTest, Cookies) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 125 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 126 | ASSERT_TRUE(embedded_test_server()->Start()); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 127 | |
svaldez | c3a9a17 | 2015-11-03 22:01:33 | [diff] [blame] | 128 | net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
Alan Cutter | fb568919 | 2019-03-28 08:39:19 | [diff] [blame] | 129 | https_server.AddDefaultHandlers(GetTestDataFilePath()); |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 130 | https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 131 | ASSERT_TRUE(https_server.Start()); |
| 132 | |
Lily Chen | 15b3ff88 | 2021-05-21 01:27:41 | [diff] [blame] | 133 | // The server sends a HttpOnly cookie. The RestrictedCookieManager should |
nick | 462f9283 | 2015-12-04 18:47:45 | [diff] [blame] | 134 | // never allow this to be sent to any renderer process. |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 135 | GURL https_url = |
| 136 | https_server.GetURL("a.test", "/set-cookie?notforjs=1;HttpOnly"); |
| 137 | GURL http_url = |
| 138 | embedded_test_server()->GetURL("a.test", "/frame_with_load_event.html"); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 139 | |
| 140 | Shell* shell2 = CreateBrowser(); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 141 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 142 | EXPECT_TRUE(NavigateToURL(shell2, https_url)); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 143 | |
| 144 | WebContentsImpl* web_contents_https = |
| 145 | static_cast<WebContentsImpl*>(shell2->web_contents()); |
| 146 | WebContentsImpl* web_contents_http = |
| 147 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 148 | if (AreDefaultSiteInstancesEnabled()) { |
| 149 | // Note: Both use the default SiteInstance because the URLs don't require |
| 150 | // a dedicated process, but these default SiteInstances are not the same |
| 151 | // object because they come from different BrowsingInstances. |
| 152 | EXPECT_TRUE(web_contents_http->GetSiteInstance()->IsDefaultSiteInstance()); |
| 153 | EXPECT_TRUE(web_contents_https->GetSiteInstance()->IsDefaultSiteInstance()); |
| 154 | EXPECT_NE(web_contents_http->GetSiteInstance(), |
| 155 | web_contents_https->GetSiteInstance()); |
| 156 | EXPECT_FALSE(web_contents_http->GetSiteInstance()->IsRelatedSiteInstance( |
| 157 | web_contents_https->GetSiteInstance())); |
| 158 | } else { |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 159 | EXPECT_EQ("http://a.test/", |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 160 | web_contents_http->GetSiteInstance()->GetSiteURL().spec()); |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 161 | EXPECT_EQ("https://a.test/", |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 162 | web_contents_https->GetSiteInstance()->GetSiteURL().spec()); |
| 163 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 164 | |
| 165 | EXPECT_NE(web_contents_http->GetSiteInstance()->GetProcess(), |
| 166 | web_contents_https->GetSiteInstance()->GetProcess()); |
| 167 | |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 168 | EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 169 | EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 170 | |
| 171 | // Non-TLS page writes secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 172 | EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(), |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 173 | "document.cookie = 'A=1; secure;';")); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 174 | EXPECT_EQ("", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 175 | EXPECT_EQ("", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 176 | |
Joshua Hood | f54ef85 | 2022-02-23 17:33:58 | [diff] [blame] | 177 | // Non-TLS page writes not-secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 178 | EXPECT_TRUE(ExecJs(web_contents_http->GetPrimaryMainFrame(), |
| 179 | "document.cookie = 'B=2';")); |
| 180 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 181 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 182 | |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 183 | // TLS page writes secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 184 | EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(), |
Avi Drissman | 49ecc70 | 2021-04-15 20:37:41 | [diff] [blame] | 185 | "document.cookie = 'C=3;secure;';")); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 186 | EXPECT_EQ("B=2; C=3", |
| 187 | GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 188 | EXPECT_EQ("B=2", GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 189 | |
| 190 | // TLS page writes not-secure cookie. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 191 | EXPECT_TRUE(ExecJs(web_contents_https->GetPrimaryMainFrame(), |
| 192 | "document.cookie = 'D=4';")); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 193 | EXPECT_EQ("B=2; C=3; D=4", |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 194 | GetCookieFromJS(web_contents_https->GetPrimaryMainFrame())); |
| 195 | EXPECT_EQ("B=2; D=4", |
| 196 | GetCookieFromJS(web_contents_http->GetPrimaryMainFrame())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 197 | } |
| 198 | |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 199 | // Ensure "priority" cookie option is settable via document.cookie. |
Thomas Phillips | 7c0c3a5 | 2020-11-19 02:01:23 | [diff] [blame] | 200 | IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookiePriority) { |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 201 | ASSERT_TRUE(embedded_test_server()->Start()); |
| 202 | |
| 203 | struct { |
| 204 | std::string param; |
| 205 | net::CookiePriority priority; |
| 206 | } cases[] = {{"name=value", net::COOKIE_PRIORITY_DEFAULT}, |
| 207 | {"name=value;priority=Low", net::COOKIE_PRIORITY_LOW}, |
| 208 | {"name=value;priority=Medium", net::COOKIE_PRIORITY_MEDIUM}, |
| 209 | {"name=value;priority=High", net::COOKIE_PRIORITY_HIGH}}; |
| 210 | |
| 211 | for (auto test_case : cases) { |
| 212 | GURL url = embedded_test_server()->GetURL("/set_document_cookie.html?" + |
| 213 | test_case.param); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 214 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
Joshua Bell | 1d432be | 2018-06-27 19:59:48 | [diff] [blame] | 215 | std::vector<net::CanonicalCookie> cookies = |
| 216 | GetCanonicalCookies(shell()->web_contents()->GetBrowserContext(), url); |
| 217 | |
| 218 | EXPECT_EQ(1u, cookies.size()); |
| 219 | EXPECT_EQ("name", cookies[0].Name()); |
| 220 | EXPECT_EQ("value", cookies[0].Value()); |
| 221 | EXPECT_EQ(test_case.priority, cookies[0].Priority()); |
| 222 | } |
| 223 | } |
| 224 | |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 225 | // SameSite cookies (that aren't marked as http-only) should be available to |
| 226 | // JavaScript. |
Thomas Phillips | 7c0c3a5 | 2020-11-19 02:01:23 | [diff] [blame] | 227 | IN_PROC_BROWSER_TEST_F(CookieBrowserTest, SameSiteCookies) { |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 228 | // Must use HTTPS because SameSite=None cookies must be Secure. |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 229 | net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS); |
| 230 | server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
| 231 | server.AddDefaultHandlers(GetTestDataFilePath()); |
| 232 | SetupCrossSiteRedirector(&server); |
| 233 | ASSERT_TRUE(server.Start()); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 234 | |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 235 | // The server sets eight cookies on 'a.test' and on 'b.test', then loads |
| 236 | // a page that frames both 'a.test' and 'b.test' under 'a.test'. |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 237 | std::string cookies_to_set = |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 238 | "/set-cookie?none=1;SameSite=None;Secure" // SameSite=None must be |
| 239 | // Secure. |
| 240 | "&none-insecure=1;SameSite=None" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 241 | "&strict=1;SameSite=Strict" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 242 | "&unspecified=1" // unspecified SameSite should be treated as Lax. |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 243 | "&lax=1;SameSite=Lax" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 244 | "&none-http=1;SameSite=None;Secure;httponly" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 245 | "&strict-http=1;SameSite=Strict;httponly" |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 246 | "&unspecified-http=1;httponly" |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 247 | "&lax-http=1;SameSite=Lax;httponly"; |
| 248 | |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 249 | GURL url = server.GetURL("a.test", cookies_to_set); |
Alex Moshchuk | aeb20fe3 | 2019-09-25 17:40:01 | [diff] [blame] | 250 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 251 | url = server.GetURL("b.test", cookies_to_set); |
Alex Moshchuk | aeb20fe3 | 2019-09-25 17:40:01 | [diff] [blame] | 252 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
David Benjamin | ddf9671c3 | 2019-12-03 16:54:12 | [diff] [blame] | 253 | url = server.GetURL( |
| 254 | "a.test", "/cross_site_iframe_factory.html?a.test(a.test(),b.test())"); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 255 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 256 | |
| 257 | WebContentsImpl* web_contents = |
| 258 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 259 | RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame(); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 260 | RenderFrameHost* a_iframe = web_contents->GetPrimaryFrameTree() |
| 261 | .root() |
| 262 | ->child_at(0) |
| 263 | ->current_frame_host(); |
| 264 | RenderFrameHost* b_iframe = web_contents->GetPrimaryFrameTree() |
| 265 | .root() |
| 266 | ->child_at(1) |
| 267 | ->current_frame_host(); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 268 | |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 269 | // The top-level frame should get all same-site cookies. |
| 270 | EXPECT_EQ("none=1; strict=1; unspecified=1; lax=1", |
| 271 | GetCookieFromJS(main_frame)); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 272 | |
| 273 | // Same-site cookies will be delievered to the 'a.com' frame, as it is same- |
| 274 | // site with its ancestors. |
Lily Chen | e1d272a5 | 2019-08-12 17:00:56 | [diff] [blame] | 275 | EXPECT_EQ("none=1; strict=1; unspecified=1; lax=1", |
| 276 | GetCookieFromJS(a_iframe)); |
mkwst | f71d0bd | 2016-03-21 14:15:24 | [diff] [blame] | 277 | |
| 278 | // 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] | 279 | // isn't same-site with its ancestors. The SameSite=None but insecure cookie |
| 280 | // is rejected. |
| 281 | EXPECT_EQ("none=1", GetCookieFromJS(b_iframe)); |
mkwst | df1821c6 | 2016-02-24 13:07:20 | [diff] [blame] | 282 | } |
| 283 | |
Aaron Tagliaboschi | 6ed3c069 | 2022-02-27 18:54:00 | [diff] [blame] | 284 | IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookieTruncatingChar) { |
| 285 | using std::string_literals::operator""s; |
| 286 | |
| 287 | std::string cookie_string; |
| 288 | embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| 289 | [&](const net::test_server::HttpRequest& request) |
| 290 | -> std::unique_ptr<net::test_server::HttpResponse> { |
| 291 | auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| 292 | response->AddCustomHeader("Set-Cookie", cookie_string); |
| 293 | return std::move(response); |
| 294 | })); |
| 295 | |
| 296 | ASSERT_TRUE(embedded_test_server()->Start()); |
| 297 | |
| 298 | GURL http_url = embedded_test_server()->GetURL("/"); |
| 299 | base::HistogramTester histogram; |
| 300 | |
| 301 | // Test scenarios where a control char may appear at start, middle and end of |
| 302 | // a cookie line. Control char array with NULL (\x0), CR (\xD), and LF (xA) |
| 303 | char kTestChars[] = {'\x0', '\xD', '\xA'}; |
| 304 | |
| 305 | for (const auto& test : kTestChars) { |
| 306 | std::string ctl_string(1, test); |
| 307 | |
| 308 | // ctrl char at start of string |
| 309 | cookie_string = ctl_string + "foo=bar"s; |
| 310 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 311 | |
| 312 | // ctrl char at middle of string |
| 313 | cookie_string = "foo=bar;"s + ctl_string + "httponly"s; |
| 314 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 315 | |
| 316 | // ctrl char at end of string |
| 317 | cookie_string = "foo=bar;"s + "httponly;"s + ctl_string; |
| 318 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 319 | } |
| 320 | // Test if there are multiple control characters that terminate. |
| 321 | cookie_string = "foo=bar;\xA\xDhttponly"s; |
| 322 | EXPECT_TRUE(NavigateToURL(shell(), http_url)); |
| 323 | |
| 324 | FetchHistogramsFromChildProcesses(); |
| 325 | histogram.ExpectBucketCount( |
| 326 | "Cookie.TruncatingCharacterInCookieString", |
| 327 | net::TruncatingCharacterInCookieStringType::kTruncatingCharNull, 0); |
| 328 | histogram.ExpectBucketCount( |
| 329 | "Cookie.TruncatingCharacterInCookieString", |
| 330 | net::TruncatingCharacterInCookieStringType::kTruncatingCharNewline, 0); |
| 331 | histogram.ExpectBucketCount( |
| 332 | "Cookie.TruncatingCharacterInCookieString", |
| 333 | net::TruncatingCharacterInCookieStringType::kTruncatingCharLineFeed, 0); |
| 334 | } |
| 335 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 336 | class RestrictedCookieManagerInterceptor |
| 337 | : public network::mojom::RestrictedCookieManagerInterceptorForTesting { |
| 338 | public: |
| 339 | RestrictedCookieManagerInterceptor( |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 340 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver, |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 341 | mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm) |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 342 | : receiver_(this, std::move(receiver)), real_rcm_(std::move(real_rcm)) {} |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 343 | |
Anton Bikineev | f62d1bf | 2021-05-15 17:56:07 | [diff] [blame] | 344 | void set_override_url(absl::optional<std::string> maybe_url) { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 345 | override_url_ = std::move(maybe_url); |
| 346 | } |
| 347 | |
| 348 | void SetCookieFromString(const GURL& url, |
Maks Orlovich | ab27e24 | 2020-01-07 18:10:39 | [diff] [blame] | 349 | const net::SiteForCookies& site_for_cookies, |
Christian Dullweber | 10d62c1 | 2019-08-19 12:08:19 | [diff] [blame] | 350 | const url::Origin& top_frame_origin, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 351 | const std::string& cookie, |
Dylan Cutler | 49dc392 | 2022-02-18 01:45:27 | [diff] [blame] | 352 | bool partitioned_cookies_runtime_feature_enabled, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 353 | SetCookieFromStringCallback callback) override { |
| 354 | GetForwardingInterface()->SetCookieFromString( |
Christian Dullweber | 10d62c1 | 2019-08-19 12:08:19 | [diff] [blame] | 355 | URLToUse(url), site_for_cookies, top_frame_origin, std::move(cookie), |
Dylan Cutler | 49dc392 | 2022-02-18 01:45:27 | [diff] [blame] | 356 | /*partitioned_cookies_runtime_feature_enabled=*/false, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 357 | std::move(callback)); |
| 358 | } |
| 359 | |
| 360 | void GetCookiesString(const GURL& url, |
Maks Orlovich | ab27e24 | 2020-01-07 18:10:39 | [diff] [blame] | 361 | const net::SiteForCookies& site_for_cookies, |
Christian Dullweber | 10d62c1 | 2019-08-19 12:08:19 | [diff] [blame] | 362 | const url::Origin& top_frame_origin, |
Dylan Cutler | 49dc392 | 2022-02-18 01:45:27 | [diff] [blame] | 363 | bool partitioned_cookies_runtime_feature_enabled, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 364 | GetCookiesStringCallback callback) override { |
| 365 | GetForwardingInterface()->GetCookiesString( |
Dylan Cutler | 49dc392 | 2022-02-18 01:45:27 | [diff] [blame] | 366 | URLToUse(url), site_for_cookies, top_frame_origin, |
| 367 | /*partitioned_cookies_runtime_feature_enabled=*/false, |
| 368 | std::move(callback)); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 369 | } |
| 370 | |
| 371 | private: |
| 372 | network::mojom::RestrictedCookieManager* GetForwardingInterface() override { |
| 373 | return real_rcm_.get(); |
| 374 | } |
| 375 | |
| 376 | GURL URLToUse(const GURL& url_in) { |
| 377 | return override_url_ ? GURL(override_url_.value()) : url_in; |
| 378 | } |
| 379 | |
Anton Bikineev | f62d1bf | 2021-05-15 17:56:07 | [diff] [blame] | 380 | absl::optional<std::string> override_url_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 381 | |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 382 | mojo::Receiver<network::mojom::RestrictedCookieManager> receiver_; |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 383 | mojo::Remote<network::mojom::RestrictedCookieManager> real_rcm_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 384 | }; |
| 385 | |
| 386 | class CookieStoreContentBrowserClient : public ContentBrowserClient { |
| 387 | public: |
Chris Fredrickson | 5d0cb4f | 2022-09-27 16:48:42 | [diff] [blame^] | 388 | ~CookieStoreContentBrowserClient() override = default; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 389 | |
| 390 | bool WillCreateRestrictedCookieManager( |
Maks Orlovich | e7db7a2 | 2019-07-25 01:47:46 | [diff] [blame] | 391 | network::mojom::RestrictedCookieManagerRole role, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 392 | content::BrowserContext* browser_context, |
Shuran Huang | bd4d169 | 2021-01-26 17:03:41 | [diff] [blame] | 393 | const url::Origin& origin, |
Shuran Huang | 92b415d7 | 2021-01-12 20:48:15 | [diff] [blame] | 394 | const net::IsolationInfo& isolation_info, |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 395 | bool is_service_worker, |
| 396 | int process_id, |
| 397 | int routing_id, |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 398 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager>* receiver) |
| 399 | override { |
| 400 | mojo::PendingReceiver<network::mojom::RestrictedCookieManager> |
| 401 | orig_receiver = std::move(*receiver); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 402 | |
Julie Jeongeun Kim | f42b07c | 2019-08-27 15:38:36 | [diff] [blame] | 403 | mojo::PendingRemote<network::mojom::RestrictedCookieManager> real_rcm; |
| 404 | *receiver = real_rcm.InitWithNewPipeAndPassReceiver(); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 405 | |
| 406 | rcm_interceptor_ = std::make_unique<RestrictedCookieManagerInterceptor>( |
Julie Jeongeun Kim | d20f64b | 2019-08-26 04:13:03 | [diff] [blame] | 407 | std::move(orig_receiver), std::move(real_rcm)); |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 408 | rcm_interceptor_->set_override_url(override_url_); |
| 409 | |
| 410 | return false; // only made a proxy, still need the actual impl to be made. |
| 411 | } |
| 412 | |
Anton Bikineev | f62d1bf | 2021-05-15 17:56:07 | [diff] [blame] | 413 | void set_override_url(absl::optional<std::string> maybe_url) { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 414 | override_url_ = maybe_url; |
| 415 | if (rcm_interceptor_) |
| 416 | rcm_interceptor_->set_override_url(override_url_); |
| 417 | } |
| 418 | |
| 419 | private: |
Anton Bikineev | f62d1bf | 2021-05-15 17:56:07 | [diff] [blame] | 420 | absl::optional<std::string> override_url_; |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 421 | std::unique_ptr<RestrictedCookieManagerInterceptor> rcm_interceptor_; |
| 422 | }; |
| 423 | |
| 424 | // Cookie access in loader is locked to a particular origin, so messages |
| 425 | // for wrong URLs are rejected. |
| 426 | // TODO(https://crbug.com/954603): This should actually result in renderer |
| 427 | // kills. |
Thomas Phillips | 7c0c3a5 | 2020-11-19 02:01:23 | [diff] [blame] | 428 | IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CrossSiteCookieSecurityEnforcement) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 429 | // The code under test is only active under site isolation. |
nick | d30fd96 | 2015-07-27 21:51:08 | [diff] [blame] | 430 | if (!AreAllSitesIsolatedForTesting()) { |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 431 | return; |
| 432 | } |
| 433 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 434 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 435 | ASSERT_TRUE(embedded_test_server()->Start()); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 436 | EXPECT_TRUE(NavigateToURL( |
| 437 | shell(), embedded_test_server()->GetURL("/frame_with_load_event.html"))); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 438 | |
| 439 | WebContentsImpl* tab = static_cast<WebContentsImpl*>(shell()->web_contents()); |
| 440 | |
| 441 | // The iframe on the http page should get its own process. |
| 442 | FrameTreeVisualizer v; |
| 443 | EXPECT_EQ( |
| 444 | " Site A ------------ proxies for B\n" |
| 445 | " +--Site B ------- proxies for A\n" |
| 446 | "Where A = http://127.0.0.1/\n" |
| 447 | " B = http://baz.com/", |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 448 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 449 | |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 450 | RenderFrameHost* main_frame = tab->GetPrimaryMainFrame(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 451 | RenderFrameHost* iframe = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 452 | tab->GetPrimaryFrameTree().root()->child_at(0)->current_frame_host(); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 453 | |
| 454 | EXPECT_NE(iframe->GetProcess(), main_frame->GetProcess()); |
| 455 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 456 | SetCookieDirect(tab, GURL("http://127.0.0.1/"), "A_cookie = parent"); |
| 457 | SetCookieDirect(tab, GURL("http://baz.com/"), "B_cookie = child"); |
| 458 | EXPECT_EQ("A_cookie=parent", |
| 459 | GetCookiesDirect(tab, GURL("http://127.0.0.1/"))); |
| 460 | |
| 461 | EXPECT_EQ("B_cookie=child", GetCookiesDirect(tab, GURL("http://baz.com/"))); |
| 462 | |
| 463 | // Try to get cross-site cookies from the subframe's process. |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 464 | { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 465 | CookieStoreContentBrowserClient browser_client; |
| 466 | content::ContentBrowserClient* old_browser_client = |
| 467 | content::SetBrowserClientForTesting(&browser_client); |
| 468 | browser_client.set_override_url("http://127.0.0.1/"); |
| 469 | EXPECT_EQ("", GetCookieFromJS(iframe)); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 470 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 471 | content::SetBrowserClientForTesting(old_browser_client); |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 472 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 473 | |
| 474 | EXPECT_EQ( |
| 475 | " Site A ------------ proxies for B\n" |
| 476 | " +--Site B ------- proxies for A\n" |
| 477 | "Where A = http://127.0.0.1/\n" |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 478 | " B = http://baz.com/", |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 479 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 480 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 481 | // Now set a cross-site cookie from the main frame's process. |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 482 | { |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 483 | CookieStoreContentBrowserClient browser_client; |
| 484 | content::ContentBrowserClient* old_browser_client = |
| 485 | content::SetBrowserClientForTesting(&browser_client); |
rockot | 1587e33 | 2016-07-27 17:44:14 | [diff] [blame] | 486 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 487 | browser_client.set_override_url("https://baz.com/"); |
| 488 | SetCookieFromJS(iframe, "pwn=ed"); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 489 | |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 490 | EXPECT_EQ("B_cookie=child", GetCookiesDirect(tab, GURL("http://baz.com/"))); |
| 491 | |
| 492 | content::SetBrowserClientForTesting(old_browser_client); |
Lukasz Anforowicz | 6f74628 | 2018-01-04 23:24:51 | [diff] [blame] | 493 | } |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 494 | |
| 495 | EXPECT_EQ( |
Maks Orlovich | 710d5e3 | 2019-07-09 20:16:45 | [diff] [blame] | 496 | " Site A ------------ proxies for B\n" |
| 497 | " +--Site B ------- proxies for A\n" |
| 498 | "Where A = http://127.0.0.1/\n" |
| 499 | " B = http://baz.com/", |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 500 | v.DepictFrameTree(tab->GetPrimaryFrameTree().root())); |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 501 | } |
| 502 | |
nick | 5f3d7607 | 2015-06-09 00:24:00 | [diff] [blame] | 503 | } // namespace content |