diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc index f05439917fc..0199328943e 100644 --- a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc @@ -10,6 +10,7 @@ #include "base/memory/raw_ptr.h" #include "base/path_service.h" +#include "base/strings/escape.h" #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" #include "base/task/sequenced_task_runner.h" @@ -41,7 +42,7 @@ #include "content/public/test/test_navigation_observer.h" #include "net/base/features.h" #include "net/dns/mock_host_resolver.h" -#include "net/test/embedded_test_server/default_handlers.h" +#include "net/http/http_status_code.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/request_handler_util.h" #include "services/network/public/cpp/network_switches.h" @@ -106,6 +107,39 @@ std::unique_ptr HandleFileRequestWithCustomHeaders( return http_response; } +// This mostly mimics the behavior of /cross-site-with-cookie handler, but with +// additional cookie attributes set. +std::unique_ptr HandleCrossSiteRedirectWithSiteCookie( + EmbeddedTestServer* server, + const HttpRequest& request) { + const std::string prefix = "/cross-site-with-site-cookie"; + if (!net::test_server::ShouldHandle(request, prefix)) { + return nullptr; + } + + std::string dest_all = base::UnescapeBinaryURLComponent( + request.relative_url.substr(prefix.size() + 1)); + + std::string dest; + size_t delimiter = dest_all.find("/"); + if (delimiter != std::string::npos) { + dest = base::StringPrintf( + "//%s:%hu/%s", dest_all.substr(0, delimiter).c_str(), server->port(), + dest_all.substr(delimiter + 1).c_str()); + } + + auto http_response = std::make_unique(); + http_response->set_code(net::HTTP_MOVED_PERMANENTLY); + http_response->AddCustomHeader("Location", dest); + http_response->AddCustomHeader( + "Set-Cookie", + "server-redirect=true;path=/;SameSite=None;Secure;Max-Age=600"); + http_response->set_content_type("text/html"); + http_response->set_content( + base::StringPrintf("

