blob: f79123c0c1e6586ec2777849d8fb558ad9260f83 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/browsing_data/third_party_data_remover.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/net/storage_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
using content::BrowserThread;
namespace {
const std::vector<std::string> kStorageTypes{
"LocalStorage", "FileSystem", "FileSystemAccess",
"SessionStorage", "IndexedDb", "WebSql",
"CacheStorage", "ServiceWorker", "StorageFoundation"};
class ThirdPartyDataRemoverTest : public InProcessBrowserTest {
protected:
ThirdPartyDataRemoverTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
scoped_feature_list_.InitWithFeatures(
{features::kClientStorageAccessContextAuditing}, {});
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
base::FilePath path;
base::PathService::Get(content::DIR_TEST_DATA, &path);
https_server_.ServeFilesFromDirectory(path);
https_server_.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(https_server_.Start());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// HTTPS server only serves a valid cert for localhost, so this is needed
// to load pages from other hosts without an error.
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
// Storage Foundation has to be enabled, since it is accessed from the tests
// that use chrome/browser/net/storage_test_utils.cc.
// TODO(fivedots): Remove this switch once Storage Foundation
// is enabled by default.
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"StorageFoundationAPI");
}
network::mojom::CookieManager* CookieManager() {
return browser()
->profile()
->GetDefaultStoragePartition()
->GetCookieManagerForBrowserProcess();
}
void SetCookie(std::string host,
std::string name,
net::CookieSameSite same_site,
net::CookieOptions::SameSiteCookieContext cookie_context) {
std::unique_ptr<net::CanonicalCookie> cookie =
net::CanonicalCookie::CreateUnsafeCookieForTesting(
name, "foobar", host, "/", base::Time(), base::Time(), base::Time(),
/* secure= */ true, /* httponly= */ false, same_site,
net::COOKIE_PRIORITY_LOW, /* same_party= */ false);
net::CookieOptions options;
options.set_same_site_cookie_context(cookie_context);
bool result_out;
base::RunLoop run_loop;
CookieManager()->SetCanonicalCookie(
*cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"),
options,
base::BindLambdaForTesting([&](net::CookieAccessResult result) {
result_out = result.status.IsInclude();
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(result_out);
}
std::vector<net::CanonicalCookie> GetAllCookies() {
base::RunLoop run_loop;
std::vector<net::CanonicalCookie> cookies_out;
CookieManager()->GetAllCookies(base::BindLambdaForTesting(
[&](const std::vector<net::CanonicalCookie>& cookies) {
cookies_out = cookies;
run_loop.Quit();
}));
run_loop.Run();
return cookies_out;
}
void NavigateToPageWithFrame(const std::string& host) {
GURL main_url(https_server_.GetURL(host, "/iframe.html"));
ui_test_utils::NavigateToURL(browser(), main_url);
}
void NavigateFrameTo(const std::string& host) {
GURL page = https_server_.GetURL(host, "/browsing_data/site_data.html");
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(NavigateIframeToURL(web_contents, "test", page));
}
content::RenderFrameHost* GetFrame() {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
return ChildFrameAt(web_contents->GetMainFrame(), 0);
}
void AddStorage(const std::string& top_level_host,
const std::string& storage_host) {
NavigateToPageWithFrame(top_level_host);
NavigateFrameTo(storage_host);
for (const auto& data_type : kStorageTypes) {
bool result = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
GetFrame(), "set" + data_type + "()", &result));
EXPECT_TRUE(result);
}
}
void ExpectStorage(const std::string& host, bool expected) {
NavigateToPageWithFrame(host);
NavigateFrameTo(host);
for (const auto& data_type : kStorageTypes) {
bool actual = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
GetFrame(), "has" + data_type + "();", &actual));
EXPECT_EQ(expected, actual) << " for " << data_type;
}
}
void RunClearThirdPartyData() {
base::RunLoop run_loop;
ClearThirdPartyData(run_loop.QuitClosure(), browser()->profile());
run_loop.Run();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
net::test_server::EmbeddedTestServer https_server_;
DISALLOW_COPY_AND_ASSIGN(ThirdPartyDataRemoverTest);
};
// Test that ClearThirdPartyData clears SameSite=None cookies.
IN_PROC_BROWSER_TEST_F(ThirdPartyDataRemoverTest, ClearsSameSiteNoneCookies) {
SetCookie(
"www.google.com", "foo", net::CookieSameSite::NO_RESTRICTION,
net::CookieOptions::SameSiteCookieContext(
net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE));
SetCookie("www.google.com", "bar", net::CookieSameSite::STRICT_MODE,
net::CookieOptions::SameSiteCookieContext(
net::CookieOptions::SameSiteCookieContext::ContextType::
SAME_SITE_STRICT));
RunClearThirdPartyData();
std::vector<net::CanonicalCookie> cookies = GetAllCookies();
ASSERT_EQ(1u, cookies.size());
ASSERT_EQ("bar", cookies[0].Name());
}
// Test that ClearThirdPartyData only clears storage accessed in cross-site
// contexts when access context auditing is enabled.
IN_PROC_BROWSER_TEST_F(ThirdPartyDataRemoverTest,
ClearsStorageAccessedInThirdPartyContext) {
ExpectStorage("xsite.com", false);
ExpectStorage("samesite.com", false);
AddStorage("toplevel.com", "xsite.com");
AddStorage("foo.samesite.com", "samesite.com");
ExpectStorage("xsite.com", true);
ExpectStorage("samesite.com", true);
RunClearThirdPartyData();
ExpectStorage("xsite.com", false);
ExpectStorage("samesite.com", true);
}
class ThirdPartyDataRemoverFallbackTest : public ThirdPartyDataRemoverTest {
protected:
ThirdPartyDataRemoverFallbackTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
scoped_feature_list_.InitWithFeatures(
{}, {features::kClientStorageAccessContextAuditing});
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
net::test_server::EmbeddedTestServer https_server_;
DISALLOW_COPY_AND_ASSIGN(ThirdPartyDataRemoverFallbackTest);
};
// Test the fallback behavior of ClearThirdPartyData when access context
// auditing is not enabled. In this case the function should clear storage for
// domains
IN_PROC_BROWSER_TEST_F(ThirdPartyDataRemoverFallbackTest,
ClearsStorageForDomainsWithSameSiteNoneCookies) {
SetCookie(
"xsitecookie.com", "foo", net::CookieSameSite::NO_RESTRICTION,
net::CookieOptions::SameSiteCookieContext(
net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE));
SetCookie("samesitecookie.com", "bar", net::CookieSameSite::STRICT_MODE,
net::CookieOptions::SameSiteCookieContext(
net::CookieOptions::SameSiteCookieContext::ContextType::
SAME_SITE_STRICT));
ExpectStorage("xsitecookie.com", false);
ExpectStorage("samesitecookie.com", false);
ExpectStorage("nocookie.com", false);
// Storage for xsitecookie.com is only accessed in same-site context but
// should be cleared anyway.
AddStorage("xsitecookie.com", "xsitecookie.com");
// Storage for samesitecookie is only accessed in cross-site contexts, but
// since it does not have a SameSite=None cookie it should not be cleared.
AddStorage("toplevel.com", "samesitecookie.com");
// Similarly, if a site has no cookies it should still keep its storage.
AddStorage("toplevel.com", "nocookie.com");
ExpectStorage("xsitecookie.com", true);
ExpectStorage("samesitecookie.com", true);
ExpectStorage("nocookie.com", true);
RunClearThirdPartyData();
ExpectStorage("xsitecookie.com", false);
ExpectStorage("samesitecookie.com", true);
ExpectStorage("nocookie.com", true);
}
} // namespace