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 "components/safe_browsing/content/triggers/ad_redirect_trigger.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/feature_list.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/field_trial_params.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/rand_util.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/task/post_task.h"
18 #include "components/safe_browsing/content/triggers/trigger_util.h"
19 #include "components/safe_browsing/core/features.h"
20 #include "components/safe_browsing/core/triggers/trigger_manager.h"
21 #include "components/safe_browsing/core/triggers/trigger_throttler.h"
22 #include "components/security_interstitials/content/unsafe_resource_util.h"
23 #include "components/security_interstitials/core/unsafe_resource.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/web_contents.h"
29 #include "services/network/public/cpp/shared_url_loader_factory.h"
30 #include "services/network/public/cpp/simple_url_loader.h"
31
32 namespace safe_browsing {
33
34 // Number of milliseconds to allow data collection to run before sending a
35 // report (since this trigger runs in the background).
36 const int64_t kAdRedirectCollectionPeriodMilliseconds = 5000;
37
38 // Range of number of milliseconds to wait after a page finished loading before
39 // starting a report. Allows ads which load in the background to finish loading.
40 const int64_t kMaxAdRedirectCollectionStartDelayMilliseconds = 5000;
41 const int64_t kMinAdRedirectCollectionStartDelayMilliseconds = 500;
42
43 // Metric for tracking what the Ad Redirect trigger does on each navigation.
44 const char kAdRedirectTriggerActionMetricName[] =
45 "SafeBrowsing.Triggers.AdRedirect.Action";
46
47 namespace {
48
RecordAdRedirectTriggerAction(AdRedirectTriggerAction action)49 void RecordAdRedirectTriggerAction(AdRedirectTriggerAction action) {
50 UMA_HISTOGRAM_ENUMERATION(kAdRedirectTriggerActionMetricName, action);
51 }
52
53 } // namespace
54
AdRedirectTrigger(content::WebContents * web_contents,TriggerManager * trigger_manager,PrefService * prefs,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,history::HistoryService * history_service)55 AdRedirectTrigger::AdRedirectTrigger(
56 content::WebContents* web_contents,
57 TriggerManager* trigger_manager,
58 PrefService* prefs,
59 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
60 history::HistoryService* history_service)
61 : web_contents_(web_contents),
62 start_report_delay_ms_(
63 base::RandInt(kMinAdRedirectCollectionStartDelayMilliseconds,
64 kMaxAdRedirectCollectionStartDelayMilliseconds)),
65 finish_report_delay_ms_(kAdRedirectCollectionPeriodMilliseconds),
66 trigger_manager_(trigger_manager),
67 prefs_(prefs),
68 url_loader_factory_(url_loader_factory),
69 history_service_(history_service),
70 task_runner_(
71 base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
72
~AdRedirectTrigger()73 AdRedirectTrigger::~AdRedirectTrigger() {}
74
75 // static
CreateForWebContents(content::WebContents * web_contents,TriggerManager * trigger_manager,PrefService * prefs,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,history::HistoryService * history_service)76 void AdRedirectTrigger::CreateForWebContents(
77 content::WebContents* web_contents,
78 TriggerManager* trigger_manager,
79 PrefService* prefs,
80 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
81 history::HistoryService* history_service) {
82 DCHECK(web_contents);
83 if (!FromWebContents(web_contents)) {
84 web_contents->SetUserData(UserDataKey(),
85 base::WrapUnique(new AdRedirectTrigger(
86 web_contents, trigger_manager, prefs,
87 url_loader_factory, history_service)));
88 }
89 }
90
CreateAdRedirectReport()91 void AdRedirectTrigger::CreateAdRedirectReport() {
92 SBErrorOptions error_options =
93 TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
94 security_interstitials::UnsafeResource resource;
95 resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_REDIRECT;
96 resource.url = web_contents_->GetURL();
97 resource.web_contents_getter = security_interstitials::GetWebContentsGetter(
98 web_contents_->GetMainFrame()->GetProcess()->GetID(),
99 web_contents_->GetMainFrame()->GetRoutingID());
100 TriggerManagerReason reason;
101 if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
102 TriggerType::AD_REDIRECT, web_contents_, resource,
103 url_loader_factory_, history_service_, error_options, &reason)) {
104 if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
105 RecordAdRedirectTriggerAction(
106 AdRedirectTriggerAction::REDIRECT_DAILY_QUOTA_EXCEEDED);
107 } else {
108 RecordAdRedirectTriggerAction(
109 AdRedirectTriggerAction::REDIRECT_COULD_NOT_START_REPORT);
110 }
111 return;
112 }
113 // Call into TriggerManager to finish the reports after a short delay. Any
114 // ads that are detected during this delay will be rejected by TriggerManager
115 // because a report is already being collected, so we won't send multiple
116 // reports for the same page.
117 task_runner_->PostDelayedTask(
118 FROM_HERE,
119 base::BindOnce(
120 IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
121 base::Unretained(trigger_manager_), TriggerType::AD_REDIRECT,
122 base::Unretained(web_contents_), base::TimeDelta(),
123 /*did_proceed=*/false, /*num_visits=*/0, error_options),
124 base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
125 RecordAdRedirectTriggerAction(AdRedirectTriggerAction::AD_REDIRECT);
126 }
127
OnDidBlockNavigation(const GURL & initiator_url)128 void AdRedirectTrigger::OnDidBlockNavigation(const GURL& initiator_url) {
129 RecordAdRedirectTriggerAction(AdRedirectTriggerAction::REDIRECT_CHECK);
130 content::RenderFrameHost* initiator_frame =
131 web_contents_->GetOriginalOpener();
132 // Use focused frame as proxy if there is no opener.
133 if (!initiator_frame)
134 initiator_frame = web_contents_->GetFocusedFrame();
135 if (!DetectGoogleAd(initiator_frame, initiator_url)) {
136 RecordAdRedirectTriggerAction(
137 AdRedirectTriggerAction::REDIRECT_NO_GOOGLE_AD);
138 return;
139 }
140 // Create a report after a short delay.
141 task_runner_->PostDelayedTask(
142 FROM_HERE,
143 base::BindOnce(&AdRedirectTrigger::CreateAdRedirectReport,
144 weak_ptr_factory_.GetWeakPtr()),
145 base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
146 }
147
SetDelayForTest(int start_report_delay,int finish_report_delay)148 void AdRedirectTrigger::SetDelayForTest(int start_report_delay,
149 int finish_report_delay) {
150 start_report_delay_ms_ = start_report_delay;
151 finish_report_delay_ms_ = finish_report_delay;
152 }
153
154 WEB_CONTENTS_USER_DATA_KEY_IMPL(AdRedirectTrigger)
155 } // namespace safe_browsing
156