Redirecting to %s", dest.c_str())); + return http_response; +} + class BrowsingDataRemoverObserver : public content::BrowsingDataRemover::Observer { public: @@ -181,6 +215,8 @@ void EphemeralStorageBrowserTest::SetUpOnMainThread() { base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dirs[0]); base::PathService::Get(content::DIR_TEST_DATA, &test_data_dirs[1]); + https_server_.RegisterRequestHandler(base::BindRepeating( + &HandleCrossSiteRedirectWithSiteCookie, &https_server_)); https_server_.RegisterDefaultHandler(base::BindRepeating( &HandleFileRequestWithCustomHeaders, base::Unretained(&http_request_monitor_), test_data_dirs)); diff --git a/browser/ephemeral_storage/ephemeral_storage_forget_by_default_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_forget_by_default_browsertest.cc index 6bc5fe25636..2f1588d0d35 100644 --- a/browser/ephemeral_storage/ephemeral_storage_forget_by_default_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_forget_by_default_browsertest.cc @@ -271,6 +271,49 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest, EXPECT_EQ("", values_site_b.iframe_2.cookies); } +IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest, + NavigationRedirectCookiesAreCleared) { + brave_shields::SetForgetFirstPartyStorageEnabled(content_settings(), true, + GURL()); + + const GURL a_site_url = + https_server_.GetURL("0.com", + "/cross-site-with-site-cookie/1.com/" + "cross-site-with-site-cookie/a.com/empty.html"); + + WebContents* site_a = LoadURLInNewTab(a_site_url); + + // Default cookie storage request should return non empty results for 0.com + // and 1.com as these websites should set cookies on server redirect. + EXPECT_FALSE(content::GetCookies(browser()->profile(), + https_server_.GetURL("0.com", "/")) + .empty()); + EXPECT_FALSE(content::GetCookies(browser()->profile(), + https_server_.GetURL("1.com", "/")) + .empty()); + + // a.com/empty.html should not set cookies. + EXPECT_TRUE(content::GetCookies(browser()->profile(), + https_server_.GetURL("a.com", "/")) + .empty()); + + // Navigating to a new TLD should clear all ephemeral cookies after keep-alive + // timeout. + ASSERT_TRUE(content::NavigateToURL(site_a, c_site_ephemeral_storage_url_)); + EXPECT_EQ(3u, WaitForCleanupAfterKeepAlive()); + + // Cookies should be cleared for 0.com and 1.com. + EXPECT_TRUE(content::GetCookies(browser()->profile(), + https_server_.GetURL("0.com", "/")) + .empty()); + EXPECT_TRUE(content::GetCookies(browser()->profile(), + https_server_.GetURL("1.com", "/")) + .empty()); + EXPECT_TRUE(content::GetCookies(browser()->profile(), + https_server_.GetURL("a.com", "/")) + .empty()); +} + IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest, PRE_ForgetFirstPartyAfterRestart) { const GURL a_site_set_cookie_url( diff --git a/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc b/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc index 52ba19d28d7..9e6eb205465 100644 --- a/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc +++ b/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc @@ -60,6 +60,38 @@ EphemeralStorageTabHelper::GetEphemeralStorageToken(const url::Origin& origin) { void EphemeralStorageTabHelper::WebContentsDestroyed() {} +void EphemeralStorageTabHelper::DidStartNavigation( + NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + navigation_handle->IsSameDocument()) { + return; + } + + CreateProvisionalTLDEphemeralLifetime(navigation_handle); +} + +void EphemeralStorageTabHelper::DidRedirectNavigation( + NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + navigation_handle->IsSameDocument()) { + return; + } + + CreateProvisionalTLDEphemeralLifetime(navigation_handle); +} + +void EphemeralStorageTabHelper::DidFinishNavigation( + NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + navigation_handle->IsSameDocument()) { + return; + } + + // Clear all provisional ephemeral lifetimes. A committed ephemeral lifetime + // is created in ReadyToCommitNavigation(). + provisional_tld_ephemeral_lifetimes_.clear(); +} + void EphemeralStorageTabHelper::ReadyToCommitNavigation( NavigationHandle* navigation_handle) { if (!navigation_handle->IsInMainFrame()) { @@ -96,6 +128,27 @@ void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL( browser_context, new_domain, site_instance->GetStoragePartitionConfig()); } +void EphemeralStorageTabHelper::CreateProvisionalTLDEphemeralLifetime( + NavigationHandle* navigation_handle) { + const GURL& url = navigation_handle->GetURL(); + if (!url.SchemeIsHTTPOrHTTPS()) { + return; + } + + const std::string new_domain = net::URLToEphemeralStorageDomain(url); + if (new_domain.empty()) { + return; + } + + auto* browser_context = web_contents()->GetBrowserContext(); + auto* site_instance = web_contents()->GetSiteInstance(); + + provisional_tld_ephemeral_lifetimes_.emplace( + TLDEphemeralLifetime::GetOrCreate( + browser_context, new_domain, + site_instance->GetStoragePartitionConfig())); +} + void EphemeralStorageTabHelper::UpdateShieldsState(const GURL& url) { if (!host_content_settings_map_ || !tld_ephemeral_lifetime_) { return; diff --git a/browser/ephemeral_storage/ephemeral_storage_tab_helper.h b/browser/ephemeral_storage/ephemeral_storage_tab_helper.h index 42b02eed30c..de9916d1d72 100644 --- a/browser/ephemeral_storage/ephemeral_storage_tab_helper.h +++ b/browser/ephemeral_storage/ephemeral_storage_tab_helper.h @@ -9,6 +9,7 @@ #include #include +#include "base/containers/flat_set.h" #include "base/memory/weak_ptr.h" #include "base/unguessable_token.h" #include "brave/browser/ephemeral_storage/tld_ephemeral_lifetime.h" @@ -45,10 +46,18 @@ class EphemeralStorageTabHelper friend class content::WebContentsUserData; // WebContentsObserver + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; + void DidRedirectNavigation( + content::NavigationHandle* navigation_handle) override; + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; void ReadyToCommitNavigation( content::NavigationHandle* navigation_handle) override; void WebContentsDestroyed() override; + void CreateProvisionalTLDEphemeralLifetime( + content::NavigationHandle* navigation_handle); void CreateEphemeralStorageAreasForDomainAndURL(const std::string& new_domain, const GURL& new_url); @@ -57,6 +66,8 @@ class EphemeralStorageTabHelper const base::raw_ptr host_content_settings_map_; scoped_refptr cookie_settings_; scoped_refptr session_storage_namespace_; + base::flat_set> + provisional_tld_ephemeral_lifetimes_; scoped_refptr tld_ephemeral_lifetime_; base::WeakPtrFactory weak_factory_{this};