1 // Copyright 2015 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 <map>
6 #include <vector>
7 
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_util.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "build/build_config.h"
16 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/test/ppapi/ppapi_test.h"
19 #include "chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h"
20 #include "components/safe_browsing/buildflags.h"
21 #include "components/services/quarantine/test_support.h"
22 #include "content/public/test/browser_test.h"
23 #include "ppapi/shared_impl/test_utils.h"
24 
25 #if BUILDFLAG(FULL_SAFE_BROWSING)
26 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
27 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
28 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
29 #include "components/safe_browsing/core/db/test_database_manager.h"
30 
31 using safe_browsing::DownloadProtectionService;
32 using safe_browsing::SafeBrowsingService;
33 #endif
34 
35 namespace {
36 
37 class PPAPIFileChooserTest : public OutOfProcessPPAPITest {};
38 
39 #if BUILDFLAG(FULL_SAFE_BROWSING)
40 
41 struct SafeBrowsingTestConfiguration {
42   std::map<base::FilePath::StringType, safe_browsing::DownloadCheckResult>
43       result_map;
44   safe_browsing::DownloadCheckResult default_result =
45       safe_browsing::DownloadCheckResult::SAFE;
46 };
47 
48 class FakeDownloadProtectionService : public DownloadProtectionService {
49  public:
FakeDownloadProtectionService(const SafeBrowsingTestConfiguration * test_config)50   explicit FakeDownloadProtectionService(
51       const SafeBrowsingTestConfiguration* test_config)
52       : DownloadProtectionService(nullptr), test_configuration_(test_config) {}
53 
CheckPPAPIDownloadRequest(const GURL & requestor_url,const GURL & initiating_frame_url_unused,content::WebContents * web_contents_unused,const base::FilePath & default_file_path,const std::vector<base::FilePath::StringType> & alternate_extensions,Profile *,safe_browsing::CheckDownloadCallback callback)54   void CheckPPAPIDownloadRequest(
55       const GURL& requestor_url,
56       const GURL& initiating_frame_url_unused,
57       content::WebContents* web_contents_unused,
58       const base::FilePath& default_file_path,
59       const std::vector<base::FilePath::StringType>& alternate_extensions,
60       Profile* /* profile */,
61       safe_browsing::CheckDownloadCallback callback) override {
62     const auto iter =
63         test_configuration_->result_map.find(default_file_path.Extension());
64     if (iter != test_configuration_->result_map.end()) {
65       std::move(callback).Run(iter->second);
66       return;
67     }
68 
69     for (const auto& extension : alternate_extensions) {
70       EXPECT_EQ(base::FilePath::kExtensionSeparator, extension[0]);
71       const auto iter = test_configuration_->result_map.find(extension);
72       if (iter != test_configuration_->result_map.end()) {
73         std::move(callback).Run(iter->second);
74         return;
75       }
76     }
77 
78     std::move(callback).Run(test_configuration_->default_result);
79   }
80 
81  private:
82   const SafeBrowsingTestConfiguration* test_configuration_;
83 };
84 
85 class TestSafeBrowsingService : public safe_browsing::TestSafeBrowsingService {
86  public:
TestSafeBrowsingService(const SafeBrowsingTestConfiguration * config)87   explicit TestSafeBrowsingService(const SafeBrowsingTestConfiguration* config)
88       : test_configuration_(config) {
89     services_delegate_ =
90         safe_browsing::ServicesDelegate::CreateForTest(this, this);
91   }
92 
93  private:
94   // safe_browsing::ServicesDelegate::ServicesCreator
CanCreateDownloadProtectionService()95   bool CanCreateDownloadProtectionService() override { return true; }
CreateDownloadProtectionService()96   DownloadProtectionService* CreateDownloadProtectionService() override {
97     return new FakeDownloadProtectionService(test_configuration_);
98   }
99 
100   const SafeBrowsingTestConfiguration* test_configuration_;
101 };
102 
103 class TestSafeBrowsingServiceFactory
104     : public safe_browsing::SafeBrowsingServiceFactory {
105  public:
TestSafeBrowsingServiceFactory(const SafeBrowsingTestConfiguration * config)106   explicit TestSafeBrowsingServiceFactory(
107       const SafeBrowsingTestConfiguration* config)
108       : test_configuration_(config) {}
109 
CreateSafeBrowsingService()110   SafeBrowsingService* CreateSafeBrowsingService() override {
111     SafeBrowsingService* service =
112         new TestSafeBrowsingService(test_configuration_);
113     return service;
114   }
115 
116  private:
117   const SafeBrowsingTestConfiguration* test_configuration_;
118 };
119 
120 class PPAPIFileChooserTestWithSBService : public PPAPIFileChooserTest {
121  public:
PPAPIFileChooserTestWithSBService()122   PPAPIFileChooserTestWithSBService()
123       : safe_browsing_service_factory_(&safe_browsing_test_configuration_) {}
124 
SetUp()125   void SetUp() override {
126     SafeBrowsingService::RegisterFactory(&safe_browsing_service_factory_);
127     PPAPIFileChooserTest::SetUp();
128   }
TearDown()129   void TearDown() override {
130     PPAPIFileChooserTest::TearDown();
131     SafeBrowsingService::RegisterFactory(nullptr);
132   }
133 
134  protected:
135   SafeBrowsingTestConfiguration safe_browsing_test_configuration_;
136 
137  private:
138   TestSafeBrowsingServiceFactory safe_browsing_service_factory_;
139 };
140 
141 #endif
142 
143 }  // namespace
144 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_Open_Success)145 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_Open_Success) {
146   const char kContents[] = "Hello from browser";
147   base::ScopedAllowBlockingForTesting allow_blocking;
148   base::ScopedTempDir temp_dir;
149   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
150 
151   base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");
152   ASSERT_EQ(
153       static_cast<int>(sizeof(kContents) - 1),
154       base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1));
155 
156   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
157   file_info_list.push_back(
158       ui::SelectedFileInfo(existing_filename, existing_filename));
159   PPAPITestSelectFileDialogFactory test_dialog_factory(
160       PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
161   RunTestViaHTTP("FileChooser_OpenSimple");
162 }
163 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_Open_Cancel)164 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_Open_Cancel) {
165   PPAPITestSelectFileDialogFactory test_dialog_factory(
166       PPAPITestSelectFileDialogFactory::CANCEL,
167       PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
168   RunTestViaHTTP("FileChooser_OpenCancel");
169 }
170 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_SaveAs_Success)171 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_SaveAs_Success) {
172   base::ScopedAllowBlockingForTesting allow_blocking;
173   base::ScopedTempDir temp_dir;
174   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
175   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
176 
177   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
178   file_info_list.push_back(
179       ui::SelectedFileInfo(suggested_filename, suggested_filename));
180   PPAPITestSelectFileDialogFactory test_dialog_factory(
181       PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
182 
183   RunTestViaHTTP("FileChooser_SaveAsSafeDefaultName");
184   ASSERT_TRUE(base::PathExists(suggested_filename));
185 }
186 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_SaveAs_SafeDefaultName)187 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,
188                        FileChooser_SaveAs_SafeDefaultName) {
189   base::ScopedAllowBlockingForTesting allow_blocking;
190   base::ScopedTempDir temp_dir;
191   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
192   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
193 
194   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
195   file_info_list.push_back(
196       ui::SelectedFileInfo(suggested_filename, suggested_filename));
197   PPAPITestSelectFileDialogFactory test_dialog_factory(
198       PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
199 
200   RunTestViaHTTP("FileChooser_SaveAsSafeDefaultName");
201   base::FilePath actual_filename =
202       temp_dir.GetPath().AppendASCII("innocuous.txt");
203 
204   ASSERT_TRUE(base::PathExists(actual_filename));
205   std::string file_contents;
206   ASSERT_TRUE(base::ReadFileToString(actual_filename, &file_contents));
207   EXPECT_EQ("Hello from PPAPI", file_contents);
208 }
209 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_SaveAs_UnsafeDefaultName)210 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,
211                        FileChooser_SaveAs_UnsafeDefaultName) {
212   base::ScopedAllowBlockingForTesting allow_blocking;
213   base::ScopedTempDir temp_dir;
214   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
215   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
216 
217   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
218   file_info_list.push_back(
219       ui::SelectedFileInfo(suggested_filename, suggested_filename));
220   PPAPITestSelectFileDialogFactory test_dialog_factory(
221       PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
222 
223   RunTestViaHTTP("FileChooser_SaveAsUnsafeDefaultName");
224   base::FilePath actual_filename =
225       temp_dir.GetPath().AppendASCII("unsafe.txt_");
226 
227   ASSERT_TRUE(base::PathExists(actual_filename));
228   std::string file_contents;
229   ASSERT_TRUE(base::ReadFileToString(actual_filename, &file_contents));
230   EXPECT_EQ("Hello from PPAPI", file_contents);
231 }
232 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_SaveAs_Cancel)233 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_SaveAs_Cancel) {
234   PPAPITestSelectFileDialogFactory test_dialog_factory(
235       PPAPITestSelectFileDialogFactory::CANCEL,
236       PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
237   RunTestViaHTTP("FileChooser_SaveAsCancel");
238 }
239 
240 #if defined(OS_WIN)
241 // On Windows, tests that a file downloaded via PPAPI FileChooser API has the
242 // mark-of-the-web. The PPAPI FileChooser implementation invokes QuarantineFile
243 // in order to mark the file as being downloaded from the web as soon as the
244 // file is created. This MotW prevents the file being opened without due
245 // security warnings if the file is executable.
246 //
247 // This test is disabled on Mac even though quarantine support is implemented on
248 // Mac. New files created on Mac only receive the MOTW if the process creating
249 // them comes from an app with LSFileQuarantineEnabled in its Info.plist. Since
250 // PPAPI delegates file creation to the browser process, which in browser_tests
251 // is not part of an app, files downloaded by them do not receive the MOTW and
252 // this test fails.
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest,FileChooser_Quarantine)253 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_Quarantine) {
254   base::ScopedAllowBlockingForTesting allow_blocking;
255   base::ScopedTempDir temp_dir;
256   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
257   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
258 
259   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
260   file_info_list.push_back(
261       ui::SelectedFileInfo(suggested_filename, suggested_filename));
262   PPAPITestSelectFileDialogFactory test_dialog_factory(
263       PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
264 
265   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableAllowed");
266   base::FilePath actual_filename =
267       temp_dir.GetPath().AppendASCII("dangerous.exe");
268 
269   ASSERT_TRUE(base::PathExists(actual_filename));
270   EXPECT_TRUE(quarantine::IsFileQuarantined(actual_filename, GURL(), GURL()));
271 }
272 #endif  // defined(OS_WIN) || defined(OS_MAC)
273 
274 #if BUILDFLAG(FULL_SAFE_BROWSING)
275 // These tests only make sense when SafeBrowsing is enabled. They verify
276 // that files written via the FileChooser_Trusted API are properly passed
277 // through Safe Browsing.
278 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,FileChooser_SaveAs_DangerousExecutable_Allowed)279 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,
280                        FileChooser_SaveAs_DangerousExecutable_Allowed) {
281   base::ScopedAllowBlockingForTesting allow_blocking;
282   safe_browsing_test_configuration_.default_result =
283       safe_browsing::DownloadCheckResult::DANGEROUS;
284   safe_browsing_test_configuration_.result_map.insert(
285       std::make_pair(base::FilePath::StringType(FILE_PATH_LITERAL(".exe")),
286                      safe_browsing::DownloadCheckResult::SAFE));
287 
288   base::ScopedTempDir temp_dir;
289   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
290   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
291 
292   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
293   file_info_list.push_back(
294       ui::SelectedFileInfo(suggested_filename, suggested_filename));
295   PPAPITestSelectFileDialogFactory test_dialog_factory(
296       PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
297 
298   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableAllowed");
299   base::FilePath actual_filename =
300       temp_dir.GetPath().AppendASCII("dangerous.exe");
301 
302   ASSERT_TRUE(base::PathExists(actual_filename));
303   std::string file_contents;
304   ASSERT_TRUE(base::ReadFileToString(actual_filename, &file_contents));
305   EXPECT_EQ("Hello from PPAPI", file_contents);
306 }
307 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,FileChooser_SaveAs_DangerousExecutable_Disallowed)308 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,
309                        FileChooser_SaveAs_DangerousExecutable_Disallowed) {
310   safe_browsing_test_configuration_.default_result =
311       safe_browsing::DownloadCheckResult::SAFE;
312   safe_browsing_test_configuration_.result_map.insert(
313       std::make_pair(base::FilePath::StringType(FILE_PATH_LITERAL(".exe")),
314                      safe_browsing::DownloadCheckResult::DANGEROUS));
315 
316   PPAPITestSelectFileDialogFactory test_dialog_factory(
317       PPAPITestSelectFileDialogFactory::NOT_REACHED,
318       PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
319   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableDisallowed");
320 }
321 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,FileChooser_SaveAs_DangerousExtensionList_Disallowed)322 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,
323                        FileChooser_SaveAs_DangerousExtensionList_Disallowed) {
324   safe_browsing_test_configuration_.default_result =
325       safe_browsing::DownloadCheckResult::SAFE;
326   safe_browsing_test_configuration_.result_map.insert(
327       std::make_pair(base::FilePath::StringType(FILE_PATH_LITERAL(".exe")),
328                      safe_browsing::DownloadCheckResult::DANGEROUS));
329 
330   PPAPITestSelectFileDialogFactory test_dialog_factory(
331       PPAPITestSelectFileDialogFactory::NOT_REACHED,
332       PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
333   RunTestViaHTTP("FileChooser_SaveAsDangerousExtensionListDisallowed");
334 }
335 
IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,FileChooser_Open_NotBlockedBySafeBrowsing)336 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTestWithSBService,
337                        FileChooser_Open_NotBlockedBySafeBrowsing) {
338   base::ScopedAllowBlockingForTesting allow_blocking;
339   const char kContents[] = "Hello from browser";
340   base::ScopedTempDir temp_dir;
341   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
342 
343   base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");
344   ASSERT_EQ(
345       static_cast<int>(sizeof(kContents) - 1),
346       base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1));
347 
348   safe_browsing_test_configuration_.default_result =
349       safe_browsing::DownloadCheckResult::DANGEROUS;
350 
351   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
352   file_info_list.push_back(
353       ui::SelectedFileInfo(existing_filename, existing_filename));
354   PPAPITestSelectFileDialogFactory test_dialog_factory(
355       PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
356   RunTestViaHTTP("FileChooser_OpenSimple");
357 }
358 
359 #endif  // FULL_SAFE_BROWSING
360