1 // Copyright 2019 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 "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
6 
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/optional.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/task/post_task.h"
12 #include "base/task/thread_pool.h"
13 #include "base/task_runner_util.h"
14 #include "chrome/browser/enterprise/connectors/content_analysis_delegate.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_contents_view_delegate.h"
19 #include "content/public/common/drop_data.h"
20 
21 namespace {
22 
CompletionCallback(content::WebContentsViewDelegate::DropCompletionCallback callback,const enterprise_connectors::ContentAnalysisDelegate::Data & data,const enterprise_connectors::ContentAnalysisDelegate::Result & result)23 void CompletionCallback(
24     content::WebContentsViewDelegate::DropCompletionCallback callback,
25     const enterprise_connectors::ContentAnalysisDelegate::Data& data,
26     const enterprise_connectors::ContentAnalysisDelegate::Result& result) {
27   // If any result is negative, block the drop.
28   const auto all_true_fn = [](const auto& vec) {
29     return std::all_of(vec.cbegin(), vec.cend(), [](bool b) { return b; });
30   };
31   bool all_true =
32       all_true_fn(result.text_results) && all_true_fn(result.paths_results);
33 
34   std::move(callback).Run(
35       all_true
36           ? content::WebContentsViewDelegate::DropCompletionResult::kContinue
37           : content::WebContentsViewDelegate::DropCompletionResult::kAbort);
38 }
39 
GetPathsToScan(content::WebContents * web_contents,const content::DropData & drop_data,enterprise_connectors::ContentAnalysisDelegate::Data data)40 enterprise_connectors::ContentAnalysisDelegate::Data GetPathsToScan(
41     content::WebContents* web_contents,
42     const content::DropData& drop_data,
43     enterprise_connectors::ContentAnalysisDelegate::Data data) {
44   for (const auto& file : drop_data.filenames) {
45     base::File::Info info;
46 
47     // Ignore the path if it's a symbolic link.
48     if (!base::GetFileInfo(file.path, &info) || info.is_symbolic_link)
49       continue;
50 
51     // If the file is a directory, recursively add the files it holds to |data|.
52     if (info.is_directory) {
53       base::FileEnumerator file_enumerator(file.path, /*recursive=*/true,
54                                            base::FileEnumerator::FILES);
55       for (base::FilePath sub_path = file_enumerator.Next(); !sub_path.empty();
56            sub_path = file_enumerator.Next()) {
57         data.paths.push_back(sub_path);
58       }
59     } else {
60       data.paths.push_back(file.path);
61     }
62   }
63 
64   return data;
65 }
66 
ScanData(content::WebContents * web_contents,content::WebContentsViewDelegate::DropCompletionCallback callback,enterprise_connectors::ContentAnalysisDelegate::Data data)67 void ScanData(content::WebContents* web_contents,
68               content::WebContentsViewDelegate::DropCompletionCallback callback,
69               enterprise_connectors::ContentAnalysisDelegate::Data data) {
70   enterprise_connectors::ContentAnalysisDelegate::CreateForWebContents(
71       web_contents, std::move(data),
72       base::BindOnce(&CompletionCallback, std::move(callback)),
73       safe_browsing::DeepScanAccessPoint::DRAG_AND_DROP);
74 }
75 
76 }  // namespace
77 
HandleOnPerformDrop(content::WebContents * web_contents,const content::DropData & drop_data,content::WebContentsViewDelegate::DropCompletionCallback callback)78 void HandleOnPerformDrop(
79     content::WebContents* web_contents,
80     const content::DropData& drop_data,
81     content::WebContentsViewDelegate::DropCompletionCallback callback) {
82   enterprise_connectors::ContentAnalysisDelegate::Data data;
83   Profile* profile =
84       Profile::FromBrowserContext(web_contents->GetBrowserContext());
85   auto connector =
86       drop_data.filenames.empty()
87           ? enterprise_connectors::AnalysisConnector::BULK_DATA_ENTRY
88           : enterprise_connectors::AnalysisConnector::FILE_ATTACHED;
89   if (!enterprise_connectors::ContentAnalysisDelegate::IsEnabled(
90           profile, web_contents->GetLastCommittedURL(), &data, connector)) {
91     std::move(callback).Run(
92         content::WebContentsViewDelegate::DropCompletionResult::kContinue);
93     return;
94   }
95 
96   // Collect the data that needs to be scanned.
97   if (!drop_data.url_title.empty())
98     data.text.push_back(drop_data.url_title);
99   if (drop_data.text)
100     data.text.push_back(*drop_data.text);
101   if (drop_data.html)
102     data.text.push_back(*drop_data.html);
103   if (!drop_data.file_contents.empty())
104     data.text.push_back(base::UTF8ToUTF16(drop_data.file_contents));
105 
106   if (drop_data.filenames.empty()) {
107     ScanData(web_contents, std::move(callback), std::move(data));
108   } else {
109     base::ThreadPool::PostTaskAndReplyWithResult(
110         FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
111         base::BindOnce(&GetPathsToScan, web_contents, std::move(drop_data),
112                        std::move(data)),
113         base::BindOnce(&ScanData, web_contents, std::move(callback)));
114   }
115 }
116