1 // Copyright (c) 2012 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 "base/bind.h"
6 #include "base/command_line.h"
7 #include "base/files/file_path.h"
8 #include "base/macros.h"
9 #include "build/build_config.h"
10 #include "chrome/browser/download/download_danger_prompt.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
13 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_commands.h"
16 #include "chrome/browser/ui/browser_tabstrip.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/browser/ui/test/test_browser_dialog.h"
19 #include "chrome/test/base/in_process_browser_test.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "components/download/public/common/mock_download_item.h"
22 #include "components/safe_browsing/core/db/database_manager.h"
23 #include "components/safe_browsing/core/proto/csd.pb.h"
24 #include "content/public/browser/download_item_utils.h"
25 #include "content/public/test/browser_test.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "url/gurl.h"
29
30 using ::testing::_;
31 using ::testing::ByRef;
32 using ::testing::Eq;
33 using ::testing::Return;
34 using ::testing::ReturnRef;
35 using ::testing::SaveArg;
36
37 namespace safe_browsing {
38
39 namespace {
40
41 const char kTestDownloadUrl[] = "http://evildownload.com";
42 const char kDownloadResponseToken[] = "default_token";
43
44 } // namespace
45
46 class DownloadDangerPromptTest : public InProcessBrowserTest {
47 public:
DownloadDangerPromptTest()48 DownloadDangerPromptTest()
49 : prompt_(nullptr),
50 expected_action_(DownloadDangerPrompt::CANCEL),
51 did_receive_callback_(false),
52 test_safe_browsing_factory_(
53 std::make_unique<TestSafeBrowsingServiceFactory>()) {}
54
~DownloadDangerPromptTest()55 ~DownloadDangerPromptTest() override {}
56
SetUp()57 void SetUp() override {
58 SafeBrowsingService::RegisterFactory(test_safe_browsing_factory_.get());
59 InProcessBrowserTest::SetUp();
60 }
61
TearDown()62 void TearDown() override {
63 SafeBrowsingService::RegisterFactory(nullptr);
64 InProcessBrowserTest::TearDown();
65 }
66
67 // Opens a new tab and waits for navigations to finish. If there are pending
68 // navigations, the constrained prompt might be dismissed when the navigation
69 // completes.
OpenNewTab()70 void OpenNewTab() {
71 ui_test_utils::NavigateToURLWithDisposition(
72 browser(), GURL("about:blank"),
73 WindowOpenDisposition::NEW_FOREGROUND_TAB,
74 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
75 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
76 }
77
SetUpExpectations(const DownloadDangerPrompt::Action & expected_action,const download::DownloadDangerType & danger_type,const ClientDownloadResponse::Verdict & download_verdict,const std::string & token,bool from_download_api)78 void SetUpExpectations(
79 const DownloadDangerPrompt::Action& expected_action,
80 const download::DownloadDangerType& danger_type,
81 const ClientDownloadResponse::Verdict& download_verdict,
82 const std::string& token,
83 bool from_download_api) {
84 did_receive_callback_ = false;
85 expected_action_ = expected_action;
86 SetUpDownloadItemExpectations(danger_type, token);
87 SetUpSafeBrowsingReportExpectations(
88 expected_action == DownloadDangerPrompt::ACCEPT,
89 download_verdict,
90 token,
91 from_download_api);
92 CreatePrompt(from_download_api);
93 }
94
VerifyExpectations(bool should_send_report)95 void VerifyExpectations(bool should_send_report) {
96 content::RunAllPendingInMessageLoop();
97 // At the end of each test, we expect no more activity from the prompt. The
98 // prompt shouldn't exist anymore either.
99 EXPECT_TRUE(did_receive_callback_);
100 EXPECT_FALSE(prompt_);
101
102 if (should_send_report) {
103 EXPECT_EQ(expected_serialized_report_,
104 test_safe_browsing_factory_->test_safe_browsing_service()
105 ->serilized_download_report());
106 } else {
107 EXPECT_TRUE(test_safe_browsing_factory_->test_safe_browsing_service()
108 ->serilized_download_report()
109 .empty());
110 }
111 testing::Mock::VerifyAndClearExpectations(&download_);
112 test_safe_browsing_factory_->test_safe_browsing_service()
113 ->ClearDownloadReport();
114 }
115
SimulatePromptAction(DownloadDangerPrompt::Action action)116 void SimulatePromptAction(DownloadDangerPrompt::Action action) {
117 prompt_->InvokeActionForTesting(action);
118 }
119
download()120 download::MockDownloadItem& download() { return download_; }
121
prompt()122 DownloadDangerPrompt* prompt() { return prompt_; }
123
124 private:
SetUpDownloadItemExpectations(const download::DownloadDangerType & danger_type,const std::string & token)125 void SetUpDownloadItemExpectations(
126 const download::DownloadDangerType& danger_type,
127 const std::string& token) {
128 EXPECT_CALL(download_, GetFileNameToReportUser()).WillRepeatedly(Return(
129 base::FilePath(FILE_PATH_LITERAL("evil.exe"))));
130 EXPECT_CALL(download_, GetDangerType()).WillRepeatedly(Return(danger_type));
131 auto token_obj =
132 std::make_unique<DownloadProtectionService::DownloadPingToken>(token);
133 download_.SetUserData(DownloadProtectionService::kDownloadPingTokenKey,
134 std::move(token_obj));
135 }
136
SetUpSafeBrowsingReportExpectations(bool did_proceed,const ClientDownloadResponse::Verdict & download_verdict,const std::string & token,bool from_download_api)137 void SetUpSafeBrowsingReportExpectations(
138 bool did_proceed,
139 const ClientDownloadResponse::Verdict& download_verdict,
140 const std::string& token,
141 bool from_download_api) {
142 ClientSafeBrowsingReportRequest expected_report;
143 expected_report.set_url(GURL(kTestDownloadUrl).spec());
144 if (from_download_api)
145 expected_report.set_type(
146 ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_BY_API);
147 else
148 expected_report.set_type(
149 ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_RECOVERY);
150 expected_report.set_download_verdict(download_verdict);
151 expected_report.set_did_proceed(did_proceed);
152 if (!token.empty())
153 expected_report.set_token(token);
154 expected_report.SerializeToString(&expected_serialized_report_);
155 }
156
CreatePrompt(bool from_download_api)157 void CreatePrompt(bool from_download_api) {
158 prompt_ = DownloadDangerPrompt::Create(
159 &download_,
160 browser()->tab_strip_model()->GetActiveWebContents(),
161 from_download_api,
162 base::Bind(&DownloadDangerPromptTest::PromptCallback,
163 base::Unretained(this)));
164 content::RunAllPendingInMessageLoop();
165 }
166
PromptCallback(DownloadDangerPrompt::Action action)167 void PromptCallback(DownloadDangerPrompt::Action action) {
168 EXPECT_FALSE(did_receive_callback_);
169 EXPECT_EQ(expected_action_, action);
170 did_receive_callback_ = true;
171 prompt_ = nullptr;
172 }
173
174 download::MockDownloadItem download_;
175 DownloadDangerPrompt* prompt_;
176 DownloadDangerPrompt::Action expected_action_;
177 bool did_receive_callback_;
178 std::unique_ptr<TestSafeBrowsingServiceFactory> test_safe_browsing_factory_;
179 std::string expected_serialized_report_;
180
181 DISALLOW_COPY_AND_ASSIGN(DownloadDangerPromptTest);
182 };
183
184 // Disabled for flaky timeouts on Windows. crbug.com/446696
185 #if defined(OS_WIN)
186 #define MAYBE_TestAll DISABLED_TestAll
187 #else
188 #define MAYBE_TestAll TestAll
189 #endif
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptTest,MAYBE_TestAll)190 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptTest, MAYBE_TestAll) {
191 GURL download_url(kTestDownloadUrl);
192 ON_CALL(download(), GetURL()).WillByDefault(ReturnRef(download_url));
193 ON_CALL(download(), GetReferrerUrl())
194 .WillByDefault(ReturnRef(GURL::EmptyGURL()));
195 content::DownloadItemUtils::AttachInfo(&download(), browser()->profile(),
196 nullptr);
197 base::FilePath empty_file_path;
198 ON_CALL(download(), GetTargetFilePath())
199 .WillByDefault(ReturnRef(empty_file_path));
200
201 OpenNewTab();
202
203 // Clicking the Accept button should invoke the ACCEPT action.
204 SetUpExpectations(DownloadDangerPrompt::ACCEPT,
205 download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
206 ClientDownloadResponse::DANGEROUS, kDownloadResponseToken,
207 false);
208 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
209 SimulatePromptAction(DownloadDangerPrompt::ACCEPT);
210 VerifyExpectations(true);
211
212 // Clicking the Cancel button should invoke the CANCEL action.
213 SetUpExpectations(DownloadDangerPrompt::CANCEL,
214 download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT,
215 ClientDownloadResponse::UNCOMMON, std::string(), false);
216 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
217 SimulatePromptAction(DownloadDangerPrompt::CANCEL);
218 VerifyExpectations(true);
219
220 // If the download is no longer dangerous (because it was accepted), the
221 // dialog should DISMISS itself.
222 SetUpExpectations(DownloadDangerPrompt::DISMISS,
223 download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED,
224 ClientDownloadResponse::POTENTIALLY_UNWANTED,
225 kDownloadResponseToken, false);
226 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(false));
227 download().NotifyObserversDownloadUpdated();
228 VerifyExpectations(false);
229
230 // If the download is in a terminal state then the dialog should DISMISS
231 // itself.
232 SetUpExpectations(DownloadDangerPrompt::DISMISS,
233 download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST,
234 ClientDownloadResponse::DANGEROUS_HOST,
235 kDownloadResponseToken, false);
236 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
237 EXPECT_CALL(download(), IsDone()).WillRepeatedly(Return(true));
238 download().NotifyObserversDownloadUpdated();
239 VerifyExpectations(false);
240
241 // If the download is dangerous and is not in a terminal state, don't dismiss
242 // the dialog.
243 SetUpExpectations(DownloadDangerPrompt::ACCEPT,
244 download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT,
245 ClientDownloadResponse::DANGEROUS, kDownloadResponseToken,
246 false);
247 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
248 EXPECT_CALL(download(), IsDone()).WillRepeatedly(Return(false));
249 download().NotifyObserversDownloadUpdated();
250 EXPECT_TRUE(prompt());
251 SimulatePromptAction(DownloadDangerPrompt::ACCEPT);
252 VerifyExpectations(true);
253
254 // If the download is not dangerous, no report will be sent.
255 SetUpExpectations(DownloadDangerPrompt::ACCEPT,
256 download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
257 ClientDownloadResponse::SAFE, kDownloadResponseToken,
258 false);
259 SimulatePromptAction(DownloadDangerPrompt::ACCEPT);
260 VerifyExpectations(false);
261
262 // If the containing tab is closed, the dialog should DISMISS itself.
263 OpenNewTab();
264 SetUpExpectations(DownloadDangerPrompt::DISMISS,
265 download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
266 ClientDownloadResponse::DANGEROUS, kDownloadResponseToken,
267 false);
268 chrome::CloseTab(browser());
269 VerifyExpectations(false);
270
271 // If file is downloaded through download api, a confirm download dialog
272 // instead of a recovery dialog is shown. Clicking the Accept button should
273 // invoke the ACCEPT action, a report will be sent with type
274 // DANGEROUS_DOWNLOAD_BY_API.
275 SetUpExpectations(DownloadDangerPrompt::ACCEPT,
276 download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
277 ClientDownloadResponse::DANGEROUS, kDownloadResponseToken,
278 true);
279 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
280 SimulatePromptAction(DownloadDangerPrompt::ACCEPT);
281 VerifyExpectations(true);
282
283 // If file is downloaded through download api, a confirm download dialog
284 // instead of a recovery dialog is shown. Clicking the Cancel button should
285 // invoke the CANCEL action, a report will be sent with type
286 // DANGEROUS_DOWNLOAD_BY_API.
287 SetUpExpectations(DownloadDangerPrompt::CANCEL,
288 download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT,
289 ClientDownloadResponse::UNCOMMON, std::string(), true);
290 EXPECT_CALL(download(), IsDangerous()).WillRepeatedly(Return(true));
291 SimulatePromptAction(DownloadDangerPrompt::CANCEL);
292 VerifyExpectations(true);
293 }
294
295 // Class for testing interactive dialogs.
296 class DownloadDangerPromptBrowserTest : public DialogBrowserTest {
297 protected:
298 enum InvocationType { USER_INITIATED, FROM_DOWNLOAD_API };
DownloadDangerPromptBrowserTest()299 DownloadDangerPromptBrowserTest() : download_url_(kTestDownloadUrl) {}
300
RunTest(download::DownloadDangerType danger_type,InvocationType invocation_type)301 void RunTest(download::DownloadDangerType danger_type,
302 InvocationType invocation_type) {
303 danger_type_ = danger_type;
304 invocation_type_ = invocation_type;
305
306 ShowAndVerifyUi();
307 }
308
309 private:
ShowUi(const std::string & name)310 void ShowUi(const std::string& name) override {
311 ON_CALL(download_, GetURL()).WillByDefault(ReturnRef(download_url_));
312 ON_CALL(download_, GetReferrerUrl())
313 .WillByDefault(ReturnRef(GURL::EmptyGURL()));
314 ON_CALL(download_, GetTargetFilePath())
315 .WillByDefault(ReturnRef(empty_file_path_));
316 ON_CALL(download_, IsDangerous()).WillByDefault(Return(true));
317 ON_CALL(download_, GetFileNameToReportUser())
318 .WillByDefault(Return(base::FilePath(FILE_PATH_LITERAL("evil.exe"))));
319
320 // Set up test-specific parameters
321 ON_CALL(download_, GetDangerType()).WillByDefault(Return(danger_type_));
322 content::DownloadItemUtils::AttachInfo(&download_, browser()->profile(),
323 nullptr);
324 DownloadDangerPrompt::Create(
325 &download_, browser()->tab_strip_model()->GetActiveWebContents(),
326 invocation_type_ == FROM_DOWNLOAD_API, DownloadDangerPrompt::OnDone());
327 }
328
329 const GURL download_url_;
330 const base::FilePath empty_file_path_;
331
332 download::DownloadDangerType danger_type_;
333 InvocationType invocation_type_;
334 download::MockDownloadItem download_;
335
336 DISALLOW_COPY_AND_ASSIGN(DownloadDangerPromptBrowserTest);
337 };
338
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_DangerousFile)339 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
340 InvokeUi_DangerousFile) {
341 RunTest(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE, USER_INITIATED);
342 }
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_DangerousFileFromApi)343 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
344 InvokeUi_DangerousFileFromApi) {
345 RunTest(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE, FROM_DOWNLOAD_API);
346 }
347
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_DangerousUrl)348 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest, InvokeUi_DangerousUrl) {
349 RunTest(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, USER_INITIATED);
350 }
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_DangerousUrlFromApi)351 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
352 InvokeUi_DangerousUrlFromApi) {
353 RunTest(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, FROM_DOWNLOAD_API);
354 }
355
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_UncommonContent)356 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
357 InvokeUi_UncommonContent) {
358 RunTest(download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT, USER_INITIATED);
359 }
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_UncommonContentFromApi)360 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
361 InvokeUi_UncommonContentFromApi) {
362 RunTest(download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT, FROM_DOWNLOAD_API);
363 }
364
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_PotentiallyUnwanted)365 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
366 InvokeUi_PotentiallyUnwanted) {
367 RunTest(download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED, USER_INITIATED);
368 }
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,InvokeUi_PotentiallyUnwantedFromApi)369 IN_PROC_BROWSER_TEST_F(DownloadDangerPromptBrowserTest,
370 InvokeUi_PotentiallyUnwantedFromApi) {
371 RunTest(download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED,
372 FROM_DOWNLOAD_API);
373 }
374
375 } // namespace safe_browsing
376