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