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