| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_features.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/cookie_config/cookie_store_util.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/network_service_util.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_test.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/cookies/cookie_access_result.h" |
| #include "net/cookies/cookie_util.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/extras/sqlite/cookie_crypto_delegate.h" |
| #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/public/mojom/network_service.mojom.h" |
| #include "services/network/public/mojom/url_response_head.mojom.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "sandbox/policy/features.h" |
| #endif |
| |
| namespace { |
| |
| constexpr char kCookieName[] = "Name"; |
| constexpr char kCookieValue[] = "Value"; |
| |
| net::CookieList GetCookies(network::mojom::CookieManager* cookie_manager) { |
| base::RunLoop run_loop; |
| net::CookieList cookies_out; |
| cookie_manager->GetAllCookies( |
| base::BindLambdaForTesting([&](const net::CookieList& cookies) { |
| cookies_out = cookies; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return cookies_out; |
| } |
| |
| void SetCookie(network::mojom::CookieManager* cookie_manager) { |
| base::Time t = base::Time::Now(); |
| auto cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting( |
| kCookieName, kCookieValue, "www.test.com", "/", t, t + base::Days(1), |
| base::Time(), base::Time(), /*secure=*/true, /*http-only=*/false, |
| net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT); |
| base::RunLoop run_loop; |
| cookie_manager->SetCanonicalCookie( |
| *cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"), |
| net::CookieOptions(), |
| base::BindLambdaForTesting( |
| [&](net::CookieAccessResult result) { run_loop.Quit(); })); |
| run_loop.Run(); |
| } |
| |
| void FlushCookies(network::mojom::CookieManager* cookie_manager) { |
| base::RunLoop run_loop; |
| cookie_manager->FlushCookieStore( |
| base::BindLambdaForTesting([&]() { run_loop.Quit(); })); |
| run_loop.Run(); |
| } |
| |
| // See |NetworkServiceBrowserTest| for content's version of tests. |
| class ChromeNetworkServiceBrowserTest |
| : public InProcessBrowserTest, |
| public ::testing::WithParamInterface</*kNetworkServiceInProcess*/ bool> { |
| public: |
| ChromeNetworkServiceBrowserTest() { |
| // Verify that cookie encryption works both in-process and out of process. |
| if (GetParam()) { |
| content::ForceInProcessNetworkService(); |
| } else { |
| content::ForceOutOfProcessNetworkService(); |
| } |
| } |
| |
| ChromeNetworkServiceBrowserTest(const ChromeNetworkServiceBrowserTest&) = |
| delete; |
| ChromeNetworkServiceBrowserTest& operator=( |
| const ChromeNetworkServiceBrowserTest&) = delete; |
| |
| protected: |
| mojo::PendingRemote<network::mojom::NetworkContext> CreateNetworkContext( |
| bool enable_encrypted_cookies) { |
| mojo::PendingRemote<network::mojom::NetworkContext> network_context; |
| network::mojom::NetworkContextParamsPtr context_params = |
| network::mojom::NetworkContextParams::New(); |
| context_params->enable_encrypted_cookies = enable_encrypted_cookies; |
| // Mirrors behavior in |
| // ProfileNetworkContextService::ConfigureNetworkContextParamsInternal into |
| // this test. |
| g_browser_process->system_network_context_manager() |
| ->AddCookieEncryptionManagerToNetworkContextParams( |
| context_params.get()); |
| context_params->file_paths = network::mojom::NetworkContextFilePaths::New(); |
| |
| // Network files for the test context need to differ from the ones created |
| // for the current profile. |
| base::FilePath data_path = |
| g_browser_process->profile_manager()->user_data_dir().AppendASCII( |
| "Test Context"); |
| context_params->file_paths->unsandboxed_data_path = data_path; |
| context_params->file_paths->data_directory = |
| data_path.Append(chrome::kNetworkDataDirname); |
| context_params->file_paths->trigger_migration = true; |
| context_params->file_paths->cookie_database_name = |
| base::FilePath(chrome::kCookieFilename); |
| context_params->cert_verifier_params = content::GetCertVerifierParams( |
| cert_verifier::mojom::CertVerifierCreationParams::New()); |
| content::CreateNetworkContextInNetworkService( |
| network_context.InitWithNewPipeAndPassReceiver(), |
| std::move(context_params)); |
| return network_context; |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(ChromeNetworkServiceBrowserTest, |
| PRE_PRE_EncryptedCookies) { |
| // These test is only valid if crypto is enabled on the platform. |
| auto crypto_delegate = cookie_config::GetCookieCryptoDelegate(); |
| if (!crypto_delegate) { |
| GTEST_SKIP() << "No crypto on this platform."; |
| } |
| std::string ciphertext; |
| crypto_delegate->EncryptString(kCookieValue, &ciphertext); |
| ASSERT_NE(ciphertext, kCookieValue) << "Crypto should really encrypt."; |
| |
| // First set a cookie with cookie encryption enabled. |
| mojo::Remote<network::mojom::NetworkContext> context( |
| CreateNetworkContext(/*enable_encrypted_cookies=*/true)); |
| mojo::Remote<network::mojom::CookieManager> cookie_manager; |
| context->GetCookieManager(cookie_manager.BindNewPipeAndPassReceiver()); |
| |
| SetCookie(cookie_manager.get()); |
| |
| net::CookieList cookies = GetCookies(cookie_manager.get()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ(kCookieName, cookies[0].Name()); |
| EXPECT_EQ(kCookieValue, cookies[0].Value()); |
| |
| FlushCookies(cookie_manager.get()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ChromeNetworkServiceBrowserTest, PRE_EncryptedCookies) { |
| // Now attempt to read the cookie with encryption still enabled. |
| mojo::Remote<network::mojom::NetworkContext> context( |
| CreateNetworkContext(/*enable_encrypted_cookies=*/true)); |
| mojo::Remote<network::mojom::CookieManager> cookie_manager; |
| context->GetCookieManager(cookie_manager.BindNewPipeAndPassReceiver()); |
| |
| net::CookieList cookies = GetCookies(cookie_manager.get()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ(kCookieName, cookies[0].Name()); |
| EXPECT_EQ(kCookieValue, cookies[0].Value()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ChromeNetworkServiceBrowserTest, EncryptedCookies) { |
| // Now attempt to read the cookie again, but this time with encryption |
| // disabled. |
| mojo::Remote<network::mojom::NetworkContext> context( |
| CreateNetworkContext(/*enable_encrypted_cookies=*/false)); |
| mojo::Remote<network::mojom::CookieManager> cookie_manager; |
| context->GetCookieManager(cookie_manager.BindNewPipeAndPassReceiver()); |
| |
| net::CookieList cookies = GetCookies(cookie_manager.get()); |
| ASSERT_TRUE(cookies.empty()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| ChromeNetworkServiceBrowserTest, |
| testing::Bool(), |
| [](const auto& info) { |
| return info.param ? "InProcess" : "OutOfProcess"; |
| }); |
| |
| #if BUILDFLAG(IS_WIN) |
| class ChromeNetworkServiceBrowserCookieLockTest : public InProcessBrowserTest { |
| public: |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| }; |
| |
| // This test verifies that the cookie store cannot be opened once sqlite has an |
| // exclusive lock on the file. |
| IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceBrowserCookieLockTest, |
| CookiesAreLocked) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/title1.html"))); |
| base::FilePath cookie_filename = browser() |
| ->profile() |
| ->GetPath() |
| .Append(chrome::kNetworkDataDirname) |
| .Append(chrome::kCookieFilename); |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| |
| ASSERT_TRUE(base::PathExists(cookie_filename)); |
| base::File cookie_file( |
| cookie_filename, |
| base::File::Flags::FLAG_OPEN_ALWAYS | base::File::Flags::FLAG_READ); |
| EXPECT_FALSE(cookie_file.IsValid()); |
| } |
| } |
| |
| #endif // BUILDFLAG(IS_WIN) |
| |
| // See `NetworkServiceBrowserTest` for content's version of tests. This test |
| // merely tests that chrome's feature is wired up correctly to the migration |
| // code that exists in content. |
| class ChromeNetworkServiceMigrationBrowserTest : public InProcessBrowserTest { |
| public: |
| ChromeNetworkServiceMigrationBrowserTest() = default; |
| |
| void SetUp() override { |
| std::vector<base::test::FeatureRef> disabled_features, enabled_features; |
| #if BUILDFLAG(IS_WIN) |
| // On Windows, the Network Sandbox requires that data migration be enabled |
| // to function correctly. Thus, in order to correctly test the case when |
| // network data migration is not happening, the network sandbox must also be |
| // disabled. |
| disabled_features.push_back( |
| sandbox::policy::features::kNetworkServiceSandbox); |
| #endif |
| // For PRE_PRE, disable migration. For PRE_ enable it, and then disable it |
| // again. |
| if (GetTestPreCount() == 2 || GetTestPreCount() == 0) |
| disabled_features.push_back(features::kTriggerNetworkDataMigration); |
| else |
| enabled_features.push_back(features::kTriggerNetworkDataMigration); |
| |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| protected: |
| void VerifyCookiePresent() { |
| auto* cookie_manager = browser() |
| ->profile() |
| ->GetDefaultStoragePartition() |
| ->GetCookieManagerForBrowserProcess(); |
| auto cookies = GetCookies(cookie_manager); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ("name", cookies[0].Name()); |
| EXPECT_EQ("Good", cookies[0].Value()); |
| } |
| |
| base::FilePath GetOldCookieLocation() { |
| return browser()->profile()->GetPath().Append(chrome::kCookieFilename); |
| } |
| |
| base::FilePath GetNewCookieLocation() { |
| return browser() |
| ->profile() |
| ->GetPath() |
| .Append(chrome::kNetworkDataDirname) |
| .Append(chrome::kCookieFilename); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // The first part starts with migration disabled and stores a cookie. |
| IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceMigrationBrowserTest, |
| PRE_PRE_MigrateData) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE( |
| base::FeatureList::IsEnabled(features::kTriggerNetworkDataMigration)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/set_cookie_header.html"))); |
| ASSERT_NO_FATAL_FAILURE(VerifyCookiePresent()); |
| EXPECT_TRUE(base::PathExists(GetOldCookieLocation())); |
| EXPECT_FALSE(base::PathExists(GetNewCookieLocation())); |
| } |
| |
| // The second part enables the migration feature and checks for the cookie and |
| // also that the data itself has migrated on disk. |
| IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceMigrationBrowserTest, |
| PRE_MigrateData) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_TRUE( |
| base::FeatureList::IsEnabled(features::kTriggerNetworkDataMigration)); |
| ASSERT_NO_FATAL_FAILURE(VerifyCookiePresent()); |
| EXPECT_FALSE(base::PathExists(GetOldCookieLocation())); |
| EXPECT_TRUE(base::PathExists(GetNewCookieLocation())); |
| } |
| |
| // The third part verifies that if migration feature is disabled, then cookies |
| // still work and the data is still migrated (because it is not possible to |
| // unmigrate). |
| IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceMigrationBrowserTest, MigrateData) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE( |
| base::FeatureList::IsEnabled(features::kTriggerNetworkDataMigration)); |
| ASSERT_NO_FATAL_FAILURE(VerifyCookiePresent()); |
| EXPECT_FALSE(base::PathExists(GetOldCookieLocation())); |
| EXPECT_TRUE(base::PathExists(GetNewCookieLocation())); |
| } |
| |
| } // namespace |