Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(789)

Side by Side Diff: chrome/browser/safe_browsing/last_download_finder.cc

Issue 330653006: Include the latest binary download info in safe browsing incident reports. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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/safe_browsing/last_download_finder.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/prefs/pref_service.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/history/history_service.h"
17 #include "chrome/browser/history/history_service_factory.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/safe_browsing/csd.pb.h"
21 #include "chrome/common/safe_browsing/download_protection_util.h"
22 #include "content/public/browser/download_item.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26
27 namespace safe_browsing {
28
29 namespace {
30
31 // Returns true if |first| is more recent than |second|, preferring opened over
32 // non-opened for downloads that completed at the same time (extraordinarily
33 // unlikely). Only files that look like some kind of executable are considered.
34 bool IsMoreInterestingThan(const history::DownloadRow& first,
35 const history::DownloadRow& second) {
36 if (first.end_time < second.end_time)
37 return false;
38 // TODO(grt): Peek into archives to see if they contain binaries;
39 // http://crbug.com/386915.
40 if (!download_protection_util::IsBinaryFile(first.target_path) ||
41 download_protection_util::IsArchiveFile(first.target_path)) {
42 return false;
43 }
44 return (first.end_time != second.end_time ||
45 (first.opened && !second.opened));
46 }
47
48 // Returns a pointer to the most interesting copleted download in |downloads|.
mattm 2014/06/27 21:50:32 completed
grt (UTC plus 2) 2014/06/30 16:53:38 Done.
49 const history::DownloadRow* FindMostInteresting(
50 const std::vector<history::DownloadRow>& downloads) {
51 const history::DownloadRow* most_recent_row = NULL;
52 for (size_t i = 0; i < downloads.size(); ++i) {
53 const history::DownloadRow& row = downloads[i];
54 // Ignore incomplete downloads.
55 if (row.state != content::DownloadItem::COMPLETE)
56 continue;
57 if (!most_recent_row || IsMoreInterestingThan(row, *most_recent_row))
58 most_recent_row = &row;
59 }
60 return most_recent_row;
61 }
62
63 // Populates the |details| protobuf with information pertaining to |download|.
64 void PopulateDetails(const history::DownloadRow& download,
65 ClientIncidentReport_DownloadDetails* details) {
66 ClientDownloadRequest* download_request = details->mutable_download();
67 download_request->set_url(download.url_chain.back().spec());
68 // digests is a required field, so force it to exist.
69 // TODO(grt): Include digests in reports; http://crbug.com/389123.
70 ignore_result(download_request->mutable_digests());
71 download_request->set_length(download.received_bytes);
72 for (size_t i = 0; i < download.url_chain.size(); ++i) {
73 const GURL& url = download.url_chain[i];
74 ClientDownloadRequest_Resource* resource =
75 download_request->add_resources();
76 resource->set_url(url.spec());
77 if (i != download.url_chain.size() - 1) { // An intermediate redirect.
78 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
79 } else { // The final download URL.
80 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
81 if (!download.referrer_url.is_empty())
82 resource->set_referrer(download.referrer_url.spec());
83 }
84 }
85 download_request->set_file_basename(
86 download.target_path.BaseName().AsUTF8Unsafe());
87 download_request->set_download_type(
88 download_protection_util::GetDownloadType(download.target_path));
89 download_request->set_locale(
90 g_browser_process->local_state()->GetString(prefs::kApplicationLocale));
91
92 details->set_download_time_msec(download.end_time.ToJavaTime());
93 // Opened time is unknown for now, so use the download time if the file was
94 // opened in Chrome.
95 if (download.opened)
96 details->set_open_time_msec(download.end_time.ToJavaTime());
97 }
98
99 } // namespace
100
101 LastDownloadFinder::~LastDownloadFinder() {
102 }
103
104 // static
105 scoped_ptr<LastDownloadFinder> LastDownloadFinder::Create(
106 const LastDownloadCallback& callback) {
107 scoped_ptr<LastDownloadFinder> finder(make_scoped_ptr(new LastDownloadFinder(
108 g_browser_process->profile_manager()->GetLoadedProfiles(), callback)));
109 // Return NULL if there is no work to do.
110 if (finder->profiles_.empty())
111 return scoped_ptr<LastDownloadFinder>();
112 return finder.Pass();
113 }
114
115 LastDownloadFinder::LastDownloadFinder() : weak_ptr_factory_(this) {
116 }
117
118 LastDownloadFinder::LastDownloadFinder(const std::vector<Profile*>& profiles,
119 const LastDownloadCallback& callback)
120 : callback_(callback), weak_ptr_factory_(this) {
121 // Observe profile lifecycle events so that the finder can begin or abandon
122 // the search in profiles while it is running.
123 notification_registrar_.Add(this,
124 chrome::NOTIFICATION_PROFILE_CREATED,
mattm 2014/06/27 21:50:32 Is there a reason this is CREATED instead of ADDED
grt (UTC plus 2) 2014/06/30 16:53:38 Hmm. I think it should be ADDED, actually, since t
125 content::NotificationService::AllSources());
126 notification_registrar_.Add(this,
127 chrome::NOTIFICATION_HISTORY_LOADED,
128 content::NotificationService::AllSources());
129 notification_registrar_.Add(this,
130 chrome::NOTIFICATION_PROFILE_DESTROYED,
131 content::NotificationService::AllSources());
132
133 // Begin the seach for all given profiles.
134 std::for_each(
135 profiles.begin(),
136 profiles.end(),
137 std::bind1st(std::mem_fun(&LastDownloadFinder::SearchInProfile), this));
138 }
139
140 void LastDownloadFinder::SearchInProfile(Profile* profile) {
141 // Do not look in OTR profiles or in profiles that do not participate in
142 // safe browsing.
143 if (profile->IsOffTheRecord() ||
144 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
145 return;
146 }
147
148 HistoryService* history_service =
149 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
150 // No history service is returned for profiles that do not save history.
151 if (!history_service)
152 return;
153
154 profiles_.push_back(profile);
155 if (history_service->BackendLoaded()) {
156 history_service->QueryDownloads(
157 base::Bind(&LastDownloadFinder::OnDownloadQuery,
158 weak_ptr_factory_.GetWeakPtr(),
159 profile));
160 } // else wait until history is loaded.
161 }
162
163 void LastDownloadFinder::OnProfileHistoryLoaded(
164 Profile* profile,
165 HistoryService* history_service) {
166 if (std::find(profiles_.begin(), profiles_.end(), profile) !=
167 profiles_.end()) {
168 history_service->QueryDownloads(
169 base::Bind(&LastDownloadFinder::OnDownloadQuery,
170 weak_ptr_factory_.GetWeakPtr(),
171 profile));
172 }
173 }
174
175 void LastDownloadFinder::AbandonSearchInProfile(Profile* profile) {
176 RemoveProfileAndReportIfDone(
177 std::find(profiles_.begin(), profiles_.end(), profile));
178 }
179
180 void LastDownloadFinder::OnDownloadQuery(
181 Profile* profile,
182 scoped_ptr<std::vector<history::DownloadRow> > downloads) {
183 // Early-exit if the history search for this profile was abandoned.
184 std::vector<Profile*>::iterator it =
185 std::find(profiles_.begin(), profiles_.end(), profile);
186 if (it == profiles_.end())
187 return;
188
189 // Find the most recent from this profile and use it if it's better than
190 // anything else found so far.
191 const history::DownloadRow* profile_best = FindMostInteresting(*downloads);
192 if (profile_best && IsMoreInterestingThan(*profile_best, most_recent_row_))
193 most_recent_row_ = *profile_best;
194
195 RemoveProfileAndReportIfDone(it);
196 }
197
198 void LastDownloadFinder::RemoveProfileAndReportIfDone(
199 std::vector<Profile*>::iterator it) {
200 if (it == profiles_.end())
mattm 2014/06/27 21:50:32 should this be a DCHECK?
grt (UTC plus 2) 2014/06/30 16:53:37 No. Comment added.
201 return;
202
203 *it = profiles_.back();
204 profiles_.resize(profiles_.size() - 1);
205
206 // Finish processing if all results are in.
207 if (profiles_.empty())
208 ReportResults();
209 // Do not touch this instance after reporting results.
210 }
211
212 void LastDownloadFinder::ReportResults() {
213 DCHECK(profiles_.empty());
214 if (most_recent_row_.end_time.is_null()) {
215 callback_.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>());
216 // Do not touch this instance after running the callback, since it may have
217 // been deleted.
218 } else {
219 scoped_ptr<ClientIncidentReport_DownloadDetails> details(
220 new ClientIncidentReport_DownloadDetails());
221 PopulateDetails(most_recent_row_, details.get());
222 callback_.Run(details.Pass());
223 // Do not touch this instance after running the callback, since it may have
224 // been deleted.
225 }
226 }
227
228 void LastDownloadFinder::Observe(int type,
229 const content::NotificationSource& source,
230 const content::NotificationDetails& details) {
231 switch (type) {
232 case chrome::NOTIFICATION_PROFILE_CREATED:
233 SearchInProfile(content::Source<Profile>(source).ptr());
234 break;
235 case chrome::NOTIFICATION_HISTORY_LOADED:
236 OnProfileHistoryLoaded(content::Source<Profile>(source).ptr(),
237 content::Details<HistoryService>(details).ptr());
238 break;
239 case chrome::NOTIFICATION_PROFILE_DESTROYED:
240 AbandonSearchInProfile(content::Source<Profile>(source).ptr());
241 break;
242 default:
243 break;
244 }
245 }
246
247 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698