blob: c39d302b6a520f2345533bb28087ebe7c6f663da [file] [log] [blame]
// Copyright 2017 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 "content/public/browser/browsing_data_remover.h"
#include <memory>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/test/bind_test_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace {
const char kHstsPath[] = "/hsts";
const char kHttpAuthPath[] = "/http_auth";
const char kHstsResponseBody[] = "HSTS set";
std::unique_ptr<net::test_server::HttpResponse> HandleHstsRequest(
const net::test_server::HttpRequest& request) {
if (request.relative_url == kHstsPath) {
std::unique_ptr<net::test_server::BasicHttpResponse> hsts_response =
std::make_unique<net::test_server::BasicHttpResponse>();
hsts_response->AddCustomHeader("Strict-Transport-Security",
"max-age=1000000");
hsts_response->set_content(kHstsResponseBody);
return hsts_response;
}
return nullptr;
}
// Handles |request| to "/http_auth". If "Authorization" header is present,
// responds with a non-empty HTTP 200 page (regardless of auth credentials).
// Otherwise serves a Basic Auth challenge.
std::unique_ptr<net::test_server::HttpResponse> HandleHttpAuthRequest(
const net::test_server::HttpRequest& request) {
if (request.relative_url != kHttpAuthPath)
return nullptr;
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
if (base::Contains(request.headers, "Authorization")) {
http_response->set_code(net::HTTP_OK);
http_response->set_content("Success!");
} else {
http_response->set_code(net::HTTP_UNAUTHORIZED);
http_response->AddCustomHeader("WWW-Authenticate",
"Basic realm=\"test realm\"");
}
return http_response;
}
} // namespace
namespace content {
class BrowsingDataRemoverImplBrowserTest : public ContentBrowserTest {
public:
BrowsingDataRemoverImplBrowserTest()
: ssl_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
// Use localhost instead of 127.0.0.1, as HSTS isn't allowed on IPs.
ssl_server_.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
ssl_server_.AddDefaultHandlers(GetTestDataFilePath());
ssl_server_.RegisterRequestHandler(base::BindRepeating(&HandleHstsRequest));
ssl_server_.RegisterRequestHandler(
base::BindRepeating(&HandleHttpAuthRequest));
EXPECT_TRUE(ssl_server_.Start());
}
void SetUpOnMainThread() override {}
void RemoveAndWait(int remove_mask) {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(
shell()->web_contents()->GetBrowserContext());
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
base::Time(), base::Time::Max(), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
completion_observer.BlockUntilCompletion();
}
// Issues a request for kHstsPath on localhost, and expects it to enable HSTS
// for the domain.
void IssueRequestThatSetsHsts() {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = ssl_server_.GetURL("localhost", kHstsPath);
SimpleURLLoaderTestHelper loader_helper;
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory(), loader_helper.GetCallback());
loader_helper.WaitForCallback();
ASSERT_TRUE(loader_helper.response_body());
EXPECT_EQ(kHstsResponseBody, *loader_helper.response_body());
EXPECT_TRUE(IsHstsSet());
}
// Returns true if HSTS is set on localhost. Does this by issuing an HTTP
// request to the embedded test server, and expecting it to be redirected from
// HTTP to HTTPS if HSTS is enabled. If the request succeeds, it was sent
// over HTTPS, so HSTS is enabled. If it fails, the request was send using
// HTTP instead, so HSTS is not enabled for the domain.
bool IsHstsSet() {
GURL url = ssl_server_.GetURL("localhost", "/echo");
GURL::Replacements replacements;
replacements.SetSchemeStr("http");
url = url.ReplaceComponents(replacements);
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = url;
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
SimpleURLLoaderTestHelper loader_helper;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory(), loader_helper.GetCallback());
loader_helper.WaitForCallback();
// On success, HSTS was enabled for the domain.
if (loader_helper.response_body()) {
EXPECT_EQ("Echo", *loader_helper.response_body());
return true;
}
// On failure, the server just hangs up, since it didn't receive an SSL
// handshake.
EXPECT_EQ(net::ERR_EMPTY_RESPONSE, loader->NetError());
return false;
}
// Sets HTTP auth cache by making a request with credentials specified in the
// URL to a page with an auth challenge.
void IssueRequestThatSetsHttpAuthCache() {
GURL url = ssl_server_.GetURL(kHttpAuthPath);
GURL::Replacements replacements;
replacements.SetUsernameStr("user");
replacements.SetPasswordStr("password");
GURL url_with_creds = url.ReplaceComponents(replacements);
ASSERT_TRUE(NavigateToURL(shell(), url_with_creds));
ASSERT_TRUE(IsHttpAuthCacheSet());
}
// Determines if auth cache is populated by seeing if a request to a page with
// an auth challenge succeeds.
bool IsHttpAuthCacheSet() {
// Set a login request callback to be used instead of a login dialog since
// such a dialog is difficult to control programmatically and doesn't work
// on all platforms.
bool login_requested = false;
ShellContentBrowserClient::Get()->set_login_request_callback(
base::BindLambdaForTesting(
[&](bool is_main_frame /* unused */) { login_requested = true; }));
GURL url = ssl_server_.GetURL(kHttpAuthPath);
bool navigation_suceeded = NavigateToURL(shell(), url);
// Because our login request callback does nothing, navigation should
// succeed iff login is not needed unless some other unexpected error
// occurs.
EXPECT_NE(navigation_suceeded, login_requested);
return !login_requested && navigation_suceeded;
}
network::mojom::URLLoaderFactory* url_loader_factory() {
return BrowserContext::GetDefaultStoragePartition(
shell()->web_contents()->GetBrowserContext())
->GetURLLoaderFactoryForBrowserProcess()
.get();
}
private:
net::test_server::EmbeddedTestServer ssl_server_;
};
// Verify that TransportSecurityState data is cleared for REMOVE_CACHE.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearTransportSecurityState) {
IssueRequestThatSetsHsts();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
EXPECT_FALSE(IsHstsSet());
}
// Verify that TransportSecurityState data is not cleared if REMOVE_CACHE is not
// set.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
PreserveTransportSecurityState) {
IssueRequestThatSetsHsts();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
EXPECT_TRUE(IsHstsSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, ClearHttpAuthCache) {
ASSERT_FALSE(IsHttpAuthCacheSet());
IssueRequestThatSetsHttpAuthCache();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_FALSE(IsHttpAuthCacheSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
PreserveHttpAuthCache) {
ASSERT_FALSE(IsHttpAuthCacheSet());
IssueRequestThatSetsHttpAuthCache();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
EXPECT_TRUE(IsHttpAuthCacheSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearHttpAuthCacheWhenEmpty) {
ASSERT_FALSE(IsHttpAuthCacheSet());
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_FALSE(IsHttpAuthCacheSet());
}
} // namespace content