1 // Copyright (c) 2012 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 // Implementation of the SafeBrowsingBlockingPage class.
6
7 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
8
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/i18n/rtl.h"
14 #include "base/lazy_instance.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/metrics/histogram.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/history/history_service_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/renderer_preferences_util.h"
28 #include "chrome/browser/safe_browsing/malware_details.h"
29 #include "chrome/browser/safe_browsing/ui_manager.h"
30 #include "chrome/browser/tab_contents/tab_util.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/url_constants.h"
34 #include "chrome/grit/generated_resources.h"
35 #include "chrome/grit/locale_settings.h"
36 #include "components/google/core/browser/google_util.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/interstitial_page.h"
39 #include "content/public/browser/navigation_controller.h"
40 #include "content/public/browser/user_metrics.h"
41 #include "content/public/browser/web_contents.h"
42 #include "grit/browser_resources.h"
43 #include "net/base/escape.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/base/webui/jstemplate_builder.h"
47 #include "ui/base/webui/web_ui_util.h"
48
49 #if defined(ENABLE_EXTENSIONS)
50 #include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
51 #endif
52
53 using base::UserMetricsAction;
54 using content::BrowserThread;
55 using content::InterstitialPage;
56 using content::OpenURLParams;
57 using content::Referrer;
58 using content::WebContents;
59
60 #if defined(ENABLE_EXTENSIONS)
61 using extensions::ExperienceSamplingEvent;
62 #endif
63
64 namespace {
65
66 // For malware interstitial pages, we link the problematic URL to Google's
67 // diagnostic page.
68 #if defined(GOOGLE_CHROME_BUILD)
69 const char* const kSbDiagnosticUrl =
70 "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=googlechrome";
71 #else
72 const char* const kSbDiagnosticUrl =
73 "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
74 #endif
75
76 // URL for malware and phishing, V2.
77 const char kLearnMoreMalwareUrlV2[] =
78 "https://www.google.com/transparencyreport/safebrowsing/";
79 const char kLearnMorePhishingUrlV2[] =
80 "https://www.google.com/transparencyreport/safebrowsing/";
81
82 const char kPrivacyLinkHtml[] =
83 "<a id=\"privacy-link\" href=\"\" onclick=\"sendCommand('showPrivacy'); "
84 "return false;\" onmousedown=\"return false;\">%s</a>";
85
86 // After a malware interstitial where the user opted-in to the report
87 // but clicked "proceed anyway", we delay the call to
88 // MalwareDetails::FinishCollection() by this much time (in
89 // milliseconds).
90 const int64 kMalwareDetailsProceedDelayMilliSeconds = 3000;
91
92 // The commands returned by the page when the user performs an action.
93 const char kDoReportCommand[] = "doReport";
94 const char kDontReportCommand[] = "dontReport";
95 const char kExpandedSeeMoreCommand[] = "expandedSeeMore";
96 const char kLearnMoreCommand[] = "learnMore2";
97 const char kProceedCommand[] = "proceed";
98 const char kShowDiagnosticCommand[] = "showDiagnostic";
99 const char kShowPrivacyCommand[] = "showPrivacy";
100 const char kTakeMeBackCommand[] = "takeMeBack";
101
102 // Other constants used to communicate with the JavaScript.
103 const char kBoxChecked[] = "boxchecked";
104 const char kDisplayCheckBox[] = "displaycheckbox";
105
106 // Constants for the Experience Sampling instrumentation.
107 #if defined(ENABLE_EXTENSIONS)
108 const char kEventNameMalware[] = "safebrowsing_interstitial_";
109 const char kEventNameHarmful[] = "harmful_interstitial_";
110 const char kEventNamePhishing[] = "phishing_interstitial_";
111 const char kEventNameOther[] = "safebrowsing_other_interstitial_";
112 #endif
113
114 base::LazyInstance<SafeBrowsingBlockingPage::UnsafeResourceMap>
115 g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
116
117 } // namespace
118
119 // static
120 SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = NULL;
121
122 // The default SafeBrowsingBlockingPageFactory. Global, made a singleton so we
123 // don't leak it.
124 class SafeBrowsingBlockingPageFactoryImpl
125 : public SafeBrowsingBlockingPageFactory {
126 public:
CreateSafeBrowsingPage(SafeBrowsingUIManager * ui_manager,WebContents * web_contents,const SafeBrowsingBlockingPage::UnsafeResourceList & unsafe_resources)127 virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
128 SafeBrowsingUIManager* ui_manager,
129 WebContents* web_contents,
130 const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
131 OVERRIDE {
132 return new SafeBrowsingBlockingPage(ui_manager, web_contents,
133 unsafe_resources);
134 }
135
136 private:
137 friend struct base::DefaultLazyInstanceTraits<
138 SafeBrowsingBlockingPageFactoryImpl>;
139
SafeBrowsingBlockingPageFactoryImpl()140 SafeBrowsingBlockingPageFactoryImpl() { }
141
142 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
143 };
144
145 static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl>
146 g_safe_browsing_blocking_page_factory_impl = LAZY_INSTANCE_INITIALIZER;
147
SafeBrowsingBlockingPage(SafeBrowsingUIManager * ui_manager,WebContents * web_contents,const UnsafeResourceList & unsafe_resources)148 SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
149 SafeBrowsingUIManager* ui_manager,
150 WebContents* web_contents,
151 const UnsafeResourceList& unsafe_resources)
152 : malware_details_proceed_delay_ms_(
153 kMalwareDetailsProceedDelayMilliSeconds),
154 ui_manager_(ui_manager),
155 report_loop_(NULL),
156 is_main_frame_load_blocked_(IsMainPageLoadBlocked(unsafe_resources)),
157 unsafe_resources_(unsafe_resources),
158 proceeded_(false),
159 web_contents_(web_contents),
160 url_(unsafe_resources[0].url),
161 interstitial_page_(NULL),
162 create_view_(true),
163 num_visits_(-1) {
164 bool malware = false;
165 bool harmful = false;
166 bool phishing = false;
167 for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin();
168 iter != unsafe_resources_.end(); ++iter) {
169 const UnsafeResource& resource = *iter;
170 SBThreatType threat_type = resource.threat_type;
171 if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
172 threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
173 malware = true;
174 } else if (threat_type == SB_THREAT_TYPE_URL_HARMFUL) {
175 harmful = true;
176 } else {
177 DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
178 threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL);
179 phishing = true;
180 }
181 }
182 DCHECK(phishing || malware || harmful);
183 if (malware)
184 interstitial_type_ = TYPE_MALWARE;
185 else if (harmful)
186 interstitial_type_ = TYPE_HARMFUL;
187 else
188 interstitial_type_ = TYPE_PHISHING;
189
190 RecordUserDecision(SHOW);
191 RecordUserInteraction(TOTAL_VISITS);
192 if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled))
193 RecordUserDecision(PROCEEDING_DISABLED);
194
195 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
196 Profile::FromBrowserContext(web_contents->GetBrowserContext()),
197 Profile::EXPLICIT_ACCESS);
198 if (history_service) {
199 history_service->GetVisibleVisitCountToHost(
200 url_,
201 base::Bind(&SafeBrowsingBlockingPage::OnGotHistoryCount,
202 base::Unretained(this)),
203 &request_tracker_);
204 }
205
206 if (!is_main_frame_load_blocked_) {
207 navigation_entry_index_to_remove_ =
208 web_contents->GetController().GetLastCommittedEntryIndex();
209 } else {
210 navigation_entry_index_to_remove_ = -1;
211 }
212
213 // Start computing malware details. They will be sent only
214 // if the user opts-in on the blocking page later.
215 // If there's more than one malicious resources, it means the user
216 // clicked through the first warning, so we don't prepare additional
217 // reports.
218 if (unsafe_resources.size() == 1 &&
219 unsafe_resources[0].threat_type == SB_THREAT_TYPE_URL_MALWARE &&
220 malware_details_.get() == NULL && CanShowMalwareDetailsOption()) {
221 malware_details_ = MalwareDetails::NewMalwareDetails(
222 ui_manager_, web_contents, unsafe_resources[0]);
223 }
224
225 #if defined(ENABLE_EXTENSIONS)
226 // ExperienceSampling: Set up new sampling event for this interstitial.
227 // This needs to handle all types of warnings this interstitial can show.
228 std::string event_name;
229 switch (interstitial_type_) {
230 case TYPE_MALWARE:
231 event_name = kEventNameMalware;
232 break;
233 case TYPE_HARMFUL:
234 event_name = kEventNameHarmful;
235 break;
236 case TYPE_PHISHING:
237 event_name = kEventNamePhishing;
238 break;
239 default:
240 event_name = kEventNameOther;
241 break;
242 }
243 sampling_event_.reset(new ExperienceSamplingEvent(
244 event_name,
245 url_,
246 web_contents_->GetLastCommittedURL(),
247 web_contents_->GetBrowserContext()));
248 #endif
249
250 // Creating interstitial_page_ without showing it leaks memory, so don't
251 // create it here.
252 }
253
CanShowMalwareDetailsOption()254 bool SafeBrowsingBlockingPage::CanShowMalwareDetailsOption() {
255 return (!web_contents_->GetBrowserContext()->IsOffTheRecord() &&
256 web_contents_->GetURL().SchemeIs(url::kHttpScheme));
257 }
258
~SafeBrowsingBlockingPage()259 SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {
260 }
261
CommandReceived(const std::string & cmd)262 void SafeBrowsingBlockingPage::CommandReceived(const std::string& cmd) {
263 std::string command(cmd); // Make a local copy so we can modify it.
264 // The Jasonified response has quotes, remove them.
265 if (command.length() > 1 && command[0] == '"') {
266 command = command.substr(1, command.length() - 2);
267 }
268 if (command == kDoReportCommand) {
269 SetReportingPreference(true);
270 return;
271 }
272
273 if (command == kDontReportCommand) {
274 SetReportingPreference(false);
275 return;
276 }
277
278 if (command == kLearnMoreCommand) {
279 // User pressed "Learn more".
280 RecordUserInteraction(SHOW_LEARN_MORE);
281 GURL learn_more_url(interstitial_type_ == TYPE_PHISHING ?
282 kLearnMorePhishingUrlV2 : kLearnMoreMalwareUrlV2);
283 learn_more_url = google_util::AppendGoogleLocaleParam(
284 learn_more_url, g_browser_process->GetApplicationLocale());
285 OpenURLParams params(learn_more_url,
286 Referrer(),
287 CURRENT_TAB,
288 ui::PAGE_TRANSITION_LINK,
289 false);
290 web_contents_->OpenURL(params);
291 return;
292 }
293
294 if (command == kShowPrivacyCommand) {
295 // User pressed "Safe Browsing privacy policy".
296 RecordUserInteraction(SHOW_PRIVACY_POLICY);
297 GURL privacy_url(
298 l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_URL));
299 privacy_url = google_util::AppendGoogleLocaleParam(
300 privacy_url, g_browser_process->GetApplicationLocale());
301 OpenURLParams params(privacy_url,
302 Referrer(),
303 CURRENT_TAB,
304 ui::PAGE_TRANSITION_LINK,
305 false);
306 web_contents_->OpenURL(params);
307 return;
308 }
309
310 bool proceed_blocked = false;
311 if (command == kProceedCommand) {
312 if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
313 proceed_blocked = true;
314 } else {
315 RecordUserDecision(PROCEED);
316 interstitial_page_->Proceed();
317 // |this| has been deleted after Proceed() returns.
318 return;
319 }
320 }
321
322 if (command == kTakeMeBackCommand || proceed_blocked) {
323 // Don't record the user action here because there are other ways of
324 // triggering DontProceed, like clicking the back button.
325 if (is_main_frame_load_blocked_) {
326 // If the load is blocked, we want to close the interstitial and discard
327 // the pending entry.
328 interstitial_page_->DontProceed();
329 // |this| has been deleted after DontProceed() returns.
330 return;
331 }
332
333 // Otherwise the offending entry has committed, and we need to go back or
334 // to a safe page. We will close the interstitial when that page commits.
335 if (web_contents_->GetController().CanGoBack()) {
336 web_contents_->GetController().GoBack();
337 } else {
338 web_contents_->GetController().LoadURL(
339 GURL(chrome::kChromeUINewTabURL),
340 content::Referrer(),
341 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
342 std::string());
343 }
344 return;
345 }
346
347 // The "report error" and "show diagnostic" commands can have a number
348 // appended to them, which is the index of the element they apply to.
349 size_t element_index = 0;
350 size_t colon_index = command.find(':');
351 if (colon_index != std::string::npos) {
352 DCHECK(colon_index < command.size() - 1);
353 int result_int = 0;
354 bool result = base::StringToInt(base::StringPiece(command.begin() +
355 colon_index + 1,
356 command.end()),
357 &result_int);
358 command = command.substr(0, colon_index);
359 if (result)
360 element_index = static_cast<size_t>(result_int);
361 }
362
363 if (element_index >= unsafe_resources_.size()) {
364 NOTREACHED();
365 return;
366 }
367
368 std::string bad_url_spec = unsafe_resources_[element_index].url.spec();
369 if (command == kShowDiagnosticCommand) {
370 // We're going to take the user to Google's SafeBrowsing diagnostic page.
371 RecordUserInteraction(SHOW_DIAGNOSTIC);
372 std::string diagnostic =
373 base::StringPrintf(kSbDiagnosticUrl,
374 net::EscapeQueryParamValue(bad_url_spec, true).c_str());
375 GURL diagnostic_url(diagnostic);
376 diagnostic_url = google_util::AppendGoogleLocaleParam(
377 diagnostic_url, g_browser_process->GetApplicationLocale());
378 DCHECK(unsafe_resources_[element_index].threat_type ==
379 SB_THREAT_TYPE_URL_MALWARE ||
380 unsafe_resources_[element_index].threat_type ==
381 SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
382 OpenURLParams params(
383 diagnostic_url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_LINK,
384 false);
385 web_contents_->OpenURL(params);
386 return;
387 }
388
389 if (command == kExpandedSeeMoreCommand) {
390 RecordUserInteraction(SHOW_ADVANCED);
391 return;
392 }
393
394 NOTREACHED() << "Unexpected command: " << command;
395 }
396
OverrideRendererPrefs(content::RendererPreferences * prefs)397 void SafeBrowsingBlockingPage::OverrideRendererPrefs(
398 content::RendererPreferences* prefs) {
399 Profile* profile = Profile::FromBrowserContext(
400 web_contents_->GetBrowserContext());
401 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
402 }
403
SetReportingPreference(bool report)404 void SafeBrowsingBlockingPage::SetReportingPreference(bool report) {
405 Profile* profile = Profile::FromBrowserContext(
406 web_contents_->GetBrowserContext());
407 PrefService* pref = profile->GetPrefs();
408 pref->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, report);
409 UMA_HISTOGRAM_BOOLEAN("SB2.SetExtendedReportingEnabled", report);
410 }
411
OnProceed()412 void SafeBrowsingBlockingPage::OnProceed() {
413 proceeded_ = true;
414 // Send the malware details, if we opted to.
415 FinishMalwareDetails(malware_details_proceed_delay_ms_);
416
417 NotifySafeBrowsingUIManager(ui_manager_, unsafe_resources_, true);
418
419 // Check to see if some new notifications of unsafe resources have been
420 // received while we were showing the interstitial.
421 UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
422 UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents_);
423 SafeBrowsingBlockingPage* blocking_page = NULL;
424 if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
425 // Build an interstitial for all the unsafe resources notifications.
426 // Don't show it now as showing an interstitial while an interstitial is
427 // already showing would cause DontProceed() to be invoked.
428 blocking_page = factory_->CreateSafeBrowsingPage(ui_manager_, web_contents_,
429 iter->second);
430 unsafe_resource_map->erase(iter);
431 }
432
433 // Now that this interstitial is gone, we can show the new one.
434 if (blocking_page)
435 blocking_page->Show();
436 }
437
DontCreateViewForTesting()438 void SafeBrowsingBlockingPage::DontCreateViewForTesting() {
439 create_view_ = false;
440 }
441
Show()442 void SafeBrowsingBlockingPage::Show() {
443 DCHECK(!interstitial_page_);
444 interstitial_page_ = InterstitialPage::Create(
445 web_contents_, is_main_frame_load_blocked_, url_, this);
446 if (!create_view_)
447 interstitial_page_->DontCreateViewForTesting();
448 interstitial_page_->Show();
449 }
450
OnDontProceed()451 void SafeBrowsingBlockingPage::OnDontProceed() {
452 // We could have already called Proceed(), in which case we must not notify
453 // the SafeBrowsingUIManager again, as the client has been deleted.
454 if (proceeded_)
455 return;
456
457 if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled))
458 RecordUserDecision(DONT_PROCEED);
459
460 // Send the malware details, if we opted to.
461 FinishMalwareDetails(0); // No delay
462
463 NotifySafeBrowsingUIManager(ui_manager_, unsafe_resources_, false);
464
465 // The user does not want to proceed, clear the queued unsafe resources
466 // notifications we received while the interstitial was showing.
467 UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
468 UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents_);
469 if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
470 NotifySafeBrowsingUIManager(ui_manager_, iter->second, false);
471 unsafe_resource_map->erase(iter);
472 }
473
474 // We don't remove the navigation entry if the tab is being destroyed as this
475 // would trigger a navigation that would cause trouble as the render view host
476 // for the tab has by then already been destroyed. We also don't delete the
477 // current entry if it has been committed again, which is possible on a page
478 // that had a subresource warning.
479 int last_committed_index =
480 web_contents_->GetController().GetLastCommittedEntryIndex();
481 if (navigation_entry_index_to_remove_ != -1 &&
482 navigation_entry_index_to_remove_ != last_committed_index &&
483 !web_contents_->IsBeingDestroyed()) {
484 CHECK(web_contents_->GetController().RemoveEntryAtIndex(
485 navigation_entry_index_to_remove_));
486 navigation_entry_index_to_remove_ = -1;
487 }
488 }
489
OnGotHistoryCount(bool success,int num_visits,base::Time first_visit)490 void SafeBrowsingBlockingPage::OnGotHistoryCount(bool success,
491 int num_visits,
492 base::Time first_visit) {
493 if (success)
494 num_visits_ = num_visits;
495 }
496
RecordUserDecision(Decision decision)497 void SafeBrowsingBlockingPage::RecordUserDecision(Decision decision) {
498 switch (interstitial_type_) {
499 case TYPE_MALWARE:
500 UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision",
501 decision,
502 MAX_DECISION);
503 break;
504 case TYPE_HARMFUL:
505 UMA_HISTOGRAM_ENUMERATION("interstitial.harmful.decision",
506 decision,
507 MAX_DECISION);
508 break;
509 case TYPE_PHISHING:
510 UMA_HISTOGRAM_ENUMERATION("interstitial.phishing.decision",
511 decision,
512 MAX_DECISION);
513 break;
514 }
515
516 #if defined(ENABLE_EXTENSIONS)
517 if (sampling_event_.get()) {
518 switch (decision) {
519 case PROCEED:
520 sampling_event_->CreateUserDecisionEvent(
521 ExperienceSamplingEvent::kProceed);
522 break;
523 case DONT_PROCEED:
524 sampling_event_->CreateUserDecisionEvent(
525 ExperienceSamplingEvent::kDeny);
526 break;
527 case SHOW:
528 case PROCEEDING_DISABLED:
529 case MAX_DECISION:
530 break;
531 }
532 }
533 #endif
534
535 // Record additional information about malware sites that users have
536 // visited before.
537 if (num_visits_ < 1 || interstitial_type_ != TYPE_MALWARE)
538 return;
539 if (decision == PROCEED || decision == DONT_PROCEED) {
540 UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision.repeat_visit",
541 SHOW,
542 MAX_DECISION);
543 UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision.repeat_visit",
544 decision,
545 MAX_DECISION);
546 }
547 }
548
RecordUserInteraction(Interaction interaction)549 void SafeBrowsingBlockingPage::RecordUserInteraction(Interaction interaction) {
550 switch (interstitial_type_) {
551 case TYPE_MALWARE:
552 UMA_HISTOGRAM_ENUMERATION("interstitial.malware.interaction",
553 interaction,
554 MAX_INTERACTION);
555 break;
556 case TYPE_HARMFUL:
557 UMA_HISTOGRAM_ENUMERATION("interstitial.harmful.interaction",
558 interaction,
559 MAX_INTERACTION);
560 break;
561 case TYPE_PHISHING:
562 UMA_HISTOGRAM_ENUMERATION("interstitial.phishing.interaction",
563 interaction,
564 MAX_INTERACTION);
565 break;
566 }
567
568 #if defined(ENABLE_EXTENSIONS)
569 if (!sampling_event_.get())
570 return;
571 switch (interaction) {
572 case SHOW_LEARN_MORE:
573 sampling_event_->set_has_viewed_learn_more(true);
574 break;
575 case SHOW_ADVANCED:
576 sampling_event_->set_has_viewed_details(true);
577 break;
578 case SHOW_PRIVACY_POLICY:
579 case SHOW_DIAGNOSTIC:
580 case TOTAL_VISITS:
581 case MAX_INTERACTION:
582 break;
583 }
584 #endif
585 }
586
FinishMalwareDetails(int64 delay_ms)587 void SafeBrowsingBlockingPage::FinishMalwareDetails(int64 delay_ms) {
588 if (malware_details_.get() == NULL)
589 return; // Not all interstitials have malware details (eg phishing).
590
591 const bool enabled =
592 IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled);
593 UMA_HISTOGRAM_BOOLEAN("SB2.ExtendedReportingIsEnabled", enabled);
594 if (enabled) {
595 // Finish the malware details collection, send it over.
596 BrowserThread::PostDelayedTask(
597 BrowserThread::IO, FROM_HERE,
598 base::Bind(&MalwareDetails::FinishCollection, malware_details_.get()),
599 base::TimeDelta::FromMilliseconds(delay_ms));
600 }
601 }
602
IsPrefEnabled(const char * pref)603 bool SafeBrowsingBlockingPage::IsPrefEnabled(const char* pref) {
604 Profile* profile =
605 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
606 return profile->GetPrefs()->GetBoolean(pref);
607 }
608
609 // static
NotifySafeBrowsingUIManager(SafeBrowsingUIManager * ui_manager,const UnsafeResourceList & unsafe_resources,bool proceed)610 void SafeBrowsingBlockingPage::NotifySafeBrowsingUIManager(
611 SafeBrowsingUIManager* ui_manager,
612 const UnsafeResourceList& unsafe_resources,
613 bool proceed) {
614 BrowserThread::PostTask(
615 BrowserThread::IO, FROM_HERE,
616 base::Bind(&SafeBrowsingUIManager::OnBlockingPageDone,
617 ui_manager, unsafe_resources, proceed));
618 }
619
620 // static
621 SafeBrowsingBlockingPage::UnsafeResourceMap*
GetUnsafeResourcesMap()622 SafeBrowsingBlockingPage::GetUnsafeResourcesMap() {
623 return g_unsafe_resource_map.Pointer();
624 }
625
626 // static
CreateBlockingPage(SafeBrowsingUIManager * ui_manager,WebContents * web_contents,const UnsafeResource & unsafe_resource)627 SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage(
628 SafeBrowsingUIManager* ui_manager,
629 WebContents* web_contents,
630 const UnsafeResource& unsafe_resource) {
631 std::vector<UnsafeResource> resources;
632 resources.push_back(unsafe_resource);
633 // Set up the factory if this has not been done already (tests do that
634 // before this method is called).
635 if (!factory_)
636 factory_ = g_safe_browsing_blocking_page_factory_impl.Pointer();
637 return factory_->CreateSafeBrowsingPage(ui_manager, web_contents, resources);
638 }
639
640 // static
ShowBlockingPage(SafeBrowsingUIManager * ui_manager,const UnsafeResource & unsafe_resource)641 void SafeBrowsingBlockingPage::ShowBlockingPage(
642 SafeBrowsingUIManager* ui_manager,
643 const UnsafeResource& unsafe_resource) {
644 DVLOG(1) << __FUNCTION__ << " " << unsafe_resource.url.spec();
645 WebContents* web_contents = tab_util::GetWebContentsByID(
646 unsafe_resource.render_process_host_id, unsafe_resource.render_view_id);
647
648 InterstitialPage* interstitial =
649 InterstitialPage::GetInterstitialPage(web_contents);
650 if (interstitial && !unsafe_resource.is_subresource) {
651 // There is already an interstitial showing and we are about to display a
652 // new one for the main frame. Just hide the current one, it is now
653 // irrelevent
654 interstitial->DontProceed();
655 interstitial = NULL;
656 }
657
658 if (!interstitial) {
659 // There are no interstitial currently showing in that tab, go ahead and
660 // show this interstitial.
661 SafeBrowsingBlockingPage* blocking_page =
662 CreateBlockingPage(ui_manager, web_contents, unsafe_resource);
663 blocking_page->Show();
664 return;
665 }
666
667 // This is an interstitial for a page's resource, let's queue it.
668 UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
669 (*unsafe_resource_map)[web_contents].push_back(unsafe_resource);
670 }
671
672 // static
IsMainPageLoadBlocked(const UnsafeResourceList & unsafe_resources)673 bool SafeBrowsingBlockingPage::IsMainPageLoadBlocked(
674 const UnsafeResourceList& unsafe_resources) {
675 // Client-side phishing detection interstitials never block the main frame
676 // load, since they happen after the page is finished loading.
677 if (unsafe_resources[0].threat_type ==
678 SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) {
679 return false;
680 }
681
682 // Otherwise, check the threat type.
683 return unsafe_resources.size() == 1 && !unsafe_resources[0].is_subresource;
684 }
685
GetHTMLContents()686 std::string SafeBrowsingBlockingPage::GetHTMLContents() {
687 DCHECK(!unsafe_resources_.empty());
688
689 // Fill in the shared values.
690 base::DictionaryValue load_time_data;
691 webui::SetFontAndTextDirection(&load_time_data);
692 load_time_data.SetBoolean("ssl", false);
693 load_time_data.SetString(
694 "tabTitle", l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_TITLE));
695 load_time_data.SetString(
696 "openDetails",
697 l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON));
698 load_time_data.SetString(
699 "closeDetails",
700 l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON));
701 load_time_data.SetString(
702 "primaryButtonText",
703 l10n_util::GetStringUTF16(IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON));
704 load_time_data.SetBoolean(
705 "overridable",
706 !IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled));
707
708 switch (interstitial_type_) {
709 case TYPE_MALWARE:
710 PopulateMalwareLoadTimeData(&load_time_data);
711 break;
712 case TYPE_HARMFUL:
713 PopulateHarmfulLoadTimeData(&load_time_data);
714 break;
715 case TYPE_PHISHING:
716 PopulatePhishingLoadTimeData(&load_time_data);
717 break;
718 }
719
720 base::StringPiece html(
721 ResourceBundle::GetSharedInstance().GetRawDataResource(
722 IRD_SECURITY_INTERSTITIAL_HTML));
723 webui::UseVersion2 version;
724 return webui::GetI18nTemplateHtml(html, &load_time_data);
725 }
726
PopulateMalwareLoadTimeData(base::DictionaryValue * load_time_data)727 void SafeBrowsingBlockingPage::PopulateMalwareLoadTimeData(
728 base::DictionaryValue* load_time_data) {
729 load_time_data->SetBoolean("phishing", false);
730 load_time_data->SetString(
731 "heading", l10n_util::GetStringUTF16(IDS_MALWARE_V3_HEADING));
732 load_time_data->SetString(
733 "primaryParagraph",
734 l10n_util::GetStringFUTF16(
735 IDS_MALWARE_V3_PRIMARY_PARAGRAPH,
736 base::UTF8ToUTF16(url_.host())));
737 load_time_data->SetString(
738 "explanationParagraph",
739 is_main_frame_load_blocked_ ?
740 l10n_util::GetStringFUTF16(
741 IDS_MALWARE_V3_EXPLANATION_PARAGRAPH,
742 base::UTF8ToUTF16(url_.host())) :
743 l10n_util::GetStringFUTF16(
744 IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE,
745 base::UTF8ToUTF16(web_contents_->GetURL().host()),
746 base::UTF8ToUTF16(url_.host())));
747 load_time_data->SetString(
748 "finalParagraph",
749 l10n_util::GetStringUTF16(IDS_MALWARE_V3_PROCEED_PARAGRAPH));
750
751 load_time_data->SetBoolean(kDisplayCheckBox, CanShowMalwareDetailsOption());
752 if (CanShowMalwareDetailsOption()) {
753 std::string privacy_link = base::StringPrintf(
754 kPrivacyLinkHtml,
755 l10n_util::GetStringUTF8(
756 IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
757 load_time_data->SetString(
758 "optInLink",
759 l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
760 base::UTF8ToUTF16(privacy_link)));
761 load_time_data->SetBoolean(
762 kBoxChecked,
763 IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
764 }
765 }
766
PopulateHarmfulLoadTimeData(base::DictionaryValue * load_time_data)767 void SafeBrowsingBlockingPage::PopulateHarmfulLoadTimeData(
768 base::DictionaryValue* load_time_data) {
769 load_time_data->SetBoolean("phishing", false);
770 load_time_data->SetString(
771 "heading", l10n_util::GetStringUTF16(IDS_HARMFUL_V3_HEADING));
772 load_time_data->SetString(
773 "primaryParagraph",
774 l10n_util::GetStringFUTF16(
775 IDS_HARMFUL_V3_PRIMARY_PARAGRAPH,
776 base::UTF8ToUTF16(url_.host())));
777 load_time_data->SetString(
778 "explanationParagraph",
779 l10n_util::GetStringFUTF16(
780 IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH,
781 base::UTF8ToUTF16(url_.host())));
782 load_time_data->SetString(
783 "finalParagraph",
784 l10n_util::GetStringUTF16(IDS_HARMFUL_V3_PROCEED_PARAGRAPH));
785
786 load_time_data->SetBoolean(kDisplayCheckBox, CanShowMalwareDetailsOption());
787 if (CanShowMalwareDetailsOption()) {
788 std::string privacy_link = base::StringPrintf(
789 kPrivacyLinkHtml,
790 l10n_util::GetStringUTF8(
791 IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
792 load_time_data->SetString(
793 "optInLink",
794 l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
795 base::UTF8ToUTF16(privacy_link)));
796 load_time_data->SetBoolean(
797 kBoxChecked,
798 IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
799 }
800 }
801
PopulatePhishingLoadTimeData(base::DictionaryValue * load_time_data)802 void SafeBrowsingBlockingPage::PopulatePhishingLoadTimeData(
803 base::DictionaryValue* load_time_data) {
804 load_time_data->SetBoolean("phishing", true);
805 load_time_data->SetString(
806 "heading",
807 l10n_util::GetStringUTF16(IDS_PHISHING_V3_HEADING));
808 load_time_data->SetString(
809 "primaryParagraph",
810 l10n_util::GetStringFUTF16(
811 IDS_PHISHING_V3_PRIMARY_PARAGRAPH,
812 base::UTF8ToUTF16(url_.host())));
813 load_time_data->SetString(
814 "explanationParagraph",
815 l10n_util::GetStringFUTF16(IDS_PHISHING_V3_EXPLANATION_PARAGRAPH,
816 base::UTF8ToUTF16(url_.host())));
817 load_time_data->SetString(
818 "finalParagraph",
819 l10n_util::GetStringUTF16(IDS_PHISHING_V3_PROCEED_PARAGRAPH));
820 }
821