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