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 <memory>
6 #include <utility>
7
8 #include "base/feature_list.h"
9 #include "base/macros.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "base/time/time.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/test/base/in_process_browser_test.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "components/safe_browsing/core/db/safebrowsing.pb.h"
19 #include "components/safe_browsing/core/db/util.h"
20 #include "components/safe_browsing/core/db/v4_embedded_test_server_util.h"
21 #include "components/safe_browsing/core/db/v4_test_util.h"
22 #include "components/safe_browsing/core/features.h"
23 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/test/browser_test.h"
26 #include "content/public/test/test_utils.h"
27 #include "net/cookies/canonical_cookie.h"
28 #include "net/dns/mapped_host_resolver.h"
29 #include "net/dns/mock_host_resolver.h"
30 #include "net/test/embedded_test_server/embedded_test_server.h"
31 #include "services/network/public/mojom/network_context.mojom-forward.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "url/gurl.h"
34
35 namespace {
36
IsShowingInterstitial(content::WebContents * contents)37 bool IsShowingInterstitial(content::WebContents* contents) {
38 security_interstitials::SecurityInterstitialTabHelper* helper =
39 security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
40 contents);
41 return helper &&
42 (helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting() !=
43 nullptr);
44 }
45
GetCookies(network::mojom::NetworkContext * network_context)46 std::vector<net::CanonicalCookie> GetCookies(
47 network::mojom::NetworkContext* network_context) {
48 base::RunLoop run_loop;
49 std::vector<net::CanonicalCookie> cookies;
50 mojo::Remote<network::mojom::CookieManager> cookie_manager_remote;
51 network_context->GetCookieManager(
52 cookie_manager_remote.BindNewPipeAndPassReceiver());
53 cookie_manager_remote->GetAllCookies(base::BindOnce(
54 [](base::RunLoop* run_loop,
55 std::vector<net::CanonicalCookie>* out_cookies,
56 const std::vector<net::CanonicalCookie>& cookies) {
57 *out_cookies = cookies;
58 run_loop->Quit();
59 },
60 &run_loop, &cookies));
61 run_loop.Run();
62 return cookies;
63 }
64
65 } // namespace
66
67 namespace safe_browsing {
68
69 // This harness tests test-only code for correctness. This ensures that other
70 // test classes which want to use the V4 interceptor are testing the right
71 // thing.
72 class V4EmbeddedTestServerBrowserTest : public InProcessBrowserTest {
73 public:
V4EmbeddedTestServerBrowserTest()74 V4EmbeddedTestServerBrowserTest() {}
~V4EmbeddedTestServerBrowserTest()75 ~V4EmbeddedTestServerBrowserTest() override {}
76
SetUp()77 void SetUp() override {
78 // We only need to mock a local database. The tests will use a true real V4
79 // protocol manager.
80 V4Database::RegisterStoreFactoryForTest(
81 std::make_unique<TestV4StoreFactory>());
82
83 auto v4_db_factory = std::make_unique<TestV4DatabaseFactory>();
84 v4_db_factory_ = v4_db_factory.get();
85 V4Database::RegisterDatabaseFactoryForTest(std::move(v4_db_factory));
86
87 secure_embedded_test_server_ = std::make_unique<net::EmbeddedTestServer>(
88 net::EmbeddedTestServer::Type::TYPE_HTTPS);
89
90 InProcessBrowserTest::SetUp();
91 }
92
TearDown()93 void TearDown() override {
94 InProcessBrowserTest::TearDown();
95 V4Database::RegisterStoreFactoryForTest(nullptr);
96 V4Database::RegisterDatabaseFactoryForTest(nullptr);
97 }
98
99 // Only marks the prefix as bad in the local database. The server will respond
100 // with the source of truth.
LocallyMarkPrefixAsBad(const GURL & url,const ListIdentifier & list_id)101 void LocallyMarkPrefixAsBad(const GURL& url, const ListIdentifier& list_id) {
102 FullHash full_hash = V4ProtocolManagerUtil::GetFullHash(url);
103 v4_db_factory_->MarkPrefixAsBad(list_id, full_hash);
104 }
105
106 protected:
107 std::unique_ptr<net::EmbeddedTestServer> secure_embedded_test_server_;
108
109 private:
110 std::unique_ptr<net::MappedHostResolver> mapped_host_resolver_;
111
112 // Owned by the V4Database.
113 TestV4DatabaseFactory* v4_db_factory_ = nullptr;
114
115 DISALLOW_COPY_AND_ASSIGN(V4EmbeddedTestServerBrowserTest);
116 };
117
IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerBrowserTest,SimpleTest)118 IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerBrowserTest, SimpleTest) {
119 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
120
121 const char kMalwarePage[] = "/safe_browsing/malware.html";
122 const GURL bad_url = embedded_test_server()->GetURL(kMalwarePage);
123
124 ThreatMatch match;
125 FullHash full_hash = V4ProtocolManagerUtil::GetFullHash(bad_url);
126 LocallyMarkPrefixAsBad(bad_url, GetUrlMalwareId());
127 match.set_platform_type(GetUrlMalwareId().platform_type());
128 match.set_threat_entry_type(ThreatEntryType::URL);
129 match.set_threat_type(ThreatType::MALWARE_THREAT);
130 match.mutable_threat()->set_hash(full_hash);
131 match.mutable_cache_duration()->set_seconds(300);
132
133 std::map<GURL, safe_browsing::ThreatMatch> response_map{{bad_url, match}};
134 StartRedirectingV4RequestsForTesting(response_map, embedded_test_server());
135 embedded_test_server()->StartAcceptingConnections();
136
137 ui_test_utils::NavigateToURL(browser(), bad_url);
138 content::WebContents* contents =
139 browser()->tab_strip_model()->GetActiveWebContents();
140 EXPECT_TRUE(IsShowingInterstitial(contents));
141 }
142
IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerBrowserTest,WrongFullHash_NoInterstitial)143 IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerBrowserTest,
144 WrongFullHash_NoInterstitial) {
145 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
146
147 const char kMalwarePage[] = "/safe_browsing/malware.html";
148 const GURL bad_url = embedded_test_server()->GetURL(kMalwarePage);
149
150 // Return a different full hash, so there will be no match and no
151 // interstitial.
152 ThreatMatch match;
153 FullHash full_hash =
154 V4ProtocolManagerUtil::GetFullHash(GURL("https://example.test/"));
155 LocallyMarkPrefixAsBad(bad_url, GetUrlMalwareId());
156 match.set_platform_type(GetUrlMalwareId().platform_type());
157 match.set_threat_entry_type(ThreatEntryType::URL);
158 match.set_threat_type(ThreatType::MALWARE_THREAT);
159 match.mutable_threat()->set_hash(full_hash);
160 match.mutable_cache_duration()->set_seconds(300);
161
162 std::map<GURL, safe_browsing::ThreatMatch> response_map{{bad_url, match}};
163 StartRedirectingV4RequestsForTesting(response_map, embedded_test_server());
164 embedded_test_server()->StartAcceptingConnections();
165
166 ui_test_utils::NavigateToURL(browser(), bad_url);
167 content::WebContents* contents =
168 browser()->tab_strip_model()->GetActiveWebContents();
169 EXPECT_FALSE(IsShowingInterstitial(contents));
170 }
171
172 class V4EmbeddedTestServerWithoutCookies
173 : public V4EmbeddedTestServerBrowserTest {
174 public:
V4EmbeddedTestServerWithoutCookies()175 V4EmbeddedTestServerWithoutCookies() {
176 scoped_feature_list_.Reset();
177 scoped_feature_list_.InitWithFeatures({kSafeBrowsingRemoveCookies}, {});
178 }
179
180 private:
181 base::test::ScopedFeatureList scoped_feature_list_;
182 };
183
IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerWithoutCookies,DoesNotSaveCookies)184 IN_PROC_BROWSER_TEST_F(V4EmbeddedTestServerWithoutCookies, DoesNotSaveCookies) {
185 ASSERT_TRUE(secure_embedded_test_server_->InitializeAndListen());
186 const char kMalwarePage[] = "/safe_browsing/malware.html";
187 const GURL bad_url = secure_embedded_test_server_->GetURL(kMalwarePage);
188
189 ThreatMatch match;
190 FullHash full_hash = V4ProtocolManagerUtil::GetFullHash(bad_url);
191 LocallyMarkPrefixAsBad(bad_url, GetUrlMalwareId());
192 match.set_platform_type(GetUrlMalwareId().platform_type());
193 match.set_threat_entry_type(ThreatEntryType::URL);
194 match.set_threat_type(ThreatType::MALWARE_THREAT);
195 match.mutable_threat()->set_hash(full_hash);
196 match.mutable_cache_duration()->set_seconds(0);
197
198 std::map<GURL, safe_browsing::ThreatMatch> response_map{{bad_url, match}};
199 StartRedirectingV4RequestsForTesting(
200 response_map, secure_embedded_test_server_.get(),
201 /*delay_map=*/std::map<GURL, base::TimeDelta>(),
202 /*serve_cookies=*/true);
203 secure_embedded_test_server_->StartAcceptingConnections();
204
205 EXPECT_EQ(GetCookies(
206 g_browser_process->safe_browsing_service()->GetNetworkContext())
207 .size(),
208 0u);
209
210 ui_test_utils::NavigateToURL(browser(), bad_url);
211
212 EXPECT_EQ(GetCookies(
213 g_browser_process->safe_browsing_service()->GetNetworkContext())
214 .size(),
215 0u);
216 }
217
218 } // namespace safe_browsing
219