OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/base_blocking_page.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/lazy_instance.h" |
| 9 #include "base/memory/ptr_util.h" |
| 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/time/time.h" |
| 12 #include "components/safe_browsing_db/safe_browsing_prefs.h" |
| 13 #include "components/security_interstitials/content/security_interstitial_contro
ller_client.h" |
| 14 #include "components/security_interstitials/core/metrics_helper.h" |
| 15 #include "content/public/browser/interstitial_page.h" |
| 16 #include "content/public/browser/navigation_entry.h" |
| 17 #include "content/public/browser/user_metrics.h" |
| 18 #include "content/public/browser/web_contents.h" |
| 19 |
| 20 using base::UserMetricsAction; |
| 21 using content::InterstitialPage; |
| 22 using content::WebContents; |
| 23 using security_interstitials::SafeBrowsingErrorUI; |
| 24 using security_interstitials::SecurityInterstitialControllerClient; |
| 25 |
| 26 namespace safe_browsing { |
| 27 |
| 28 namespace { |
| 29 |
| 30 base::LazyInstance<BaseBlockingPage::UnsafeResourceMap>::Leaky |
| 31 g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER; |
| 32 |
| 33 } // namespace |
| 34 |
| 35 BaseBlockingPage::BaseBlockingPage( |
| 36 BaseUIManager* ui_manager, |
| 37 WebContents* web_contents, |
| 38 const GURL& main_frame_url, |
| 39 const UnsafeResourceList& unsafe_resources, |
| 40 std::unique_ptr<SecurityInterstitialControllerClient> controller_client, |
| 41 const SafeBrowsingErrorUI::SBErrorDisplayOptions& display_options) |
| 42 : SecurityInterstitialPage(web_contents, |
| 43 unsafe_resources[0].url, |
| 44 std::move(controller_client)), |
| 45 ui_manager_(ui_manager), |
| 46 main_frame_url_(main_frame_url), |
| 47 navigation_entry_index_to_remove_( |
| 48 IsMainPageLoadBlocked(unsafe_resources) ? |
| 49 -1 : |
| 50 web_contents->GetController().GetLastCommittedEntryIndex()), |
| 51 unsafe_resources_(unsafe_resources), |
| 52 sb_error_ui_(base::MakeUnique<SafeBrowsingErrorUI>( |
| 53 unsafe_resources_[0].url, main_frame_url_, |
| 54 GetInterstitialReason(unsafe_resources_), |
| 55 display_options, |
| 56 ui_manager->app_locale(), |
| 57 base::Time::NowFromSystemTime(), |
| 58 controller())), |
| 59 proceeded_(false) {} |
| 60 |
| 61 BaseBlockingPage::~BaseBlockingPage() {} |
| 62 |
| 63 // static |
| 64 const SafeBrowsingErrorUI::SBErrorDisplayOptions |
| 65 BaseBlockingPage::CreateDefaultDisplayOptions( |
| 66 const UnsafeResourceList& unsafe_resources) { |
| 67 return SafeBrowsingErrorUI::SBErrorDisplayOptions( |
| 68 IsMainPageLoadBlocked(unsafe_resources), |
| 69 true, // kSafeBrowsingExtendedReportingOptInAllowed |
| 70 false, // is_off_the_record |
| 71 false, // is_extended_reporting |
| 72 false, // is_scout |
| 73 false); // kSafeBrowsingProceedAnywayDisabled |
| 74 } |
| 75 |
| 76 // static |
| 77 void BaseBlockingPage::ShowBlockingPage( |
| 78 BaseUIManager* ui_manager, |
| 79 const UnsafeResource& unsafe_resource) { |
| 80 WebContents* web_contents = unsafe_resource.web_contents_getter.Run(); |
| 81 |
| 82 if (!InterstitialPage::GetInterstitialPage(web_contents) || |
| 83 !unsafe_resource.is_subresource) { |
| 84 // There is no interstitial currently showing in that tab, or we are about |
| 85 // to display a new one for the main frame. If there is already an |
| 86 // interstitial, showing the new one will automatically hide the old one. |
| 87 content::NavigationEntry* entry = |
| 88 unsafe_resource.GetNavigationEntryForResource(); |
| 89 const UnsafeResourceList resources{unsafe_resource}; |
| 90 BaseBlockingPage* blocking_page = |
| 91 new BaseBlockingPage( |
| 92 ui_manager, web_contents, |
| 93 entry ? entry->GetURL() : GURL(), |
| 94 resources, |
| 95 CreateControllerClient( |
| 96 web_contents, resources, |
| 97 ui_manager->history_service(web_contents), |
| 98 ui_manager->app_locale(), |
| 99 ui_manager->default_safe_page()), |
| 100 CreateDefaultDisplayOptions(resources)); |
| 101 blocking_page->Show(); |
| 102 return; |
| 103 } |
| 104 |
| 105 // This is an interstitial for a page's resource, let's queue it. |
| 106 UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap(); |
| 107 (*unsafe_resource_map)[web_contents].push_back(unsafe_resource); |
| 108 } |
| 109 |
| 110 // static |
| 111 bool BaseBlockingPage::IsMainPageLoadBlocked( |
| 112 const UnsafeResourceList& unsafe_resources) { |
| 113 // If there is more than one unsafe resource, the main page load must not be |
| 114 // blocked. Otherwise, check if the one resource is. |
| 115 return unsafe_resources.size() == 1 && |
| 116 unsafe_resources[0].IsMainPageLoadBlocked(); |
| 117 } |
| 118 |
| 119 void BaseBlockingPage::OnProceed() { |
| 120 proceeded_ = true; |
| 121 |
| 122 ui_manager_->OnBlockingPageDone(unsafe_resources_, true /* proceed */, |
| 123 web_contents(), main_frame_url_); |
| 124 } |
| 125 |
| 126 void BaseBlockingPage::OnDontProceed() { |
| 127 // We could have already called Proceed(), in which case we must not notify |
| 128 // the SafeBrowsingUIManager again, as the client has been deleted. |
| 129 if (proceeded_) |
| 130 return; |
| 131 |
| 132 if (!sb_error_ui_->is_proceed_anyway_disabled()) { |
| 133 controller()->metrics_helper()->RecordUserDecision( |
| 134 security_interstitials::MetricsHelper::DONT_PROCEED); |
| 135 } |
| 136 |
| 137 // Send the malware details, if we opted to. |
| 138 FinishThreatDetails(base::TimeDelta(), false /* did_proceed */, |
| 139 controller()->metrics_helper()->NumVisits()); // No delay |
| 140 |
| 141 ui_manager_->OnBlockingPageDone(unsafe_resources_, false /* proceed */, |
| 142 web_contents(), main_frame_url_); |
| 143 |
| 144 // The user does not want to proceed, clear the queued unsafe resources |
| 145 // notifications we received while the interstitial was showing. |
| 146 UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap(); |
| 147 UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents()); |
| 148 if (iter != unsafe_resource_map->end() && !iter->second.empty()) { |
| 149 ui_manager_->OnBlockingPageDone(iter->second, false, web_contents(), |
| 150 main_frame_url_); |
| 151 unsafe_resource_map->erase(iter); |
| 152 } |
| 153 |
| 154 // We don't remove the navigation entry if the tab is being destroyed as this |
| 155 // would trigger a navigation that would cause trouble as the render view host |
| 156 // for the tab has by then already been destroyed. We also don't delete the |
| 157 // current entry if it has been committed again, which is possible on a page |
| 158 // that had a subresource warning. |
| 159 const int last_committed_index = |
| 160 web_contents()->GetController().GetLastCommittedEntryIndex(); |
| 161 if (navigation_entry_index_to_remove_ != -1 && |
| 162 navigation_entry_index_to_remove_ != last_committed_index && |
| 163 !web_contents()->IsBeingDestroyed()) { |
| 164 CHECK(web_contents()->GetController().RemoveEntryAtIndex( |
| 165 navigation_entry_index_to_remove_)); |
| 166 } |
| 167 } |
| 168 |
| 169 void BaseBlockingPage::CommandReceived( |
| 170 const std::string& page_cmd) { |
| 171 if (page_cmd == "\"pageLoadComplete\"") { |
| 172 // content::WaitForRenderFrameReady sends this message when the page |
| 173 // load completes. Ignore it. |
| 174 return; |
| 175 } |
| 176 |
| 177 int command = 0; |
| 178 bool retval = base::StringToInt(page_cmd, &command); |
| 179 DCHECK(retval) << page_cmd; |
| 180 |
| 181 sb_error_ui_->HandleCommand( |
| 182 static_cast<security_interstitials::SecurityInterstitialCommands>( |
| 183 command)); |
| 184 } |
| 185 |
| 186 bool BaseBlockingPage::ShouldCreateNewNavigation() const { |
| 187 return sb_error_ui_->is_main_frame_load_blocked(); |
| 188 } |
| 189 |
| 190 void BaseBlockingPage::PopulateInterstitialStrings( |
| 191 base::DictionaryValue* load_time_data) { |
| 192 sb_error_ui_->PopulateStringsForHTML(load_time_data); |
| 193 } |
| 194 |
| 195 void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay, |
| 196 bool did_proceed, |
| 197 int num_visits) {} |
| 198 |
| 199 // static |
| 200 BaseBlockingPage::UnsafeResourceMap* |
| 201 BaseBlockingPage::GetUnsafeResourcesMap() { |
| 202 return g_unsafe_resource_map.Pointer(); |
| 203 } |
| 204 |
| 205 // static |
| 206 std::string BaseBlockingPage::GetMetricPrefix( |
| 207 const UnsafeResourceList& unsafe_resources, |
| 208 SafeBrowsingErrorUI::SBInterstitialReason interstitial_reason) { |
| 209 bool primary_subresource = unsafe_resources[0].is_subresource; |
| 210 switch (interstitial_reason) { |
| 211 case SafeBrowsingErrorUI::SB_REASON_MALWARE: |
| 212 return primary_subresource ? "malware_subresource" : "malware"; |
| 213 case SafeBrowsingErrorUI::SB_REASON_HARMFUL: |
| 214 return primary_subresource ? "harmful_subresource" : "harmful"; |
| 215 case SafeBrowsingErrorUI::SB_REASON_PHISHING: |
| 216 ThreatPatternType threat_pattern_type = |
| 217 unsafe_resources[0].threat_metadata.threat_pattern_type; |
| 218 if (threat_pattern_type == ThreatPatternType::PHISHING || |
| 219 threat_pattern_type == ThreatPatternType::NONE) |
| 220 return primary_subresource ? "phishing_subresource" : "phishing"; |
| 221 else if (threat_pattern_type == ThreatPatternType::SOCIAL_ENGINEERING_ADS) |
| 222 return primary_subresource ? "social_engineering_ads_subresource" |
| 223 : "social_engineering_ads"; |
| 224 else if (threat_pattern_type == |
| 225 ThreatPatternType::SOCIAL_ENGINEERING_LANDING) |
| 226 return primary_subresource ? "social_engineering_landing_subresource" |
| 227 : "social_engineering_landing"; |
| 228 } |
| 229 NOTREACHED(); |
| 230 return "unkown_metric_prefix"; |
| 231 } |
| 232 |
| 233 // We populate a parallel set of metrics to differentiate some threat sources. |
| 234 // static |
| 235 std::string BaseBlockingPage::GetExtraMetricsSuffix( |
| 236 const UnsafeResourceList& unsafe_resources) { |
| 237 switch (unsafe_resources[0].threat_source) { |
| 238 case safe_browsing::ThreatSource::DATA_SAVER: |
| 239 return "from_data_saver"; |
| 240 case safe_browsing::ThreatSource::REMOTE: |
| 241 case safe_browsing::ThreatSource::LOCAL_PVER3: |
| 242 // REMOTE and LOCAL_PVER3 can be distinguished in the logs |
| 243 // by platform type: Remote is mobile, local_pver3 is desktop. |
| 244 return "from_device"; |
| 245 case safe_browsing::ThreatSource::LOCAL_PVER4: |
| 246 return "from_device_v4"; |
| 247 case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION: |
| 248 return "from_client_side_detection"; |
| 249 case safe_browsing::ThreatSource::UNKNOWN: |
| 250 break; |
| 251 } |
| 252 NOTREACHED(); |
| 253 return std::string(); |
| 254 } |
| 255 |
| 256 // static |
| 257 SafeBrowsingErrorUI::SBInterstitialReason |
| 258 BaseBlockingPage::GetInterstitialReason( |
| 259 const UnsafeResourceList& unsafe_resources) { |
| 260 bool harmful = false; |
| 261 for (UnsafeResourceList::const_iterator iter = unsafe_resources.begin(); |
| 262 iter != unsafe_resources.end(); ++iter) { |
| 263 const BaseUIManager::UnsafeResource& resource = *iter; |
| 264 safe_browsing::SBThreatType threat_type = resource.threat_type; |
| 265 if (threat_type == SB_THREAT_TYPE_URL_MALWARE || |
| 266 threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) { |
| 267 return SafeBrowsingErrorUI::SB_REASON_MALWARE; |
| 268 } else if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) { |
| 269 harmful = true; |
| 270 } else { |
| 271 DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING || |
| 272 threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL); |
| 273 } |
| 274 } |
| 275 |
| 276 if (harmful) |
| 277 return SafeBrowsingErrorUI::SB_REASON_HARMFUL; |
| 278 return SafeBrowsingErrorUI::SB_REASON_PHISHING; |
| 279 } |
| 280 |
| 281 BaseUIManager* BaseBlockingPage::ui_manager() const { |
| 282 return ui_manager_; |
| 283 } |
| 284 |
| 285 const GURL BaseBlockingPage::main_frame_url() const { |
| 286 return main_frame_url_; |
| 287 } |
| 288 |
| 289 BaseBlockingPage::UnsafeResourceList |
| 290 BaseBlockingPage::unsafe_resources() const { |
| 291 return unsafe_resources_; |
| 292 } |
| 293 |
| 294 SafeBrowsingErrorUI* BaseBlockingPage::sb_error_ui() const { |
| 295 return sb_error_ui_.get(); |
| 296 } |
| 297 |
| 298 void BaseBlockingPage::set_proceeded(bool proceeded) { |
| 299 proceeded_ = proceeded; |
| 300 } |
| 301 |
| 302 // static |
| 303 std::unique_ptr<SecurityInterstitialControllerClient> |
| 304 BaseBlockingPage::CreateControllerClient( |
| 305 content::WebContents* web_contents, |
| 306 const UnsafeResourceList& unsafe_resources, |
| 307 history::HistoryService* history_service, |
| 308 const std::string& app_locale, |
| 309 const GURL& default_safe_page) { |
| 310 SafeBrowsingErrorUI::SBInterstitialReason interstitial_reason = |
| 311 GetInterstitialReason(unsafe_resources); |
| 312 security_interstitials::MetricsHelper::ReportDetails reporting_info; |
| 313 reporting_info.metric_prefix = |
| 314 GetMetricPrefix(unsafe_resources, interstitial_reason); |
| 315 reporting_info.extra_suffix = GetExtraMetricsSuffix(unsafe_resources); |
| 316 |
| 317 std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper = |
| 318 base::MakeUnique<security_interstitials::MetricsHelper>( |
| 319 unsafe_resources[0].url, reporting_info, history_service); |
| 320 |
| 321 return base::MakeUnique<SecurityInterstitialControllerClient>( |
| 322 web_contents, std::move(metrics_helper), nullptr, app_locale, |
| 323 default_safe_page); |
| 324 } |
| 325 |
| 326 } // namespace safe_browsing |
OLD | NEW |