blob: 350cdb31532094c4efffa44755724fd637917617 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Implementation of the SafeBrowsingBlockingPage class.
#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
#include <memory>
#include "base/feature_list.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/interstitials/enterprise_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/safe_browsing/chrome_controller_client.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/content/browser/threat_details.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/triggers/trigger_manager.h"
#include "components/security_interstitials/content/content_metrics_helper.h"
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/content/unsafe_resource_util.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
using content::BrowserThread;
using content::WebContents;
using security_interstitials::BaseSafeBrowsingErrorUI;
using security_interstitials::SecurityInterstitialControllerClient;
namespace safe_browsing {
namespace {
const char kHelpCenterLink[] = "cpn_safe_browsing";
} // namespace
// static
SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = NULL;
// The default SafeBrowsingBlockingPageFactory. Global, made a singleton so we
// don't leak it.
class SafeBrowsingBlockingPageFactoryImpl
: public SafeBrowsingBlockingPageFactory {
public:
SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
BaseUIManager* ui_manager,
WebContents* web_contents,
const GURL& main_frame_url,
const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources,
bool should_trigger_reporting) override {
// Create appropriate display options for this blocking page.
PrefService* prefs =
Profile::FromBrowserContext(web_contents->GetBrowserContext())
->GetPrefs();
bool is_extended_reporting_opt_in_allowed =
IsExtendedReportingOptInAllowed(*prefs);
bool is_proceed_anyway_disabled =
prefs->GetBoolean(prefs::kSafeBrowsingProceedAnywayDisabled);
// Determine if any prefs need to be updated prior to showing the security
// interstitial. This must happen before querying IsScout to populate the
// Display Options below.
safe_browsing::UpdatePrefsBeforeSecurityInterstitial(prefs);
BaseSafeBrowsingErrorUI::SBErrorDisplayOptions display_options(
BaseBlockingPage::IsMainPageLoadBlocked(unsafe_resources),
is_extended_reporting_opt_in_allowed,
web_contents->GetBrowserContext()->IsOffTheRecord(),
IsExtendedReportingEnabled(*prefs),
IsExtendedReportingPolicyManaged(*prefs),
IsEnhancedProtectionEnabled(*prefs), is_proceed_anyway_disabled,
true, // should_open_links_in_new_tab
true, // always_show_back_to_safety
kHelpCenterLink);
return new SafeBrowsingBlockingPage(
ui_manager, web_contents, main_frame_url, unsafe_resources,
display_options, should_trigger_reporting);
}
private:
friend struct base::LazyInstanceTraitsBase<
SafeBrowsingBlockingPageFactoryImpl>;
SafeBrowsingBlockingPageFactoryImpl() { }
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
};
static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl>::DestructorAtExit
g_safe_browsing_blocking_page_factory_impl = LAZY_INSTANCE_INITIALIZER;
// static
const security_interstitials::SecurityInterstitialPage::TypeID
SafeBrowsingBlockingPage::kTypeForTesting =
&SafeBrowsingBlockingPage::kTypeForTesting;
SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
BaseUIManager* ui_manager,
WebContents* web_contents,
const GURL& main_frame_url,
const UnsafeResourceList& unsafe_resources,
const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
bool should_trigger_reporting,
network::SharedURLLoaderFactory* url_loader_for_testing)
: BaseBlockingPage(
ui_manager,
web_contents,
main_frame_url,
unsafe_resources,
CreateControllerClient(web_contents, unsafe_resources, ui_manager),
display_options),
threat_details_in_progress_(false) {
// Make sure the safe browsing service is available - it may not be when
// shutting down.
if (!g_browser_process->safe_browsing_service())
return;
// Start computing threat details. Trigger Manager will decide if it's safe to
// begin collecting data at this time. The report will be sent only if the
// user opts-in on the blocking page later.
// If there's more than one malicious resources, it means the user clicked
// through the first warning, so we don't prepare additional reports.
if (unsafe_resources.size() == 1 &&
ShouldReportThreatDetails(unsafe_resources[0].threat_type)) {
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
url_loader_for_testing
? url_loader_for_testing
: content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess();
if (should_trigger_reporting) {
threat_details_in_progress_ =
g_browser_process->safe_browsing_service()
->trigger_manager()
->StartCollectingThreatDetails(
TriggerType::SECURITY_INTERSTITIAL, web_contents,
unsafe_resources[0], url_loader_factory,
HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS),
sb_error_ui()->get_error_display_options());
}
}
}
SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {
}
security_interstitials::SecurityInterstitialPage::TypeID
SafeBrowsingBlockingPage::GetTypeForTesting() {
return SafeBrowsingBlockingPage::kTypeForTesting;
}
void SafeBrowsingBlockingPage::OnInterstitialClosing() {
// With committed interstitials OnProceed and OnDontProceed don't get
// called, so call FinishThreatDetails from here.
FinishThreatDetails((proceeded() ? base::TimeDelta::FromMilliseconds(
threat_details_proceed_delay())
: base::TimeDelta()),
proceeded(), controller()->metrics_helper()->NumVisits());
if (!proceeded()) {
OnDontProceedDone();
}
BaseBlockingPage::OnInterstitialClosing();
}
void SafeBrowsingBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
bool did_proceed,
int num_visits) {
// Not all interstitials collect threat details (eg., incognito mode).
if (!threat_details_in_progress_)
return;
// Make sure the safe browsing service is available - it may not be when
// shutting down.
if (!g_browser_process->safe_browsing_service())
return;
// Finish computing threat details. TriggerManager will decide if its safe to
// send the report.
bool report_sent = g_browser_process->safe_browsing_service()
->trigger_manager()
->FinishCollectingThreatDetails(
TriggerType::SECURITY_INTERSTITIAL, web_contents(),
delay, did_proceed, num_visits,
sb_error_ui()->get_error_display_options());
if (report_sent) {
controller()->metrics_helper()->RecordUserInteraction(
security_interstitials::MetricsHelper::EXTENDED_REPORTING_IS_ENABLED);
}
}
// static
SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage(
BaseUIManager* ui_manager,
WebContents* web_contents,
const GURL& main_frame_url,
const UnsafeResource& unsafe_resource,
bool should_trigger_reporting) {
UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.BlockingPage.ResourceType",
unsafe_resource.resource_type);
const UnsafeResourceList resources{unsafe_resource};
// Set up the factory if this has not been done already (tests do that
// before this method is called).
if (!factory_)
factory_ = g_safe_browsing_blocking_page_factory_impl.Pointer();
return factory_->CreateSafeBrowsingPage(ui_manager, web_contents,
main_frame_url, resources,
should_trigger_reporting);
}
// static
std::unique_ptr<SecurityInterstitialControllerClient>
SafeBrowsingBlockingPage::CreateControllerClient(
WebContents* web_contents,
const UnsafeResourceList& unsafe_resources,
const BaseUIManager* ui_manager) {
Profile* profile = Profile::FromBrowserContext(
web_contents->GetBrowserContext());
DCHECK(profile);
std::unique_ptr<ContentMetricsHelper> metrics_helper =
std::make_unique<ContentMetricsHelper>(
HistoryServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
ServiceAccessType::EXPLICIT_ACCESS),
unsafe_resources[0].url, GetReportingInfo(unsafe_resources));
return std::make_unique<ChromeControllerClient>(
web_contents, std::move(metrics_helper), profile->GetPrefs(),
ui_manager->app_locale(), ui_manager->default_safe_page());
}
} // namespace safe_browsing