1 // Copyright 2017 The Chromium Authors. All rights reserved.
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 "content/public/browser/browsing_data_remover.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/test/bind_test_util.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/browsing_data_filter_builder.h"
14 #include "content/public/browser/browsing_data_remover.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/common/content_client.h"
19 #include "content/public/test/browsing_data_remover_test_util.h"
20 #include "content/public/test/content_browser_test.h"
21 #include "content/public/test/content_browser_test_utils.h"
22 #include "content/public/test/simple_url_loader_test_helper.h"
23 #include "content/public/test/test_navigation_observer.h"
24 #include "content/shell/browser/shell.h"
25 #include "content/shell/browser/shell_content_browser_client.h"
26 #include "net/base/net_errors.h"
27 #include "net/dns/mock_host_resolver.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29 #include "net/test/embedded_test_server/http_request.h"
30 #include "net/test/embedded_test_server/http_response.h"
31 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
32 #include "services/network/public/cpp/simple_url_loader.h"
33 #include "services/network/public/mojom/network_service.mojom.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/gurl.h"
36
37 namespace {
38
39 const char kHstsPath[] = "/hsts";
40 const char kHttpAuthPath[] = "/http_auth";
41 const char kHstsResponseBody[] = "HSTS set";
42
HandleHstsRequest(const net::test_server::HttpRequest & request)43 std::unique_ptr<net::test_server::HttpResponse> HandleHstsRequest(
44 const net::test_server::HttpRequest& request) {
45 if (request.relative_url == kHstsPath) {
46 std::unique_ptr<net::test_server::BasicHttpResponse> hsts_response =
47 std::make_unique<net::test_server::BasicHttpResponse>();
48 hsts_response->AddCustomHeader("Strict-Transport-Security",
49 "max-age=1000000");
50 hsts_response->set_content(kHstsResponseBody);
51 return hsts_response;
52 }
53 return nullptr;
54 }
55
56 // Handles |request| to "/http_auth". If "Authorization" header is present,
57 // responds with a non-empty HTTP 200 page (regardless of auth credentials).
58 // Otherwise serves a Basic Auth challenge.
HandleHttpAuthRequest(const net::test_server::HttpRequest & request)59 std::unique_ptr<net::test_server::HttpResponse> HandleHttpAuthRequest(
60 const net::test_server::HttpRequest& request) {
61 if (request.relative_url != kHttpAuthPath)
62 return nullptr;
63
64 auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
65 if (base::Contains(request.headers, "Authorization")) {
66 http_response->set_code(net::HTTP_OK);
67 http_response->set_content("Success!");
68 } else {
69 http_response->set_code(net::HTTP_UNAUTHORIZED);
70 http_response->AddCustomHeader("WWW-Authenticate",
71 "Basic realm=\"test realm\"");
72 }
73 return http_response;
74 }
75
76 } // namespace
77
78 namespace content {
79
80 class BrowsingDataRemoverImplBrowserTest : public ContentBrowserTest {
81 public:
BrowsingDataRemoverImplBrowserTest()82 BrowsingDataRemoverImplBrowserTest()
83 : ssl_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
84 // Use localhost instead of 127.0.0.1, as HSTS isn't allowed on IPs.
85 ssl_server_.SetSSLConfig(
86 net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
87 ssl_server_.AddDefaultHandlers(GetTestDataFilePath());
88 ssl_server_.RegisterRequestHandler(base::BindRepeating(&HandleHstsRequest));
89 ssl_server_.RegisterRequestHandler(
90 base::BindRepeating(&HandleHttpAuthRequest));
91 EXPECT_TRUE(ssl_server_.Start());
92 }
93
SetUpOnMainThread()94 void SetUpOnMainThread() override {}
95
RemoveAndWait(int remove_mask)96 void RemoveAndWait(int remove_mask) {
97 content::BrowsingDataRemover* remover =
98 content::BrowserContext::GetBrowsingDataRemover(
99 shell()->web_contents()->GetBrowserContext());
100 content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
101 remover->RemoveAndReply(
102 base::Time(), base::Time::Max(), remove_mask,
103 content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
104 &completion_observer);
105 completion_observer.BlockUntilCompletion();
106 }
107
RemoveWithFilterAndWait(int remove_mask,std::unique_ptr<BrowsingDataFilterBuilder> filter)108 void RemoveWithFilterAndWait(
109 int remove_mask,
110 std::unique_ptr<BrowsingDataFilterBuilder> filter) {
111 content::BrowsingDataRemover* remover =
112 content::BrowserContext::GetBrowsingDataRemover(
113 shell()->web_contents()->GetBrowserContext());
114 content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
115 remover->RemoveWithFilterAndReply(
116 base::Time(), base::Time::Max(), remove_mask,
117 content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
118 std::move(filter), &completion_observer);
119 completion_observer.BlockUntilCompletion();
120 }
121
122 // Issues a request for kHstsPath on localhost, and expects it to enable HSTS
123 // for the domain.
IssueRequestThatSetsHsts()124 void IssueRequestThatSetsHsts() {
125 std::unique_ptr<network::ResourceRequest> request =
126 std::make_unique<network::ResourceRequest>();
127 request->url = ssl_server_.GetURL("localhost", kHstsPath);
128
129 SimpleURLLoaderTestHelper loader_helper;
130 std::unique_ptr<network::SimpleURLLoader> loader =
131 network::SimpleURLLoader::Create(std::move(request),
132 TRAFFIC_ANNOTATION_FOR_TESTS);
133 loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
134 url_loader_factory(), loader_helper.GetCallback());
135 loader_helper.WaitForCallback();
136 ASSERT_TRUE(loader_helper.response_body());
137 EXPECT_EQ(kHstsResponseBody, *loader_helper.response_body());
138
139 EXPECT_TRUE(IsHstsSet());
140 }
141
142 // Returns true if HSTS is set on localhost. Does this by issuing an HTTP
143 // request to the embedded test server, and expecting it to be redirected from
144 // HTTP to HTTPS if HSTS is enabled. If the request succeeds, it was sent
145 // over HTTPS, so HSTS is enabled. If it fails, the request was send using
146 // HTTP instead, so HSTS is not enabled for the domain.
IsHstsSet()147 bool IsHstsSet() {
148 GURL url = ssl_server_.GetURL("localhost", "/echo");
149 GURL::Replacements replacements;
150 replacements.SetSchemeStr("http");
151 url = url.ReplaceComponents(replacements);
152 std::unique_ptr<network::ResourceRequest> request =
153 std::make_unique<network::ResourceRequest>();
154 request->url = url;
155
156 std::unique_ptr<network::SimpleURLLoader> loader =
157 network::SimpleURLLoader::Create(std::move(request),
158 TRAFFIC_ANNOTATION_FOR_TESTS);
159 SimpleURLLoaderTestHelper loader_helper;
160 loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
161 url_loader_factory(), loader_helper.GetCallback());
162 loader_helper.WaitForCallback();
163
164 // On success, HSTS was enabled for the domain.
165 if (loader_helper.response_body()) {
166 EXPECT_EQ("Echo", *loader_helper.response_body());
167 return true;
168 }
169
170 // On failure, the server just hangs up, since it didn't receive an SSL
171 // handshake.
172 EXPECT_EQ(net::ERR_EMPTY_RESPONSE, loader->NetError());
173 return false;
174 }
175
176 // Sets HTTP auth cache by making a request with credentials specified in the
177 // URL to a page with an auth challenge.
IssueRequestThatSetsHttpAuthCache()178 void IssueRequestThatSetsHttpAuthCache() {
179 GURL url = ssl_server_.GetURL(kHttpAuthPath);
180 GURL::Replacements replacements;
181 replacements.SetUsernameStr("user");
182 replacements.SetPasswordStr("password");
183 GURL url_with_creds = url.ReplaceComponents(replacements);
184 ASSERT_TRUE(NavigateToURL(shell(), url_with_creds));
185
186 ASSERT_TRUE(IsHttpAuthCacheSet());
187 }
188
189 // Determines if auth cache is populated by seeing if a request to a page with
190 // an auth challenge succeeds.
IsHttpAuthCacheSet()191 bool IsHttpAuthCacheSet() {
192 // Set a login request callback to be used instead of a login dialog since
193 // such a dialog is difficult to control programmatically and doesn't work
194 // on all platforms.
195 bool login_requested = false;
196 ShellContentBrowserClient::Get()->set_login_request_callback(
197 base::BindLambdaForTesting(
198 [&](bool is_main_frame /* unused */) { login_requested = true; }));
199
200 GURL url = ssl_server_.GetURL(kHttpAuthPath);
201 bool navigation_suceeded = NavigateToURL(shell(), url);
202
203 // Because our login request callback does nothing, navigation should
204 // succeed iff login is not needed unless some other unexpected error
205 // occurs.
206 EXPECT_NE(navigation_suceeded, login_requested);
207
208 return !login_requested && navigation_suceeded;
209 }
210
url_loader_factory()211 network::mojom::URLLoaderFactory* url_loader_factory() {
212 return BrowserContext::GetDefaultStoragePartition(
213 shell()->web_contents()->GetBrowserContext())
214 ->GetURLLoaderFactoryForBrowserProcess()
215 .get();
216 }
217
218 private:
219 net::test_server::EmbeddedTestServer ssl_server_;
220 };
221
222 // Verify that TransportSecurityState data is cleared for REMOVE_CACHE.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,ClearTransportSecurityState)223 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
224 ClearTransportSecurityState) {
225 IssueRequestThatSetsHsts();
226
227 RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
228 EXPECT_FALSE(IsHstsSet());
229 }
230
231 // Verify that TransportSecurityState data is not cleared if REMOVE_CACHE is not
232 // set or there is a WHITELIST filter.
233 // TODO(crbug.com/1040065): Add support for filtered deletions and update test.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,PreserveTransportSecurityState)234 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
235 PreserveTransportSecurityState) {
236 IssueRequestThatSetsHsts();
237
238 RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
239 EXPECT_TRUE(IsHstsSet());
240
241 auto filter =
242 BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST);
243 filter->AddRegisterableDomain("foobar.com");
244 RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
245 std::move(filter));
246 EXPECT_TRUE(IsHstsSet());
247 }
248
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,ClearHttpAuthCache)249 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, ClearHttpAuthCache) {
250 ASSERT_FALSE(IsHttpAuthCacheSet());
251 IssueRequestThatSetsHttpAuthCache();
252
253 RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
254 EXPECT_FALSE(IsHttpAuthCacheSet());
255 }
256
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,PreserveHttpAuthCache)257 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
258 PreserveHttpAuthCache) {
259 ASSERT_FALSE(IsHttpAuthCacheSet());
260 IssueRequestThatSetsHttpAuthCache();
261
262 RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
263 EXPECT_TRUE(IsHttpAuthCacheSet());
264 }
265
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,ClearHttpAuthCacheWhenEmpty)266 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
267 ClearHttpAuthCacheWhenEmpty) {
268 ASSERT_FALSE(IsHttpAuthCacheSet());
269
270 RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
271 EXPECT_FALSE(IsHttpAuthCacheSet());
272 }
273
274 } // namespace content
275