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