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/incident_reporting/incident_reporting_service.h"
6
7 #include <math.h>
8 #include <stddef.h>
9
10 #include <algorithm>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/process/process.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_util.h"
22 #include "base/task/post_task.h"
23 #include "base/task/task_traits.h"
24 #include "base/task/thread_pool.h"
25 #include "base/threading/thread_task_runner_handle.h"
26 #include "build/branding_buildflags.h"
27 #include "build/build_config.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/browser/profiles/profile_manager.h"
32 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
33 #include "chrome/browser/safe_browsing/incident_reporting/extension_data_collection.h"
34 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
35 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
36 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
37 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
38 #include "chrome/browser/safe_browsing/incident_reporting/state_store.h"
39 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
40 #include "chrome/common/pref_names.h"
41 #include "components/prefs/pref_service.h"
42 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
43 #include "components/safe_browsing/core/features.h"
44 #include "components/safe_browsing/core/proto/csd.pb.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/download_item_utils.h"
47 #include "services/network/public/cpp/shared_url_loader_factory.h"
48 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
49
50 namespace safe_browsing {
51
52 const base::Feature kIncidentReportingEnableUpload {
53 "IncidentReportingEnableUpload",
54 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
55 base::FEATURE_ENABLED_BY_DEFAULT
56 #else
57 base::FEATURE_DISABLED_BY_DEFAULT
58 #endif
59 };
60
61 namespace {
62
63 // The action taken for an incident; used for user metrics (see
64 // LogIncidentDataType).
65 enum IncidentDisposition {
66 RECEIVED,
67 DROPPED,
68 ACCEPTED,
69 PRUNED,
70 DISCARDED,
71 NO_DOWNLOAD,
72 NUM_DISPOSITIONS
73 };
74
75 // The state persisted for a specific instance of an incident to enable pruning
76 // of previously-reported incidents.
77 struct PersistentIncidentState {
78 // The type of the incident.
79 IncidentType type;
80
81 // The key for a specific instance of an incident.
82 std::string key;
83
84 // A hash digest representing a specific instance of an incident.
85 uint32_t digest;
86 };
87
88 // The amount of time the service will wait to collate incidents.
89 const int64_t kDefaultUploadDelayMs = 1000 * 60; // one minute
90
91 // The amount of time between running delayed analysis callbacks.
92 const int64_t kDefaultCallbackIntervalMs = 1000 * 20;
93
94 // Logs the type of incident in |incident_data| to a user metrics histogram.
LogIncidentDataType(IncidentDisposition disposition,const Incident & incident)95 void LogIncidentDataType(IncidentDisposition disposition,
96 const Incident& incident) {
97 static const char* const kHistogramNames[] = {
98 "SBIRS.ReceivedIncident",
99 "SBIRS.DroppedIncident",
100 "SBIRS.Incident",
101 "SBIRS.PrunedIncident",
102 "SBIRS.DiscardedIncident",
103 "SBIRS.NoDownloadIncident",
104 };
105 static_assert(base::size(kHistogramNames) == NUM_DISPOSITIONS,
106 "Keep kHistogramNames in sync with enum IncidentDisposition.");
107 DCHECK_GE(disposition, 0);
108 DCHECK_LT(disposition, NUM_DISPOSITIONS);
109 base::LinearHistogram::FactoryGet(
110 kHistogramNames[disposition],
111 1, // minimum
112 static_cast<int32_t>(IncidentType::NUM_TYPES), // maximum
113 static_cast<size_t>(IncidentType::NUM_TYPES) + 1, // bucket_count
114 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(
115 static_cast<int32_t>(incident.GetType()));
116 }
117
118 // Computes the persistent state for an incident.
ComputeIncidentState(const Incident & incident)119 PersistentIncidentState ComputeIncidentState(const Incident& incident) {
120 PersistentIncidentState state = {
121 incident.GetType(),
122 incident.GetKey(),
123 incident.ComputeDigest(),
124 };
125 return state;
126 }
127
128 // Returns a task runner for blocking tasks in the background.
GetBackgroundTaskRunner()129 scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() {
130 return base::ThreadPool::CreateTaskRunner(
131 {base::TaskPriority::BEST_EFFORT,
132 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()});
133 }
134
135 } // namespace
136
137 struct IncidentReportingService::ProfileContext {
138 ProfileContext();
139 ~ProfileContext();
140
141 // Returns true if the profile has incidents to be uploaded or cleared.
142 bool HasIncidents() const;
143
144 // The incidents collected for this profile pending creation and/or upload.
145 // Will contain null values for pruned incidents.
146 std::vector<std::unique_ptr<Incident>> incidents;
147
148 // The incidents data of which should be cleared.
149 std::vector<std::unique_ptr<Incident>> incidents_to_clear;
150
151 // State storage for this profile; null until OnProfileAdded is called.
152 std::unique_ptr<StateStore> state_store;
153
154 // False until OnProfileAdded is called.
155 bool added;
156
157 private:
158 DISALLOW_COPY_AND_ASSIGN(ProfileContext);
159 };
160
161 class IncidentReportingService::UploadContext {
162 public:
163 typedef std::map<ProfileContext*, std::vector<PersistentIncidentState>>
164 PersistentIncidentStateCollection;
165
166 explicit UploadContext(std::unique_ptr<ClientIncidentReport> report);
167 ~UploadContext();
168
169 // The report being uploaded.
170 std::unique_ptr<ClientIncidentReport> report;
171
172 // The uploader in use.
173 std::unique_ptr<IncidentReportUploader> uploader;
174
175 // A mapping of profile contexts to the data to be persisted upon successful
176 // upload.
177 PersistentIncidentStateCollection profiles_to_state;
178
179 private:
180 DISALLOW_COPY_AND_ASSIGN(UploadContext);
181 };
182
183 // An IncidentReceiver that is weakly-bound to the service and transparently
184 // bounces process-wide incidents back to the main thread for handling.
185 class IncidentReportingService::Receiver : public IncidentReceiver {
186 public:
187 explicit Receiver(const base::WeakPtr<IncidentReportingService>& service);
188 ~Receiver() override;
189
190 // IncidentReceiver methods:
191 void AddIncidentForProfile(Profile* profile,
192 std::unique_ptr<Incident> incident) override;
193 void AddIncidentForProcess(std::unique_ptr<Incident> incident) override;
194 void ClearIncidentForProcess(std::unique_ptr<Incident> incident) override;
195
196 private:
197 static void AddIncidentOnMainThread(
198 const base::WeakPtr<IncidentReportingService>& service,
199 Profile* profile,
200 std::unique_ptr<Incident> incident);
201 static void ClearIncidentOnMainThread(
202 const base::WeakPtr<IncidentReportingService>& service,
203 Profile* profile,
204 std::unique_ptr<Incident> incident);
205
206 base::WeakPtr<IncidentReportingService> service_;
207 scoped_refptr<base::SingleThreadTaskRunner> thread_runner_;
208
209 DISALLOW_COPY_AND_ASSIGN(Receiver);
210 };
211
Receiver(const base::WeakPtr<IncidentReportingService> & service)212 IncidentReportingService::Receiver::Receiver(
213 const base::WeakPtr<IncidentReportingService>& service)
214 : service_(service),
215 thread_runner_(base::ThreadTaskRunnerHandle::Get()) {
216 }
217
~Receiver()218 IncidentReportingService::Receiver::~Receiver() {
219 }
220
AddIncidentForProfile(Profile * profile,std::unique_ptr<Incident> incident)221 void IncidentReportingService::Receiver::AddIncidentForProfile(
222 Profile* profile,
223 std::unique_ptr<Incident> incident) {
224 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
225 DCHECK(profile);
226 AddIncidentOnMainThread(service_, profile, std::move(incident));
227 }
228
AddIncidentForProcess(std::unique_ptr<Incident> incident)229 void IncidentReportingService::Receiver::AddIncidentForProcess(
230 std::unique_ptr<Incident> incident) {
231 if (thread_runner_->BelongsToCurrentThread()) {
232 AddIncidentOnMainThread(service_, nullptr, std::move(incident));
233 } else {
234 thread_runner_->PostTask(
235 FROM_HERE,
236 base::BindOnce(
237 &IncidentReportingService::Receiver::AddIncidentOnMainThread,
238 service_, nullptr, std::move(incident)));
239 }
240 }
241
ClearIncidentForProcess(std::unique_ptr<Incident> incident)242 void IncidentReportingService::Receiver::ClearIncidentForProcess(
243 std::unique_ptr<Incident> incident) {
244 if (thread_runner_->BelongsToCurrentThread()) {
245 ClearIncidentOnMainThread(service_, nullptr, std::move(incident));
246 } else {
247 thread_runner_->PostTask(
248 FROM_HERE,
249 base::BindOnce(
250 &IncidentReportingService::Receiver::ClearIncidentOnMainThread,
251 service_, nullptr, std::move(incident)));
252 }
253 }
254
HasIncidentsToUpload() const255 bool IncidentReportingService::HasIncidentsToUpload() const {
256 for (const auto& profile_and_context : profiles_) {
257 if (!profile_and_context.second->incidents.empty())
258 return true;
259 }
260 return false;
261 }
262
263 // static
AddIncidentOnMainThread(const base::WeakPtr<IncidentReportingService> & service,Profile * profile,std::unique_ptr<Incident> incident)264 void IncidentReportingService::Receiver::AddIncidentOnMainThread(
265 const base::WeakPtr<IncidentReportingService>& service,
266 Profile* profile,
267 std::unique_ptr<Incident> incident) {
268 if (service)
269 service->AddIncident(profile, std::move(incident));
270 else
271 LogIncidentDataType(DISCARDED, *incident);
272 }
273
274 // static
ClearIncidentOnMainThread(const base::WeakPtr<IncidentReportingService> & service,Profile * profile,std::unique_ptr<Incident> incident)275 void IncidentReportingService::Receiver::ClearIncidentOnMainThread(
276 const base::WeakPtr<IncidentReportingService>& service,
277 Profile* profile,
278 std::unique_ptr<Incident> incident) {
279 if (service)
280 service->ClearIncident(profile, std::move(incident));
281 }
282
ProfileContext()283 IncidentReportingService::ProfileContext::ProfileContext() : added(false) {
284 }
285
~ProfileContext()286 IncidentReportingService::ProfileContext::~ProfileContext() {
287 for (const auto& incident : incidents) {
288 if (incident)
289 LogIncidentDataType(DISCARDED, *incident);
290 }
291 }
292
HasIncidents() const293 bool IncidentReportingService::ProfileContext::HasIncidents() const {
294 return !incidents.empty() || !incidents_to_clear.empty();
295 }
296
UploadContext(std::unique_ptr<ClientIncidentReport> report)297 IncidentReportingService::UploadContext::UploadContext(
298 std::unique_ptr<ClientIncidentReport> report)
299 : report(std::move(report)) {}
300
~UploadContext()301 IncidentReportingService::UploadContext::~UploadContext() {
302 }
303
304 // static
IsEnabledForProfile(Profile * profile)305 bool IncidentReportingService::IsEnabledForProfile(Profile* profile) {
306 if (profile->IsOffTheRecord())
307 return false;
308 if (!IsSafeBrowsingEnabled(*profile->GetPrefs()))
309 return false;
310 return IsExtendedReportingEnabled(*profile->GetPrefs());
311 }
312
IncidentReportingService(SafeBrowsingService * safe_browsing_service)313 IncidentReportingService::IncidentReportingService(
314 SafeBrowsingService* safe_browsing_service)
315 : IncidentReportingService(
316 safe_browsing_service,
317 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
318 GetBackgroundTaskRunner()) {
319 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
320 DownloadProtectionService* download_protection_service =
321 (safe_browsing_service
322 ? safe_browsing_service->download_protection_service()
323 : nullptr);
324 if (download_protection_service) {
325 client_download_request_subscription_ =
326 download_protection_service->RegisterClientDownloadRequestCallback(
327 base::BindRepeating(
328 &IncidentReportingService::OnClientDownloadRequest,
329 base::Unretained(this)));
330 }
331 }
332
~IncidentReportingService()333 IncidentReportingService::~IncidentReportingService() {
334 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
335 CancelIncidentCollection();
336
337 // Cancel all internal asynchronous tasks.
338 weak_ptr_factory_.InvalidateWeakPtrs();
339
340 CancelEnvironmentCollection();
341 CancelDownloadCollection();
342 CancelAllReportUploads();
343
344 if (g_browser_process->profile_manager())
345 g_browser_process->profile_manager()->RemoveObserver(this);
346
347 for (const auto& profile_and_context : profiles_) {
348 Profile* profile = profile_and_context.first;
349 if (profile)
350 profile->RemoveObserver(this);
351 }
352 }
353
354 std::unique_ptr<IncidentReceiver>
GetIncidentReceiver()355 IncidentReportingService::GetIncidentReceiver() {
356 return std::make_unique<Receiver>(receiver_weak_ptr_factory_.GetWeakPtr());
357 }
358
359 std::unique_ptr<prefs::mojom::TrackedPreferenceValidationDelegate>
CreatePreferenceValidationDelegate(Profile * profile)360 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
361 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
362
363 if (profile->IsOffTheRecord())
364 return std::unique_ptr<prefs::mojom::TrackedPreferenceValidationDelegate>();
365 return std::make_unique<PreferenceValidationDelegate>(profile,
366 GetIncidentReceiver());
367 }
368
RegisterDelayedAnalysisCallback(DelayedAnalysisCallback callback)369 void IncidentReportingService::RegisterDelayedAnalysisCallback(
370 DelayedAnalysisCallback callback) {
371 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
372
373 // |callback| will be run on the blocking pool. The receiver will bounce back
374 // to the origin thread if needed.
375 delayed_analysis_callbacks_.RegisterCallback(
376 base::BindOnce(std::move(callback), base::Passed(GetIncidentReceiver())));
377
378 // Start running the callbacks if any profiles are participating in safe
379 // browsing extended reporting. If none are now, running will commence if/when
380 // such a profile is added.
381 if (FindEligibleProfile())
382 delayed_analysis_callbacks_.Start();
383 }
384
AddDownloadManager(content::DownloadManager * download_manager)385 void IncidentReportingService::AddDownloadManager(
386 content::DownloadManager* download_manager) {
387 download_metadata_manager_.AddDownloadManager(download_manager);
388 }
389
IncidentReportingService(SafeBrowsingService * safe_browsing_service,base::TimeDelta delayed_task_interval,const scoped_refptr<base::TaskRunner> & delayed_task_runner)390 IncidentReportingService::IncidentReportingService(
391 SafeBrowsingService* safe_browsing_service,
392 base::TimeDelta delayed_task_interval,
393 const scoped_refptr<base::TaskRunner>& delayed_task_runner)
394 : url_loader_factory_(nullptr),
395 collect_environment_data_fn_(&CollectEnvironmentData),
396 environment_collection_task_runner_(GetBackgroundTaskRunner()),
397 collation_timer_(FROM_HERE,
398 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
399 this,
400 &IncidentReportingService::OnCollationTimeout),
401 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner) {
402 if (base::FeatureList::IsEnabled(kSafeBrowsingRemoveCookies)) {
403 url_loader_factory_ = g_browser_process->shared_url_loader_factory();
404 } else if (safe_browsing_service) {
405 url_loader_factory_ = safe_browsing_service->GetURLLoaderFactory();
406 }
407 if (g_browser_process->profile_manager())
408 g_browser_process->profile_manager()->AddObserver(this);
409 }
410
SetCollectEnvironmentHook(CollectEnvironmentDataFn collect_environment_data_hook,const scoped_refptr<base::TaskRunner> & task_runner)411 void IncidentReportingService::SetCollectEnvironmentHook(
412 CollectEnvironmentDataFn collect_environment_data_hook,
413 const scoped_refptr<base::TaskRunner>& task_runner) {
414 if (collect_environment_data_hook) {
415 collect_environment_data_fn_ = collect_environment_data_hook;
416 environment_collection_task_runner_ = task_runner;
417 } else {
418 collect_environment_data_fn_ = &CollectEnvironmentData;
419 environment_collection_task_runner_ = GetBackgroundTaskRunner();
420 }
421 }
422
DoExtensionCollection(ClientIncidentReport_ExtensionData * extension_data)423 void IncidentReportingService::DoExtensionCollection(
424 ClientIncidentReport_ExtensionData* extension_data) {
425 CollectExtensionData(extension_data);
426 }
427
OnProfileAdded(Profile * profile)428 void IncidentReportingService::OnProfileAdded(Profile* profile) {
429 // Handle the addition of a new profile to the ProfileManager. Create a new
430 // context for |profile| if one does not exist, drop any received incidents
431 // for the profile if the profile is not participating in safe browsing
432 // extended reporting, and initiate a new search for the most recent download
433 // if a report is being assembled and the most recent has not been found.
434 // Note that |profile| is assumed to outlive |this|.
435
436 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
437
438 // Track the addition of all profiles even when no report is being assembled
439 // so that the service can determine whether or not it can evaluate a
440 // profile's preferences at the time of incident addition.
441 ProfileContext* context = GetOrCreateProfileContext(profile);
442 DCHECK(!context->added);
443 context->added = true;
444 context->state_store = std::make_unique<StateStore>(profile);
445 bool enabled_for_profile = IsEnabledForProfile(profile);
446
447 // Drop all incidents associated with this profile that were received prior to
448 // its addition if incident reporting is not enabled for it.
449 if (!context->incidents.empty() && !enabled_for_profile) {
450 for (const auto& incident : context->incidents)
451 LogIncidentDataType(DROPPED, *incident);
452 context->incidents.clear();
453 }
454
455 if (enabled_for_profile) {
456 // Start processing delayed analysis callbacks if incident reporting is
457 // enabled for this new profile. Start is idempotent, so this is safe even
458 // if they're already running.
459 delayed_analysis_callbacks_.Start();
460
461 // Start a new report if there are process-wide incidents, or incidents for
462 // this profile.
463 if ((GetProfileContext(nullptr) &&
464 GetProfileContext(nullptr)->HasIncidents()) ||
465 context->HasIncidents()) {
466 BeginReportProcessing();
467 }
468 }
469
470 // TODO(grt): register for pref change notifications to start delayed analysis
471 // and/or report processing if sb is currently disabled but subsequently
472 // enabled.
473
474 // Nothing else to do if a report is not being assembled.
475 if (!report_)
476 return;
477
478 // Environment collection is deferred until at least one profile for which the
479 // service is enabled is added. Re-initiate collection now in case this is the
480 // first such profile.
481 BeginEnvironmentCollection();
482 // Take another stab at finding the most recent download if a report is being
483 // assembled and one hasn't been found yet (the LastDownloadFinder operates
484 // only on profiles that have been added to the ProfileManager).
485 BeginDownloadCollection();
486 }
487
OnProfileWillBeDestroyed(Profile * profile)488 void IncidentReportingService::OnProfileWillBeDestroyed(Profile* profile) {
489 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
490
491 profile->RemoveObserver(this);
492
493 auto it = profiles_.find(profile);
494 DCHECK(it != profiles_.end());
495
496 // Take ownership of the context.
497 std::unique_ptr<ProfileContext> context = std::move(it->second);
498
499 // TODO(grt): Persist incidents for upload on future profile load.
500
501 // Remove the association with this profile context from all pending uploads.
502 for (const auto& upload : uploads_)
503 upload->profiles_to_state.erase(context.get());
504
505 // Forget about this profile. Incidents not yet sent for upload are lost.
506 // No new incidents will be accepted for it.
507 profiles_.erase(it);
508 }
509
510 std::unique_ptr<LastDownloadFinder>
CreateDownloadFinder(LastDownloadFinder::LastDownloadCallback callback)511 IncidentReportingService::CreateDownloadFinder(
512 LastDownloadFinder::LastDownloadCallback callback) {
513 return LastDownloadFinder::Create(
514 base::BindRepeating(&DownloadMetadataManager::GetDownloadDetails,
515 base::Unretained(&download_metadata_manager_)),
516 std::move(callback));
517 }
518
519 std::unique_ptr<IncidentReportUploader>
StartReportUpload(IncidentReportUploader::OnResultCallback callback,const ClientIncidentReport & report)520 IncidentReportingService::StartReportUpload(
521 IncidentReportUploader::OnResultCallback callback,
522 const ClientIncidentReport& report) {
523 return IncidentReportUploaderImpl::UploadReport(std::move(callback),
524 url_loader_factory_, report);
525 }
526
IsProcessingReport() const527 bool IncidentReportingService::IsProcessingReport() const {
528 return report_ != nullptr;
529 }
530
531 IncidentReportingService::ProfileContext*
GetOrCreateProfileContext(Profile * profile)532 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
533 std::unique_ptr<ProfileContext>& context = profiles_[profile];
534 if (!context) {
535 context = std::make_unique<ProfileContext>();
536 if (profile)
537 profile->AddObserver(this);
538 }
539 return context.get();
540 }
541
542 IncidentReportingService::ProfileContext*
GetProfileContext(Profile * profile)543 IncidentReportingService::GetProfileContext(Profile* profile) {
544 auto it = profiles_.find(profile);
545 return it != profiles_.end() ? it->second.get() : nullptr;
546 }
547
FindEligibleProfile() const548 Profile* IncidentReportingService::FindEligibleProfile() const {
549 for (const auto& scan : profiles_) {
550 // Skip over profiles that have yet to be added to the profile manager.
551 // This will also skip over the NULL-profile context used to hold
552 // process-wide incidents.
553 if (!scan.second->added)
554 continue;
555
556 if (IsEnabledForProfile(scan.first))
557 return scan.first;
558 }
559
560 return nullptr;
561 }
562
AddIncident(Profile * profile,std::unique_ptr<Incident> incident)563 void IncidentReportingService::AddIncident(Profile* profile,
564 std::unique_ptr<Incident> incident) {
565 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
566
567 // Ignore incidents from off-the-record profiles.
568 if (profile && profile->IsOffTheRecord())
569 return;
570
571 ProfileContext* context = GetOrCreateProfileContext(profile);
572 // If this is a process-wide incident, the context must not indicate that the
573 // profile (which is NULL) has been added to the profile manager.
574 DCHECK(profile || !context->added);
575
576 LogIncidentDataType(RECEIVED, *incident);
577
578 // Drop the incident immediately if the profile has already been added to the
579 // manager and does not have incident reporting enabled. Preference evaluation
580 // is deferred until OnProfileAdded() otherwise.
581 if (context->added && !IsEnabledForProfile(profile)) {
582 LogIncidentDataType(DROPPED, *incident);
583 return;
584 }
585
586 // Take ownership of the incident.
587 context->incidents.push_back(std::move(incident));
588
589 // Remember when the first incident for this report arrived.
590 if (first_incident_time_.is_null())
591 first_incident_time_ = base::Time::Now();
592 // Log the time between the previous incident and this one.
593 if (!last_incident_time_.is_null()) {
594 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
595 base::TimeTicks::Now() - last_incident_time_);
596 }
597 last_incident_time_ = base::TimeTicks::Now();
598
599 // Persist the incident data.
600
601 // Start assembling a new report if this is the first incident ever or the
602 // first since the last upload.
603 BeginReportProcessing();
604 }
605
ClearIncident(Profile * profile,std::unique_ptr<Incident> incident)606 void IncidentReportingService::ClearIncident(
607 Profile* profile,
608 std::unique_ptr<Incident> incident) {
609 ProfileContext* context = GetOrCreateProfileContext(profile);
610 context->incidents_to_clear.push_back(std::move(incident));
611 // Begin processing to handle cleared incidents following collation.
612 BeginReportProcessing();
613 }
614
BeginReportProcessing()615 void IncidentReportingService::BeginReportProcessing() {
616 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
617
618 // Creates a new report if needed.
619 if (!report_)
620 report_.reset(new ClientIncidentReport());
621
622 // Ensure that collection tasks are running (calls are idempotent).
623 BeginIncidentCollation();
624 BeginEnvironmentCollection();
625 BeginDownloadCollection();
626 }
627
BeginIncidentCollation()628 void IncidentReportingService::BeginIncidentCollation() {
629 // Restart the delay timer to send the report upon expiration.
630 collation_timeout_pending_ = true;
631 collation_timer_.Reset();
632 }
633
WaitingToCollateIncidents()634 bool IncidentReportingService::WaitingToCollateIncidents() {
635 return collation_timeout_pending_;
636 }
637
CancelIncidentCollection()638 void IncidentReportingService::CancelIncidentCollection() {
639 collation_timeout_pending_ = false;
640 last_incident_time_ = base::TimeTicks();
641 report_.reset();
642 }
643
OnCollationTimeout()644 void IncidentReportingService::OnCollationTimeout() {
645 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
646
647 // Exit early if collection was cancelled.
648 if (!collation_timeout_pending_)
649 return;
650
651 // Wait another round if profile-bound incidents have come in from a profile
652 // that has yet to complete creation.
653 for (const auto& scan : profiles_) {
654 if (scan.first && !scan.second->added && scan.second->HasIncidents()) {
655 collation_timer_.Reset();
656 return;
657 }
658 }
659
660 collation_timeout_pending_ = false;
661
662 ProcessIncidentsIfCollectionComplete();
663 }
664
BeginEnvironmentCollection()665 void IncidentReportingService::BeginEnvironmentCollection() {
666 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
667 DCHECK(report_);
668 // Nothing to do if environment collection is pending or has already
669 // completed, if there are no incidents to process, or if there is no eligible
670 // profile.
671 if (environment_collection_pending_ || report_->has_environment() ||
672 !HasIncidentsToUpload() || !FindEligibleProfile()) {
673 return;
674 }
675
676 environment_collection_begin_ = base::TimeTicks::Now();
677 ClientIncidentReport_EnvironmentData* environment_data =
678 new ClientIncidentReport_EnvironmentData();
679 environment_collection_pending_ =
680 environment_collection_task_runner_->PostTaskAndReply(
681 FROM_HERE,
682 base::BindOnce(collect_environment_data_fn_, environment_data),
683 base::BindOnce(&IncidentReportingService::OnEnvironmentDataCollected,
684 weak_ptr_factory_.GetWeakPtr(),
685 base::WrapUnique(environment_data)));
686
687 // Posting the task will fail if the runner has been shut down. This should
688 // never happen since the blocking pool is shut down after this service.
689 DCHECK(environment_collection_pending_);
690 }
691
WaitingForEnvironmentCollection()692 bool IncidentReportingService::WaitingForEnvironmentCollection() {
693 return environment_collection_pending_;
694 }
695
CancelEnvironmentCollection()696 void IncidentReportingService::CancelEnvironmentCollection() {
697 environment_collection_begin_ = base::TimeTicks();
698 environment_collection_pending_ = false;
699 if (report_)
700 report_->clear_environment();
701 }
702
OnEnvironmentDataCollected(std::unique_ptr<ClientIncidentReport_EnvironmentData> environment_data)703 void IncidentReportingService::OnEnvironmentDataCollected(
704 std::unique_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
705 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
706 DCHECK(environment_collection_pending_);
707 DCHECK(report_ && !report_->has_environment());
708 environment_collection_pending_ = false;
709
710 // Process::Current().CreationTime() is missing on some platforms.
711 #if defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) || defined(OS_BSD) || \
712 defined(OS_CHROMEOS)
713 base::TimeDelta uptime =
714 first_incident_time_ - base::Process::Current().CreationTime();
715 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
716 #endif
717
718 report_->set_allocated_environment(environment_data.release());
719
720 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
721 base::TimeTicks::Now() - environment_collection_begin_);
722 environment_collection_begin_ = base::TimeTicks();
723
724 ProcessIncidentsIfCollectionComplete();
725 }
726
BeginDownloadCollection()727 void IncidentReportingService::BeginDownloadCollection() {
728 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
729 DCHECK(report_);
730 // Nothing to do if a search for the most recent download is already pending,
731 // if one has already been found, or if there are no incidents to process.
732 if (last_download_finder_ || report_->has_download() ||
733 !HasIncidentsToUpload()) {
734 return;
735 }
736
737 last_download_begin_ = base::TimeTicks::Now();
738 last_download_finder_ = CreateDownloadFinder(
739 base::BindOnce(&IncidentReportingService::OnLastDownloadFound,
740 weak_ptr_factory_.GetWeakPtr()));
741 // No instance is returned if there are no eligible loaded profiles. Another
742 // search will be attempted in OnProfileAdded() if another profile appears on
743 // the scene.
744 if (!last_download_finder_)
745 last_download_begin_ = base::TimeTicks();
746 }
747
WaitingForMostRecentDownload()748 bool IncidentReportingService::WaitingForMostRecentDownload() {
749 DCHECK(report_); // Only call this when a report is being assembled.
750 // The easy case: not waiting if a download has already been found.
751 if (report_->has_download())
752 return false;
753 // The next easy case: waiting if the finder is operating.
754 if (last_download_finder_)
755 return true;
756 // Harder case 1: not waiting if there are no incidents to upload (only
757 // incidents to clear).
758 if (!HasIncidentsToUpload())
759 return false;
760 // Harder case 2: waiting if a non-NULL profile has not yet been added.
761 for (const auto& scan : profiles_) {
762 if (scan.first && !scan.second->added)
763 return true;
764 }
765 // There is no most recent download and there's nothing more to wait for.
766 return false;
767 }
768
CancelDownloadCollection()769 void IncidentReportingService::CancelDownloadCollection() {
770 last_download_finder_.reset();
771 last_download_begin_ = base::TimeTicks();
772 if (report_)
773 report_->clear_download();
774 }
775
OnLastDownloadFound(std::unique_ptr<ClientIncidentReport_DownloadDetails> last_binary_download,std::unique_ptr<ClientIncidentReport_NonBinaryDownloadDetails> last_non_binary_download)776 void IncidentReportingService::OnLastDownloadFound(
777 std::unique_ptr<ClientIncidentReport_DownloadDetails> last_binary_download,
778 std::unique_ptr<ClientIncidentReport_NonBinaryDownloadDetails>
779 last_non_binary_download) {
780 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
781 DCHECK(report_);
782
783 UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
784 base::TimeTicks::Now() - last_download_begin_);
785 last_download_begin_ = base::TimeTicks();
786
787 // Harvest the finder.
788 last_download_finder_.reset();
789
790 if (last_binary_download)
791 report_->set_allocated_download(last_binary_download.release());
792
793 if (last_non_binary_download) {
794 report_->set_allocated_non_binary_download(
795 last_non_binary_download.release());
796 }
797
798 ProcessIncidentsIfCollectionComplete();
799 }
800
ProcessIncidentsIfCollectionComplete()801 void IncidentReportingService::ProcessIncidentsIfCollectionComplete() {
802 DCHECK(report_);
803 // Bail out if there are still outstanding collection tasks. Completion of any
804 // of these will start another upload attempt.
805 if (WaitingForEnvironmentCollection() || WaitingToCollateIncidents() ||
806 WaitingForMostRecentDownload()) {
807 return;
808 }
809
810 // Take ownership of the report and clear things for future reports.
811 std::unique_ptr<ClientIncidentReport> report(std::move(report_));
812 first_incident_time_ = base::Time();
813 last_incident_time_ = base::TimeTicks();
814
815 ClientIncidentReport_EnvironmentData_Process* process =
816 report->mutable_environment()->mutable_process();
817
818 process->set_metrics_consent(
819 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
820
821 // Associate process-wide incidents with any eligible profile. If there is no
822 // eligible profile, drop the incidents.
823 ProfileContext* null_context = GetProfileContext(nullptr);
824 if (null_context && null_context->HasIncidents()) {
825 Profile* eligible_profile = FindEligibleProfile();
826 if (eligible_profile) {
827 ProfileContext* eligible_context = GetProfileContext(eligible_profile);
828 // Move the incidents to the target context.
829 for (auto& incident : null_context->incidents) {
830 eligible_context->incidents.push_back(std::move(incident));
831 }
832 null_context->incidents.clear();
833 for (auto& incident : null_context->incidents_to_clear)
834 eligible_context->incidents_to_clear.push_back(std::move(incident));
835 null_context->incidents_to_clear.clear();
836 } else {
837 for (const auto& incident : null_context->incidents)
838 LogIncidentDataType(DROPPED, *incident);
839 null_context->incidents.clear();
840 }
841 }
842
843 // Clear incidents data where needed.
844 for (auto& profile_and_context : profiles_) {
845 // Bypass process-wide incidents that have not yet been associated with a
846 // profile and profiles with no incidents to clear.
847 if (!profile_and_context.first ||
848 profile_and_context.second->incidents_to_clear.empty()) {
849 continue;
850 }
851 ProfileContext* context = profile_and_context.second.get();
852 StateStore::Transaction transaction(context->state_store.get());
853 for (const auto& incident : context->incidents_to_clear)
854 transaction.Clear(incident->GetType(), incident->GetKey());
855 context->incidents_to_clear.clear();
856 }
857 // Abandon report if there are no incidents to upload.
858 if (!HasIncidentsToUpload())
859 return;
860
861 bool has_download =
862 report->has_download() || report->has_non_binary_download();
863
864 // Collect incidents across all profiles participating in safe browsing
865 // extended reporting.
866 // Associate the profile contexts and their incident data with the upload.
867 UploadContext::PersistentIncidentStateCollection profiles_to_state;
868 for (auto& profile_and_context : profiles_) {
869 // Bypass process-wide incidents that have not yet been associated with a
870 // profile.
871 if (!profile_and_context.first)
872 continue;
873 ProfileContext* context = profile_and_context.second.get();
874 if (context->incidents.empty())
875 continue;
876 // Drop all incidents collected for the profile if it stopped participating
877 // before collection completed.
878 if (!IsEnabledForProfile(profile_and_context.first)) {
879 for (const auto& incident : context->incidents)
880 LogIncidentDataType(DROPPED, *incident);
881 context->incidents.clear();
882 continue;
883 }
884 StateStore::Transaction transaction(context->state_store.get());
885 std::vector<PersistentIncidentState> states;
886 // Prep persistent data and prune any incidents already sent.
887 for (const auto& incident : context->incidents) {
888 const PersistentIncidentState state = ComputeIncidentState(*incident);
889 if (context->state_store->HasBeenReported(state.type, state.key,
890 state.digest)) {
891 LogIncidentDataType(PRUNED, *incident);
892 } else if (!has_download) {
893 LogIncidentDataType(NO_DOWNLOAD, *incident);
894 // Drop the incident and mark for future pruning since no executable
895 // download was found.
896 transaction.MarkAsReported(state.type, state.key, state.digest);
897 } else {
898 LogIncidentDataType(ACCEPTED, *incident);
899 // Ownership of the payload is passed to the report.
900 ClientIncidentReport_IncidentData* data =
901 incident->TakePayload().release();
902 DCHECK(data->has_incident_time_msec());
903 report->mutable_incident()->AddAllocated(data);
904 data = nullptr;
905 states.push_back(state);
906 }
907 }
908 context->incidents.clear();
909 profiles_to_state[context].swap(states);
910 }
911
912 const int count = report->incident_size();
913
914 // Abandon the request if all incidents were pruned or otherwise dropped.
915 if (!count) {
916 if (!has_download) {
917 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
918 IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
919 IncidentReportUploader::NUM_UPLOAD_RESULTS);
920 }
921 return;
922 }
923 // Perform final synchronous collection tasks for the report.
924 DoExtensionCollection(report->mutable_extension_data());
925
926 std::unique_ptr<UploadContext> context(new UploadContext(std::move(report)));
927 context->profiles_to_state.swap(profiles_to_state);
928 UploadContext* temp_context = context.get();
929 uploads_.push_back(std::move(context));
930 IncidentReportingService::UploadReportIfUploadingEnabled(temp_context);
931 }
932
CancelAllReportUploads()933 void IncidentReportingService::CancelAllReportUploads() {
934 for (size_t i = 0; i < uploads_.size(); ++i) {
935 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
936 IncidentReportUploader::UPLOAD_CANCELLED,
937 IncidentReportUploader::NUM_UPLOAD_RESULTS);
938 }
939 uploads_.clear();
940 }
941
UploadReportIfUploadingEnabled(UploadContext * context)942 void IncidentReportingService::UploadReportIfUploadingEnabled(
943 UploadContext* context) {
944 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
945
946 if (!base::FeatureList::IsEnabled(kIncidentReportingEnableUpload)) {
947 OnReportUploadResult(context, IncidentReportUploader::UPLOAD_SUPPRESSED,
948 std::unique_ptr<ClientIncidentResponse>());
949 return;
950 }
951
952 // Initiate the upload.
953 context->uploader = StartReportUpload(
954 base::BindOnce(&IncidentReportingService::OnReportUploadResult,
955 weak_ptr_factory_.GetWeakPtr(), context),
956 *context->report);
957 if (!context->uploader) {
958 OnReportUploadResult(context,
959 IncidentReportUploader::UPLOAD_INVALID_REQUEST,
960 std::unique_ptr<ClientIncidentResponse>());
961 }
962 }
963
HandleResponse(const UploadContext & context)964 void IncidentReportingService::HandleResponse(const UploadContext& context) {
965 // Mark each incident as reported in its corresponding profile's state store.
966 for (const auto& context_and_states : context.profiles_to_state) {
967 StateStore::Transaction transaction(
968 context_and_states.first->state_store.get());
969 for (const auto& state : context_and_states.second)
970 transaction.MarkAsReported(state.type, state.key, state.digest);
971 }
972 }
973
OnReportUploadResult(UploadContext * context,IncidentReportUploader::Result result,std::unique_ptr<ClientIncidentResponse> response)974 void IncidentReportingService::OnReportUploadResult(
975 UploadContext* context,
976 IncidentReportUploader::Result result,
977 std::unique_ptr<ClientIncidentResponse> response) {
978 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
979
980 UMA_HISTOGRAM_ENUMERATION(
981 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
982
983 // The upload is no longer outstanding, so take ownership of the context (from
984 // the collection of outstanding uploads) in this scope.
985 auto it =
986 std::find_if(uploads_.begin(), uploads_.end(),
987 [context](const std::unique_ptr<UploadContext>& value) {
988 return value.get() == context;
989 });
990 DCHECK(it != uploads_.end());
991 std::unique_ptr<UploadContext> upload(std::move(*it));
992 uploads_.erase(it);
993
994 if (result == IncidentReportUploader::UPLOAD_SUCCESS)
995 HandleResponse(*upload);
996 // else retry?
997 }
998
OnClientDownloadRequest(download::DownloadItem * download,const ClientDownloadRequest * request)999 void IncidentReportingService::OnClientDownloadRequest(
1000 download::DownloadItem* download,
1001 const ClientDownloadRequest* request) {
1002 if (content::DownloadItemUtils::GetBrowserContext(download) &&
1003 !content::DownloadItemUtils::GetBrowserContext(download)
1004 ->IsOffTheRecord()) {
1005 download_metadata_manager_.SetRequest(download, request);
1006 }
1007 }
1008
1009 } // namespace safe_browsing
1010