summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsdefresne <sdefresne@chromium.org>2015-12-16 08:25:18 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-16 16:26:15 +0000
commit5eaf7d21d324c5d7a65cb757cfd517f6161dbc87 (patch)
tree46f86df126aafc4eb53c5e2d6ab24432b6ea1964
parent5e3721ac64f5951d1844d7308ce06c4786788565 (diff)
downloadchromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.zip
chromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.tar.gz
chromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.tar.bz2
Upstream iOS implementation of safe browsing service.
BUG=567727 Review URL: https://codereview.chromium.org/1528313002 Cr-Commit-Position: refs/heads/master@{#365538}
-rw-r--r--components/components.gyp2
-rw-r--r--ios/chrome/browser/BUILD.gn2
-rw-r--r--ios/chrome/browser/DEPS2
-rw-r--r--ios/chrome/browser/safe_browsing/BUILD.gn11
-rw-r--r--ios/chrome/browser/safe_browsing/hit_report.cc22
-rw-r--r--ios/chrome/browser/safe_browsing/hit_report.h50
-rw-r--r--ios/chrome/browser/safe_browsing/metadata.proto23
-rw-r--r--ios/chrome/browser/safe_browsing/ping_manager.cc216
-rw-r--r--ios/chrome/browser/safe_browsing/ping_manager.h105
-rw-r--r--ios/chrome/browser/safe_browsing/protocol_manager_helper.cc74
-rw-r--r--ios/chrome/browser/safe_browsing/protocol_manager_helper.h59
-rw-r--r--ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc592
-rw-r--r--ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h167
-rw-r--r--ios/chrome/browser/safe_browsing/safe_browsing_service.cc434
-rw-r--r--ios/chrome/browser/safe_browsing/safe_browsing_service.h227
-rw-r--r--ios/chrome/browser/safe_browsing/ui_manager.cc335
-rw-r--r--ios/chrome/browser/safe_browsing/ui_manager.h164
-rw-r--r--ios/chrome/ios_chrome.gyp36
18 files changed, 2520 insertions, 1 deletions
diff --git a/components/components.gyp b/components/components.gyp
index 57da5e7..a4b3169 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -74,6 +74,7 @@
'query_parser.gypi',
'rappor.gypi',
'renderer_context_menu.gypi',
+ 'safe_browsing_db.gypi',
'search.gypi',
'search_engines.gypi',
'search_provider_logos.gypi',
@@ -124,7 +125,6 @@
'packed_ct_ev_whitelist.gypi',
'page_load_metrics.gypi',
'power.gypi',
- 'safe_browsing_db.gypi',
'safe_json.gypi',
'visitedlink.gypi',
'wallpaper.gypi',
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 150eb46..4323ac0 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -480,6 +480,7 @@ source_set("browser") {
"//components/bookmarks/managed",
"//components/browser_sync/browser",
"//components/browser_sync/common",
+ "//components/certificate_reporting",
"//components/component_updater",
"//components/content_settings/core/browser",
"//components/cookie_config",
@@ -519,6 +520,7 @@ source_set("browser") {
"//components/proxy_config",
"//components/rappor",
"//components/resources",
+ "//components/safe_browsing_db",
"//components/search",
"//components/search_engines",
"//components/security_interstitials/core",
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index d18a618..9ca7243 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -8,6 +8,7 @@ include_rules = [
"+components/bookmarks",
"+components/browser_sync/browser",
"+components/browser_sync/common",
+ "+components/certificate_reporting",
"+components/component_updater",
"+components/content_settings/core",
"+components/cookie_config",
@@ -46,6 +47,7 @@ include_rules = [
"+components/proxy_config",
"+components/rappor",
"+components/rlz",
+ "+components/safe_browsing_db",
"+components/search",
"+components/search_engines",
"+components/security_interstitials",
diff --git a/ios/chrome/browser/safe_browsing/BUILD.gn b/ios/chrome/browser/safe_browsing/BUILD.gn
new file mode 100644
index 0000000..6ae55a6
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2015 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ sources = [
+ "metadata.proto",
+ ]
+}
diff --git a/ios/chrome/browser/safe_browsing/hit_report.cc b/ios/chrome/browser/safe_browsing/hit_report.cc
new file mode 100644
index 0000000..a536e97
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/hit_report.cc
@@ -0,0 +1,22 @@
+// Copyright 2015 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.
+
+#include "ios/chrome/browser/safe_browsing/hit_report.h"
+
+#include "components/metrics/metrics_service.h"
+#include "ios/chrome/browser/application_context.h"
+
+namespace safe_browsing {
+
+HitReport::HitReport() {}
+
+HitReport::~HitReport() {}
+
+bool IsMetricsReportingActive() {
+ const metrics::MetricsService* metrics =
+ GetApplicationContext()->GetMetricsService();
+ return metrics && metrics->reporting_active();
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/hit_report.h b/ios/chrome/browser/safe_browsing/hit_report.h
new file mode 100644
index 0000000..34aa362
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/hit_report.h
@@ -0,0 +1,50 @@
+// Copyright 2015 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.
+//
+// Datastructures that hold details of a Safe Browsing hit for reporting.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_
+
+#include "components/safe_browsing_db/util.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// What service classified this threat as unsafe.
+enum class ThreatSource {
+ UNKNOWN,
+ DATA_SAVER, // From the Data Reduction service.
+ LOCAL_PVER3, // From LocalSafeBrowingDatabaseManager, protocol v3
+ LOCAL_PVER4, // From LocalSafeBrowingDatabaseManager, protocol v4
+ REMOTE, // From RemoteSafeBrowingDatabaseManager
+};
+
+// Data to report about the contents of a particular threat (malware, phishing,
+// unsafe download URL). If post_data is non-empty, the request will be
+// sent as a POST instead of a GET.
+struct HitReport {
+ HitReport();
+ ~HitReport();
+
+ GURL malicious_url;
+ GURL page_url;
+ GURL referrer_url;
+
+ bool is_subresource;
+ SBThreatType threat_type;
+ ThreatSource threat_source;
+ bool is_extended_reporting;
+ bool is_metrics_reporting_active;
+
+ std::string post_data;
+};
+
+// Return true if the user has opted in to UMA metrics reporting.
+// Used when filling out a HitReport.
+bool IsMetricsReportingActive();
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_
diff --git a/ios/chrome/browser/safe_browsing/metadata.proto b/ios/chrome/browser/safe_browsing/metadata.proto
new file mode 100644
index 0000000..d37c15a
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/metadata.proto
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package safe_browsing;
+
+// Everything below this comment was copied from the page
+// <https://developers.google.com/safe-browsing/developers_guide_v3>,
+// section "Full Hash Metadata".
+
+// Metadata for the goog-malware-shavar list.
+message MalwarePatternType {
+ enum PATTERN_TYPE {
+ LANDING = 1;
+ DISTRIBUTION = 2;
+ }
+
+ required PATTERN_TYPE pattern_type = 1;
+}
diff --git a/ios/chrome/browser/safe_browsing/ping_manager.cc b/ios/chrome/browser/safe_browsing/ping_manager.cc
new file mode 100644
index 0000000..ebeb344
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/ping_manager.cc
@@ -0,0 +1,216 @@
+// Copyright 2015 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.
+
+#include "ios/chrome/browser/safe_browsing/ping_manager.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/certificate_reporting/error_reporter.h"
+#include "google_apis/google_api_keys.h"
+#include "ios/web/public/web_thread.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/ssl/ssl_info.h"
+#include "net/url_request/certificate_report_sender.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace {
+// URLs to upload invalid certificate chain reports. The HTTP URL is
+// preferred since a client seeing an invalid cert might not be able to
+// make an HTTPS connection to report it.
+const char kExtendedReportingUploadUrlInsecure[] =
+ "http://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
+ "chrome-certs";
+const char kExtendedReportingUploadUrlSecure[] =
+ "https://sb-ssl.google.com/safebrowsing/clientreport/chrome-certs";
+} // namespace
+
+namespace safe_browsing {
+
+// SafeBrowsingPingManager implementation ----------------------------------
+
+// static
+SafeBrowsingPingManager* SafeBrowsingPingManager::Create(
+ net::URLRequestContextGetter* request_context_getter,
+ const SafeBrowsingProtocolConfig& config) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ return new SafeBrowsingPingManager(request_context_getter, config);
+}
+
+SafeBrowsingPingManager::SafeBrowsingPingManager(
+ net::URLRequestContextGetter* request_context_getter,
+ const SafeBrowsingProtocolConfig& config)
+ : client_name_(config.client_name),
+ request_context_getter_(request_context_getter),
+ url_prefix_(config.url_prefix) {
+ DCHECK(!url_prefix_.empty());
+
+ if (request_context_getter) {
+ // Set the upload URL and whether or not to send cookies with
+ // certificate reports sent to Safe Browsing servers.
+ bool use_insecure_certificate_upload_url =
+ certificate_reporting::ErrorReporter::IsHttpUploadUrlSupported();
+
+ net::CertificateReportSender::CookiesPreference cookies_preference;
+ GURL certificate_upload_url;
+ if (use_insecure_certificate_upload_url) {
+ cookies_preference = net::CertificateReportSender::DO_NOT_SEND_COOKIES;
+ certificate_upload_url = GURL(kExtendedReportingUploadUrlInsecure);
+ } else {
+ cookies_preference = net::CertificateReportSender::SEND_COOKIES;
+ certificate_upload_url = GURL(kExtendedReportingUploadUrlSecure);
+ }
+
+ certificate_error_reporter_.reset(new certificate_reporting::ErrorReporter(
+ request_context_getter->GetURLRequestContext(), certificate_upload_url,
+ cookies_preference));
+ }
+
+ version_ = SafeBrowsingProtocolManagerHelper::Version();
+}
+
+SafeBrowsingPingManager::~SafeBrowsingPingManager() {
+ // Delete in-progress safebrowsing reports (hits and details).
+ STLDeleteContainerPointers(safebrowsing_reports_.begin(),
+ safebrowsing_reports_.end());
+}
+
+// net::URLFetcherDelegate implementation ----------------------------------
+
+// All SafeBrowsing request responses are handled here.
+void SafeBrowsingPingManager::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ Reports::iterator sit = safebrowsing_reports_.find(source);
+ DCHECK(sit != safebrowsing_reports_.end());
+ delete *sit;
+ safebrowsing_reports_.erase(sit);
+}
+
+// Sends a SafeBrowsing "hit" report.
+void SafeBrowsingPingManager::ReportSafeBrowsingHit(
+ const safe_browsing::HitReport& hit_report) {
+ GURL report_url = SafeBrowsingHitUrl(hit_report);
+ net::URLFetcher* report =
+ net::URLFetcher::Create(report_url, hit_report.post_data.empty()
+ ? net::URLFetcher::GET
+ : net::URLFetcher::POST,
+ this)
+ .release();
+ report->SetLoadFlags(net::LOAD_DISABLE_CACHE);
+ report->SetRequestContext(request_context_getter_.get());
+ if (!hit_report.post_data.empty())
+ report->SetUploadData("text/plain", hit_report.post_data);
+ safebrowsing_reports_.insert(report);
+ report->Start();
+}
+
+// Sends threat details for users who opt-in.
+void SafeBrowsingPingManager::ReportThreatDetails(const std::string& report) {
+ GURL report_url = ThreatDetailsUrl();
+ net::URLFetcher* fetcher =
+ net::URLFetcher::Create(report_url, net::URLFetcher::POST, this)
+ .release();
+ fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
+ fetcher->SetRequestContext(request_context_getter_.get());
+ fetcher->SetUploadData("application/octet-stream", report);
+ // Don't try too hard to send reports on failures.
+ fetcher->SetAutomaticallyRetryOn5xx(false);
+ fetcher->Start();
+ safebrowsing_reports_.insert(fetcher);
+}
+
+void SafeBrowsingPingManager::ReportInvalidCertificateChain(
+ const std::string& serialized_report) {
+ DCHECK(certificate_error_reporter_);
+ certificate_error_reporter_->SendExtendedReportingReport(serialized_report);
+}
+
+void SafeBrowsingPingManager::SetCertificateErrorReporterForTesting(
+ scoped_ptr<certificate_reporting::ErrorReporter>
+ certificate_error_reporter) {
+ certificate_error_reporter_ = certificate_error_reporter.Pass();
+}
+
+GURL SafeBrowsingPingManager::SafeBrowsingHitUrl(
+ const safe_browsing::HitReport& hit_report) const {
+ DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+ hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+ hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+ hit_report.threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL ||
+ hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
+ hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
+ std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
+ url_prefix_, "report", client_name_, version_, std::string(),
+ hit_report.is_extended_reporting);
+
+ std::string threat_list = "none";
+ switch (hit_report.threat_type) {
+ case SB_THREAT_TYPE_URL_MALWARE:
+ threat_list = "malblhit";
+ break;
+ case SB_THREAT_TYPE_URL_PHISHING:
+ threat_list = "phishblhit";
+ break;
+ case SB_THREAT_TYPE_URL_UNWANTED:
+ threat_list = "uwsblhit";
+ break;
+ case SB_THREAT_TYPE_BINARY_MALWARE_URL:
+ threat_list = "binurlhit";
+ break;
+ case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
+ threat_list = "phishcsdhit";
+ break;
+ case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
+ threat_list = "malcsdhit";
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ std::string threat_source = "none";
+ switch (hit_report.threat_source) {
+ case safe_browsing::ThreatSource::DATA_SAVER:
+ threat_source = "ds";
+ break;
+ case safe_browsing::ThreatSource::REMOTE:
+ threat_source = "rem";
+ break;
+ case safe_browsing::ThreatSource::LOCAL_PVER3:
+ threat_source = "l3";
+ break;
+ case safe_browsing::ThreatSource::LOCAL_PVER4:
+ threat_source = "l4";
+ break;
+ case safe_browsing::ThreatSource::UNKNOWN:
+ NOTREACHED();
+ }
+
+ return GURL(base::StringPrintf(
+ "%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d", url.c_str(),
+ threat_list.c_str(),
+ net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(),
+ net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(),
+ net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(),
+ hit_report.is_subresource, threat_source.c_str(),
+ hit_report.is_metrics_reporting_active));
+}
+
+GURL SafeBrowsingPingManager::ThreatDetailsUrl() const {
+ std::string url = base::StringPrintf(
+ "%s/clientreport/malware?client=%s&appver=%s&pver=1.0",
+ url_prefix_.c_str(), client_name_.c_str(), version_.c_str());
+ std::string api_key = google_apis::GetAPIKey();
+ if (!api_key.empty()) {
+ base::StringAppendF(&url, "&key=%s",
+ net::EscapeQueryParamValue(api_key, true).c_str());
+ }
+ return GURL(url);
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/ping_manager.h b/ios/chrome/browser/safe_browsing/ping_manager.h
new file mode 100644
index 0000000..be84b4e
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/ping_manager.h
@@ -0,0 +1,105 @@
+// Copyright 2015 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.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_
+
+// A class that reports safebrowsing statistics to Google's SafeBrowsing
+// servers.
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/safe_browsing_db/util.h"
+#include "ios/chrome/browser/safe_browsing/hit_report.h"
+#include "ios/chrome/browser/safe_browsing/protocol_manager_helper.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+namespace certificate_reporting {
+class ErrorReporter;
+}
+
+namespace net {
+class SSLInfo;
+class URLRequestContextGetter;
+} // namespace net
+
+namespace safe_browsing {
+
+class SafeBrowsingPingManager : public net::URLFetcherDelegate {
+ public:
+ ~SafeBrowsingPingManager() override;
+
+ // Create an instance of the safe browsing ping manager.
+ static SafeBrowsingPingManager* Create(
+ net::URLRequestContextGetter* request_context_getter,
+ const SafeBrowsingProtocolConfig& config);
+
+ // net::URLFetcherDelegate interface.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ // Report to Google when a SafeBrowsing warning is shown to the user.
+ // |hit_report.threat_type| should be one of the types known by
+ // SafeBrowsingtHitUrl.
+ void ReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report);
+
+ // Users can opt-in on the SafeBrowsing interstitial to send detailed
+ // threat reports. |report| is the serialized report.
+ void ReportThreatDetails(const std::string& report);
+
+ // Users can opt-in on the SSL interstitial to send reports of invalid
+ // certificate chains.
+ void ReportInvalidCertificateChain(const std::string& serialized_report);
+
+ void SetCertificateErrorReporterForTesting(
+ scoped_ptr<certificate_reporting::ErrorReporter>
+ certificate_error_reporter);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SafeBrowsingPingManagerTest, TestSafeBrowsingHitUrl);
+ FRIEND_TEST_ALL_PREFIXES(SafeBrowsingPingManagerTest, TestThreatDetailsUrl);
+
+ typedef std::set<const net::URLFetcher*> Reports;
+
+ // Constructs a SafeBrowsingPingManager that issues network requests
+ // using |request_context_getter|.
+ SafeBrowsingPingManager(net::URLRequestContextGetter* request_context_getter,
+ const SafeBrowsingProtocolConfig& config);
+
+ // Generates URL for reporting safe browsing hits.
+ GURL SafeBrowsingHitUrl(const safe_browsing::HitReport& hit_report) const;
+
+ // Generates URL for reporting threat details for users who opt-in.
+ GURL ThreatDetailsUrl() const;
+
+ // Current product version sent in each request.
+ std::string version_;
+
+ // The safe browsing client name sent in each request.
+ std::string client_name_;
+
+ // The context we use to issue network requests.
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+
+ // URL prefix where browser reports hits to the safebrowsing list and
+ // sends detaild threat reports for UMA users.
+ std::string url_prefix_;
+
+ // Track outstanding SafeBrowsing report fetchers for clean up.
+ // We add both "hit" and "detail" fetchers in this set.
+ Reports safebrowsing_reports_;
+
+ // Sends reports of invalid SSL certificate chains.
+ scoped_ptr<certificate_reporting::ErrorReporter> certificate_error_reporter_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingPingManager);
+};
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_
diff --git a/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc b/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc
new file mode 100644
index 0000000..2783727
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc
@@ -0,0 +1,74 @@
+// Copyright 2015 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.
+
+#include "ios/chrome/browser/safe_browsing/protocol_manager_helper.h"
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/version_info/version_info.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+
+namespace safe_browsing {
+
+SafeBrowsingProtocolConfig::SafeBrowsingProtocolConfig()
+ : disable_auto_update(false) {}
+
+SafeBrowsingProtocolConfig::~SafeBrowsingProtocolConfig() {}
+
+// static
+std::string SafeBrowsingProtocolManagerHelper::Version() {
+ if (version_info::GetVersionNumber().empty())
+ return "0.1";
+ else
+ return version_info::GetVersionNumber();
+}
+
+// static
+std::string SafeBrowsingProtocolManagerHelper::ComposeUrl(
+ const std::string& prefix,
+ const std::string& method,
+ const std::string& client_name,
+ const std::string& version,
+ const std::string& additional_query) {
+ DCHECK(!prefix.empty() && !method.empty() && !client_name.empty() &&
+ !version.empty());
+ std::string url =
+ base::StringPrintf("%s/%s?client=%s&appver=%s&pver=3.0", prefix.c_str(),
+ method.c_str(), client_name.c_str(), version.c_str());
+ std::string api_key = google_apis::GetAPIKey();
+ if (!api_key.empty()) {
+ base::StringAppendF(&url, "&key=%s",
+ net::EscapeQueryParamValue(api_key, true).c_str());
+ }
+ if (!additional_query.empty()) {
+ DCHECK(url.find("?") != std::string::npos);
+ url.append("&");
+ url.append(additional_query);
+ }
+ return url;
+}
+
+// static
+std::string SafeBrowsingProtocolManagerHelper::ComposeUrl(
+ const std::string& prefix,
+ const std::string& method,
+ const std::string& client_name,
+ const std::string& version,
+ const std::string& additional_query,
+ bool is_extended_reporting) {
+ std::string url =
+ ComposeUrl(prefix, method, client_name, version, additional_query);
+ if (is_extended_reporting) {
+ url.append("&ext=1");
+ } else {
+ url.append("&ext=0");
+ }
+ return url;
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/protocol_manager_helper.h b/ios/chrome/browser/safe_browsing/protocol_manager_helper.h
new file mode 100644
index 0000000..cafda86
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/protocol_manager_helper.h
@@ -0,0 +1,59 @@
+// Copyright 2015 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.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_
+
+// A class that provides common functionality for safebrowsing protocol managers
+// that communicate with Google servers.
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace safe_browsing {
+
+struct SafeBrowsingProtocolConfig {
+ SafeBrowsingProtocolConfig();
+ ~SafeBrowsingProtocolConfig();
+ std::string client_name;
+ std::string url_prefix;
+ std::string backup_connect_error_url_prefix;
+ std::string backup_http_error_url_prefix;
+ std::string backup_network_error_url_prefix;
+ std::string version;
+ bool disable_auto_update;
+};
+
+class SafeBrowsingProtocolManagerHelper {
+ public:
+ // returns version
+ static std::string Version();
+
+ // Composes a URL using |prefix|, |method| (e.g.: gethash, download, report).
+ // |client_name| and |version|. When not empty, |additional_query| is
+ // appended to the URL with an additional "&" in the front.
+ static std::string ComposeUrl(const std::string& prefix,
+ const std::string& method,
+ const std::string& client_name,
+ const std::string& version,
+ const std::string& additional_query);
+
+ // Similar to above function, and appends "&ext=1" at the end of URL if
+ // |is_extended_reporting| is true, otherwise, appends "&ext=0".
+ static std::string ComposeUrl(const std::string& prefix,
+ const std::string& method,
+ const std::string& client_name,
+ const std::string& version,
+ const std::string& additional_query,
+ bool is_extended_reporting);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SafeBrowsingProtocolManagerHelper);
+};
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
new file mode 100644
index 0000000..ad3ef67
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -0,0 +1,592 @@
+// Copyright 2015 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 "ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/i18n/rtl.h"
+#include "base/lazy_instance.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/interstitials/ios_chrome_metrics_helper.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/safe_browsing/ui_manager.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/interstitials/web_interstitial.h"
+#include "ios/web/public/navigation_manager.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "ios/web/public/web_thread.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// For malware interstitial pages, we link the problematic URL to Google's
+// diagnostic page.
+#if defined(GOOGLE_CHROME_BUILD)
+const char kSbDiagnosticUrl[] =
+ "https://www.google.com/safebrowsing/"
+ "diagnostic?site=%s&client=googlechrome";
+#else
+const char kSbDiagnosticUrl[] =
+ "https://www.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
+#endif
+
+// URL for malware and phishing, V2.
+const char kLearnMoreMalwareUrlV2[] =
+ "https://www.google.com/transparencyreport/safebrowsing/";
+const char kLearnMorePhishingUrlV2[] =
+ "https://www.google.com/transparencyreport/safebrowsing/";
+
+// Constants for the V4 phishing string upgrades.
+const char kSocialEngineeringTrial[] = "SafeBrowsingSocialEngineeringStrings";
+const char kSocialEngineeringEnabled[] = "Enabled";
+
+// Constants for the Experience Sampling instrumentation.
+const char kEventNameMalware[] = "safebrowsing_interstitial_";
+const char kEventNameHarmful[] = "harmful_interstitial_";
+const char kEventNamePhishing[] = "phishing_interstitial_";
+const char kEventNameOther[] = "safebrowsing_other_interstitial_";
+
+// Constants for the V4 phishing string upgrades.
+const char kReportPhishingErrorUrl[] =
+ "https://www.google.com/safebrowsing/report_error/";
+const char kReportPhishingErrorTrial[] = "SafeBrowsingReportPhishingErrorLink";
+const char kReportPhishingErrorEnabled[] = "Enabled";
+
+base::LazyInstance<SafeBrowsingBlockingPage::UnsafeResourceMap>
+ g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = nullptr;
+
+// The default SafeBrowsingBlockingPageFactory. Global, made a singleton so we
+// don't leak it.
+class SafeBrowsingBlockingPageFactoryImpl
+ : public SafeBrowsingBlockingPageFactory {
+ public:
+ SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
+ SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
+ override {
+ return new SafeBrowsingBlockingPage(ui_manager, web_state,
+ unsafe_resources);
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<
+ SafeBrowsingBlockingPageFactoryImpl>;
+
+ SafeBrowsingBlockingPageFactoryImpl() {}
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
+};
+
+static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl>
+ g_safe_browsing_blocking_page_factory_impl = LAZY_INSTANCE_INITIALIZER;
+
+SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
+ SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const UnsafeResourceList& unsafe_resources)
+ : IOSSecurityInterstitialPage(web_state, unsafe_resources[0].url),
+ ui_manager_(ui_manager),
+ is_main_frame_load_blocked_(IsMainPageLoadBlocked(unsafe_resources)),
+ unsafe_resources_(unsafe_resources),
+ proceeded_(false) {
+ bool malware = false;
+ bool harmful = false;
+ bool phishing = false;
+ for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin();
+ iter != unsafe_resources_.end(); ++iter) {
+ const UnsafeResource& resource = *iter;
+ SBThreatType threat_type = resource.threat_type;
+ if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+ threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
+ malware = true;
+ } else if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) {
+ harmful = true;
+ } else {
+ DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+ threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL);
+ phishing = true;
+ }
+ }
+ DCHECK(phishing || malware || harmful);
+ if (malware)
+ interstitial_reason_ = SB_REASON_MALWARE;
+ else if (harmful)
+ interstitial_reason_ = SB_REASON_HARMFUL;
+ else
+ interstitial_reason_ = SB_REASON_PHISHING;
+
+ // This must be done after calculating |interstitial_reason_| above.
+ security_interstitials::MetricsHelper::ReportDetails reporting_info;
+ reporting_info.metric_prefix = GetMetricPrefix();
+ reporting_info.extra_suffix = GetExtraMetricsSuffix();
+ reporting_info.rappor_prefix = GetRapporPrefix();
+ reporting_info.rappor_report_type = rappor::SAFEBROWSING_RAPPOR_TYPE;
+ SetMetricsHelper(make_scoped_ptr(
+ new IOSChromeMetricsHelper(web_state, request_url(), reporting_info)));
+ GetMetricsHelper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::SHOW);
+ GetMetricsHelper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::TOTAL_VISITS);
+ if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
+ GetMetricsHelper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::PROCEEDING_DISABLED);
+ }
+
+ if (!is_main_frame_load_blocked_) {
+ navigation_entry_index_to_remove_ =
+ web_state->GetNavigationManager()->GetLastCommittedItemIndex();
+ } else {
+ navigation_entry_index_to_remove_ = -1;
+ }
+}
+
+SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {}
+
+void SafeBrowsingBlockingPage::CommandReceived(const std::string& page_cmd) {
+ if (page_cmd == "\"pageLoadComplete\"") {
+ // content::WaitForRenderFrameReady sends this message when the page
+ // load completes. Ignore it.
+ return;
+ }
+
+ int command = 0;
+ bool retval = base::StringToInt(page_cmd, &command);
+ DCHECK(retval) << page_cmd;
+
+ switch (command) {
+ case security_interstitials::CMD_DO_REPORT: {
+ // User enabled SB Extended Reporting via the checkbox.
+ SetReportingPreference(true);
+ break;
+ }
+ case security_interstitials::CMD_DONT_REPORT: {
+ // User disabled SB Extended Reporting via the checkbox.
+ SetReportingPreference(false);
+ break;
+ }
+ case security_interstitials::CMD_OPEN_HELP_CENTER: {
+ // User pressed "Learn more".
+ GetMetricsHelper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
+ GURL learn_more_url(interstitial_reason_ == SB_REASON_PHISHING
+ ? kLearnMorePhishingUrlV2
+ : kLearnMoreMalwareUrlV2);
+ learn_more_url = google_util::AppendGoogleLocaleParam(
+ learn_more_url, GetApplicationContext()->GetApplicationLocale());
+ web_state()->OpenURL(web::WebState::OpenURLParams(
+ learn_more_url, web::Referrer(), CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+ break;
+ }
+ case security_interstitials::CMD_OPEN_REPORTING_PRIVACY: {
+ // User pressed on the SB Extended Reporting "privacy policy" link.
+ OpenExtendedReportingPrivacyPolicy();
+ break;
+ }
+ case security_interstitials::CMD_PROCEED: {
+ // User pressed on the button to proceed.
+ if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
+ GetMetricsHelper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::PROCEED);
+ web_interstitial()->Proceed();
+ // |this| has been deleted after Proceed() returns.
+ break;
+ }
+ // If the user can't proceed, fall through to CMD_DONT_PROCEED.
+ }
+ case security_interstitials::CMD_DONT_PROCEED: {
+ // User pressed on the button to return to safety.
+ // Don't record the user action here because there are other ways of
+ // triggering DontProceed, like clicking the back button.
+ if (is_main_frame_load_blocked_) {
+ // If the load is blocked, we want to close the interstitial and discard
+ // the pending entry.
+ web_interstitial()->DontProceed();
+ // |this| has been deleted after DontProceed() returns.
+ break;
+ }
+
+ // Otherwise the offending entry has committed, and we need to go back or
+ // to a safe page. We will close the interstitial when that page commits.
+ if (web_state()->GetNavigationManager()->CanGoBack()) {
+ web_state()->GetNavigationManager()->GoBack();
+ } else {
+ web_state()->OpenURL(web::WebState::OpenURLParams(
+ GURL(kChromeUINewTabURL), web::Referrer(), CURRENT_TAB,
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
+ }
+ break;
+ }
+ case security_interstitials::CMD_OPEN_DIAGNOSTIC: {
+ // User wants to see why this page is blocked.
+ const UnsafeResource& unsafe_resource = unsafe_resources_[0];
+ std::string bad_url_spec = unsafe_resource.url.spec();
+ GetMetricsHelper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_DIAGNOSTIC);
+ std::string diagnostic = base::StringPrintf(
+ kSbDiagnosticUrl,
+ net::EscapeQueryParamValue(bad_url_spec, true).c_str());
+ GURL diagnostic_url(diagnostic);
+ diagnostic_url = google_util::AppendGoogleLocaleParam(
+ diagnostic_url, GetApplicationContext()->GetApplicationLocale());
+ DCHECK(unsafe_resource.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+ unsafe_resource.threat_type ==
+ SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL ||
+ unsafe_resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED);
+ web_state()->OpenURL(web::WebState::OpenURLParams(
+ diagnostic_url, web::Referrer(), CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+ break;
+ }
+ case security_interstitials::CMD_SHOW_MORE_SECTION: {
+ // User has opened up the hidden text.
+ GetMetricsHelper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_ADVANCED);
+ break;
+ }
+ case security_interstitials::CMD_REPORT_PHISHING_ERROR: {
+ // User wants to report a phishing error.
+ GetMetricsHelper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::REPORT_PHISHING_ERROR);
+ GURL phishing_error_url(kReportPhishingErrorUrl);
+ phishing_error_url = google_util::AppendGoogleLocaleParam(
+ phishing_error_url, GetApplicationContext()->GetApplicationLocale());
+ web_state()->OpenURL(web::WebState::OpenURLParams(
+ phishing_error_url, web::Referrer(), CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+ break;
+ }
+ }
+}
+
+void SafeBrowsingBlockingPage::OnProceed() {
+ proceeded_ = true;
+ ui_manager_->OnBlockingPageDone(unsafe_resources_, true);
+
+ // Check to see if some new notifications of unsafe resources have been
+ // received while we were showing the interstitial.
+ UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+ UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_state());
+ SafeBrowsingBlockingPage* blocking_page = nullptr;
+ if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
+ // Build an interstitial for all the unsafe resources notifications.
+ // Don't show it now as showing an interstitial while an interstitial is
+ // already showing would cause DontProceed() to be invoked.
+ blocking_page = factory_->CreateSafeBrowsingPage(ui_manager_, web_state(),
+ iter->second);
+ unsafe_resource_map->erase(iter);
+ }
+
+ // Now that this interstitial is gone, we can show the new one.
+ if (blocking_page)
+ blocking_page->Show();
+}
+
+bool SafeBrowsingBlockingPage::ShouldCreateNewNavigation() const {
+ return is_main_frame_load_blocked_;
+}
+
+void SafeBrowsingBlockingPage::OnDontProceed() {
+ // We could have already called Proceed(), in which case we must not notify
+ // the SafeBrowsingUIManager again, as the client has been deleted.
+ if (proceeded_)
+ return;
+
+ if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
+ GetMetricsHelper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::DONT_PROCEED);
+ }
+
+ ui_manager_->OnBlockingPageDone(unsafe_resources_, false);
+
+ // The user does not want to proceed, clear the queued unsafe resources
+ // notifications we received while the interstitial was showing.
+ UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+ UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_state());
+ if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
+ ui_manager_->OnBlockingPageDone(iter->second, false);
+ unsafe_resource_map->erase(iter);
+ }
+
+ // We don't remove the navigation entry if the tab is being destroyed as this
+ // would trigger a navigation that would cause trouble as the render view host
+ // for the tab has by then already been destroyed. We also don't delete the
+ // current entry if it has been committed again, which is possible on a page
+ // that had a subresource warning.
+ int last_committed_index =
+ web_state()->GetNavigationManager()->GetLastCommittedItemIndex();
+ if (navigation_entry_index_to_remove_ != -1 &&
+ navigation_entry_index_to_remove_ != last_committed_index &&
+ !web_state()->IsBeingDestroyed()) {
+ CHECK(web_state()->GetNavigationManager()->RemoveItemAtIndex(
+ navigation_entry_index_to_remove_));
+ navigation_entry_index_to_remove_ = -1;
+ }
+}
+
+// static
+SafeBrowsingBlockingPage::UnsafeResourceMap*
+SafeBrowsingBlockingPage::GetUnsafeResourcesMap() {
+ return g_unsafe_resource_map.Pointer();
+}
+
+// static
+SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage(
+ SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const UnsafeResource& unsafe_resource) {
+ std::vector<UnsafeResource> resources;
+ resources.push_back(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_state, resources);
+}
+
+// static
+void SafeBrowsingBlockingPage::ShowBlockingPage(
+ web::WebState* web_state,
+ SafeBrowsingUIManager* ui_manager,
+ const UnsafeResource& unsafe_resource) {
+ DVLOG(1) << __FUNCTION__ << " " << unsafe_resource.url.spec();
+ web::WebInterstitial* web_interstitial =
+ web::WebInterstitial::GetWebInterstitial(web_state);
+ if (web_interstitial && !unsafe_resource.is_subresource) {
+ // There is already an interstitial showing and we are about to display a
+ // new one for the main frame. Just hide the current one, it is now
+ // irrelevent
+ web_interstitial->DontProceed();
+ web_interstitial = nullptr;
+ }
+
+ if (!web_interstitial) {
+ // There are no interstitial currently showing in that tab, go ahead and
+ // show this interstitial.
+ SafeBrowsingBlockingPage* blocking_page =
+ CreateBlockingPage(ui_manager, web_state, unsafe_resource);
+ blocking_page->Show();
+ return;
+ }
+
+ // This is an interstitial for a page's resource, let's queue it.
+ UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+ (*unsafe_resource_map)[web_state].push_back(unsafe_resource);
+}
+
+// static
+bool SafeBrowsingBlockingPage::IsMainPageLoadBlocked(
+ const UnsafeResourceList& unsafe_resources) {
+ // If there is more than one unsafe resource, the main page load must not be
+ // blocked. Otherwise, check if the one resource is.
+ return unsafe_resources.size() == 1 &&
+ unsafe_resources[0].IsMainPageLoadBlocked();
+}
+
+std::string SafeBrowsingBlockingPage::GetMetricPrefix() const {
+ bool primary_subresource = unsafe_resources_[0].is_subresource;
+ switch (interstitial_reason_) {
+ case SB_REASON_MALWARE:
+ return primary_subresource ? "malware_subresource" : "malware";
+ case SB_REASON_HARMFUL:
+ return primary_subresource ? "harmful_subresource" : "harmful";
+ case SB_REASON_PHISHING:
+ return primary_subresource ? "phishing_subresource" : "phishing";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// We populate a parallel set of metrics to differentiate some threat sources.
+std::string SafeBrowsingBlockingPage::GetExtraMetricsSuffix() const {
+ switch (unsafe_resources_[0].threat_source) {
+ case safe_browsing::ThreatSource::DATA_SAVER:
+ return "from_data_saver";
+ case safe_browsing::ThreatSource::REMOTE:
+ case safe_browsing::ThreatSource::LOCAL_PVER3:
+ // REMOTE and LOCAL_PVER3 can be distinguished in the logs
+ // by platform type: Remote is mobile, local_pver3 is desktop.
+ return "from_device";
+ case safe_browsing::ThreatSource::LOCAL_PVER4:
+ return "from_device_v4";
+ case safe_browsing::ThreatSource::UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::string SafeBrowsingBlockingPage::GetRapporPrefix() const {
+ switch (interstitial_reason_) {
+ case SB_REASON_MALWARE:
+ return "malware";
+ case SB_REASON_HARMFUL:
+ return "harmful";
+ case SB_REASON_PHISHING:
+ return "phishing";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::string SafeBrowsingBlockingPage::GetSamplingEventName() const {
+ switch (interstitial_reason_) {
+ case SB_REASON_MALWARE:
+ return kEventNameMalware;
+ case SB_REASON_HARMFUL:
+ return kEventNameHarmful;
+ case SB_REASON_PHISHING:
+ return kEventNamePhishing;
+ default:
+ return kEventNameOther;
+ }
+}
+
+void SafeBrowsingBlockingPage::PopulateInterstitialStrings(
+ base::DictionaryValue* load_time_data) const {
+ CHECK(load_time_data);
+ CHECK(!unsafe_resources_.empty());
+
+ load_time_data->SetString("type", "SAFEBROWSING");
+ load_time_data->SetString(
+ "tabTitle", l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_TITLE));
+ load_time_data->SetString(
+ "openDetails",
+ l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON));
+ load_time_data->SetString(
+ "closeDetails",
+ l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON));
+ load_time_data->SetString(
+ "primaryButtonText", l10n_util::GetStringUTF16(
+ IDS_IOS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON));
+ // TODO(crbug.com/390675): Undo this forkage. This is a temporary fix to make
+ // sure the broken proceed-from-unsafe-resource path can't be hit on iOS.
+ // Always set subresource alerts to be non-overridable. Otherwise obey the
+ // global pref.
+ bool overridable = !unsafe_resources_[0].is_subresource &&
+ !IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled);
+ load_time_data->SetBoolean("overridable", overridable);
+
+ switch (interstitial_reason_) {
+ case SB_REASON_MALWARE:
+ PopulateMalwareLoadTimeData(load_time_data);
+ break;
+ case SB_REASON_HARMFUL:
+ PopulateHarmfulLoadTimeData(load_time_data);
+ break;
+ case SB_REASON_PHISHING:
+ PopulatePhishingLoadTimeData(load_time_data);
+ break;
+ }
+}
+
+void SafeBrowsingBlockingPage::PopulateMalwareLoadTimeData(
+ base::DictionaryValue* load_time_data) const {
+ load_time_data->SetBoolean("phishing", false);
+ load_time_data->SetString(
+ "heading", l10n_util::GetStringUTF16(IDS_IOS_MALWARE_V3_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(IDS_IOS_MALWARE_V3_PRIMARY_PARAGRAPH,
+ GetFormattedHostName()));
+ load_time_data->SetString(
+ "explanationParagraph",
+ is_main_frame_load_blocked_
+ ? l10n_util::GetStringFUTF16(IDS_IOS_MALWARE_V3_EXPLANATION_PARAGRAPH,
+ GetFormattedHostName())
+ : l10n_util::GetStringFUTF16(
+ IDS_IOS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE,
+ base::UTF8ToUTF16(web_state()->GetVisibleURL().host()),
+ GetFormattedHostName()));
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_IOS_MALWARE_V3_PROCEED_PARAGRAPH));
+
+ load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false);
+}
+
+void SafeBrowsingBlockingPage::PopulateHarmfulLoadTimeData(
+ base::DictionaryValue* load_time_data) const {
+ load_time_data->SetBoolean("phishing", false);
+ load_time_data->SetString(
+ "heading", l10n_util::GetStringUTF16(IDS_IOS_HARMFUL_V3_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(IDS_IOS_HARMFUL_V3_PRIMARY_PARAGRAPH,
+ GetFormattedHostName()));
+ load_time_data->SetString(
+ "explanationParagraph",
+ l10n_util::GetStringFUTF16(IDS_IOS_HARMFUL_V3_EXPLANATION_PARAGRAPH,
+ GetFormattedHostName()));
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_IOS_HARMFUL_V3_PROCEED_PARAGRAPH));
+
+ load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false);
+}
+
+void SafeBrowsingBlockingPage::PopulatePhishingLoadTimeData(
+ base::DictionaryValue* load_time_data) const {
+ bool use_social_engineering_strings =
+ base::FieldTrialList::FindFullName(kSocialEngineeringTrial) ==
+ kSocialEngineeringEnabled;
+ load_time_data->SetBoolean("phishing", true);
+ load_time_data->SetString(
+ "heading", l10n_util::GetStringUTF16(use_social_engineering_strings
+ ? IDS_IOS_PHISHING_V4_HEADING
+ : IDS_IOS_PHISHING_V3_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(use_social_engineering_strings
+ ? IDS_IOS_PHISHING_V4_PRIMARY_PARAGRAPH
+ : IDS_IOS_PHISHING_V3_PRIMARY_PARAGRAPH,
+ GetFormattedHostName()));
+ load_time_data->SetString(
+ "explanationParagraph",
+ l10n_util::GetStringFUTF16(IDS_IOS_PHISHING_V3_EXPLANATION_PARAGRAPH,
+ GetFormattedHostName()));
+
+ if (base::FieldTrialList::FindFullName(kReportPhishingErrorTrial) ==
+ kReportPhishingErrorEnabled) {
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(
+ IDS_IOS_PHISHING_V4_PROCEED_AND_REPORT_PARAGRAPH));
+ } else {
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_IOS_PHISHING_V3_PROCEED_PARAGRAPH));
+ }
+
+ load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false);
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
new file mode 100644
index 0000000..cfc2828
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
@@ -0,0 +1,167 @@
+// Copyright 2015 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.
+//
+// Classes for managing the SafeBrowsing interstitial pages.
+//
+// When a user is about to visit a page the SafeBrowsing system has deemed to
+// be malicious, either as malware or a phishing page, we show an interstitial
+// page with some options (go back, continue) to give the user a chance to avoid
+// the harmful page.
+//
+// The SafeBrowsingBlockingPage is created by the SafeBrowsingUIManager on the
+// UI thread when we've determined that a page is malicious. The operation of
+// the blocking page occurs on the UI thread, where it waits for the user to
+// make a decision about what to do: either go back or continue on.
+//
+// The blocking page forwards the result of the user's choice back to the
+// SafeBrowsingUIManager so that we can cancel the request for the new page,
+// or allow it to continue.
+//
+// A web page may contain several resources flagged as malware/phishing. This
+// results into more than one interstitial being shown. On the first unsafe
+// resource received we show an interstitial. Any subsequent unsafe resource
+// notifications while the first interstitial is showing is queued. If the user
+// decides to proceed in the first interstitial, we display all queued unsafe
+// resources in a new interstitial.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "ios/chrome/browser/interstitials/ios_security_interstitial_page.h"
+#include "ios/chrome/browser/safe_browsing/ui_manager.h"
+#include "url/gurl.h"
+
+namespace web {
+class WebState;
+}
+
+namespace safe_browsing {
+
+class SafeBrowsingBlockingPageFactory;
+
+class SafeBrowsingBlockingPage : public IOSSecurityInterstitialPage {
+ public:
+ typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
+ typedef std::vector<UnsafeResource> UnsafeResourceList;
+ typedef std::map<web::WebState*, UnsafeResourceList> UnsafeResourceMap;
+
+ ~SafeBrowsingBlockingPage() override;
+
+ // Creates a blocking page. Use ShowBlockingPage if you don't need to access
+ // the blocking page directly.
+ static SafeBrowsingBlockingPage* CreateBlockingPage(
+ SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const UnsafeResource& unsafe_resource);
+
+ // Shows a blocking page warning the user about phishing/malware for a
+ // specific resource.
+ // You can call this method several times, if an interstitial is already
+ // showing, the new one will be queued and displayed if the user decides
+ // to proceed on the currently showing interstitial.
+ static void ShowBlockingPage(web::WebState* web_state,
+ SafeBrowsingUIManager* ui_manager,
+ const UnsafeResource& resource);
+
+ // Makes the passed |factory| the factory used to instantiate
+ // SafeBrowsingBlockingPage objects. Useful for tests.
+ static void RegisterFactory(SafeBrowsingBlockingPageFactory* factory) {
+ factory_ = factory;
+ }
+
+ // InterstitialPageDelegate method:
+ void OnProceed() override;
+ void OnDontProceed() override;
+ void CommandReceived(const std::string& command) override;
+
+ protected:
+ void UpdateReportingPref(); // Used for the transition from old to new pref.
+
+ // Don't instantiate this class directly, use ShowBlockingPage instead.
+ SafeBrowsingBlockingPage(SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const UnsafeResourceList& unsafe_resources);
+
+ // IOSSecurityInterstitialPage methods:
+ bool ShouldCreateNewNavigation() const override;
+ void PopulateInterstitialStrings(
+ base::DictionaryValue* load_time_data) const override;
+ void AfterShow() override {}
+
+ // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user
+ // should be warned about. They are queued when displaying more than one
+ // interstitial at a time.
+ static UnsafeResourceMap* GetUnsafeResourcesMap();
+
+ // Returns true if the passed |unsafe_resources| is blocking the load of
+ // the main page.
+ static bool IsMainPageLoadBlocked(const UnsafeResourceList& unsafe_resources);
+
+ friend class SafeBrowsingBlockingPageFactoryImpl;
+
+ // For reporting back user actions.
+ SafeBrowsingUIManager* ui_manager_;
+
+ // True if the interstitial is blocking the main page because it is on one
+ // of our lists. False if a subresource is being blocked, or in the case of
+ // client-side detection where the interstitial is shown after page load
+ // finishes.
+ bool is_main_frame_load_blocked_;
+
+ // The index of a navigation entry that should be removed when DontProceed()
+ // is invoked, -1 if not entry should be removed.
+ int navigation_entry_index_to_remove_;
+
+ // The list of unsafe resources this page is warning about.
+ UnsafeResourceList unsafe_resources_;
+
+ bool proceeded_;
+
+ // Which type of Safe Browsing interstitial this is.
+ enum SBInterstitialReason {
+ SB_REASON_MALWARE,
+ SB_REASON_HARMFUL,
+ SB_REASON_PHISHING,
+ } interstitial_reason_;
+
+ // The factory used to instantiate SafeBrowsingBlockingPage objects.
+ // Useful for tests, so they can provide their own implementation of
+ // SafeBrowsingBlockingPage.
+ static SafeBrowsingBlockingPageFactory* factory_;
+
+ private:
+ // Fills the passed dictionary with the values to be passed to the template
+ // when creating the HTML.
+ void PopulateMalwareLoadTimeData(base::DictionaryValue* load_time_data) const;
+ void PopulateHarmfulLoadTimeData(base::DictionaryValue* load_time_data) const;
+ void PopulatePhishingLoadTimeData(
+ base::DictionaryValue* load_time_data) const;
+
+ std::string GetMetricPrefix() const;
+ std::string GetExtraMetricsSuffix() const;
+ std::string GetRapporPrefix() const;
+ std::string GetSamplingEventName() const;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPage);
+};
+
+// Factory for creating SafeBrowsingBlockingPage. Useful for tests.
+class SafeBrowsingBlockingPageFactory {
+ public:
+ virtual ~SafeBrowsingBlockingPageFactory() {}
+
+ virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
+ SafeBrowsingUIManager* ui_manager,
+ web::WebState* web_state,
+ const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) = 0;
+};
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_service.cc b/ios/chrome/browser/safe_browsing/safe_browsing_service.cc
new file mode 100644
index 0000000..2427cf7
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -0,0 +1,434 @@
+// Copyright 2015 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.
+
+#include "ios/chrome/browser/safe_browsing/safe_browsing_service.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/prefs/pref_service.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/chrome_paths.h"
+#include "ios/chrome/browser/chrome_switches.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/safe_browsing/ping_manager.h"
+#include "ios/chrome/browser/safe_browsing/ui_manager.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state_manager.h"
+#include "ios/web/public/web_thread.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/extras/sqlite/cookie_crypto_delegate.h"
+#include "net/extras/sqlite/sqlite_persistent_cookie_store.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace safe_browsing {
+
+namespace {
+
+const base::FilePath::CharType kIOSChromeSafeBrowsingBaseFilename[] =
+ FILE_PATH_LITERAL("Safe Browsing");
+
+// Filename suffix for the cookie database.
+const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies");
+
+// The default URL prefix where browser fetches chunk updates, hashes,
+// and reports safe browsing hits and malware details.
+const char kSbDefaultURLPrefix[] =
+ "https://safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are issues establishing a connection
+// with the server at the primary URL.
+const char kSbBackupConnectErrorURLPrefix[] =
+ "https://alt1-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are HTTP-specific issues with the
+// server at the primary URL.
+const char kSbBackupHttpErrorURLPrefix[] =
+ "https://alt2-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are local network specific issues.
+const char kSbBackupNetworkErrorURLPrefix[] =
+ "https://alt3-safebrowsing.google.com/safebrowsing";
+
+} // namespace
+
+class SafeBrowsingURLRequestContextGetter
+ : public net::URLRequestContextGetter {
+ public:
+ explicit SafeBrowsingURLRequestContextGetter(
+ SafeBrowsingService* sb_service_);
+
+ // Implementation for net::UrlRequestContextGetter.
+ net::URLRequestContext* GetURLRequestContext() override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
+ const override;
+
+ // Shuts down any pending requests using the getter, and nulls out
+ // |sb_service_|.
+ void SafeBrowsingServiceShuttingDown();
+
+ protected:
+ ~SafeBrowsingURLRequestContextGetter() override;
+
+ private:
+ SafeBrowsingService* sb_service_; // Owned by BrowserProcess.
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+};
+
+SafeBrowsingURLRequestContextGetter::SafeBrowsingURLRequestContextGetter(
+ SafeBrowsingService* sb_service)
+ : sb_service_(sb_service),
+ network_task_runner_(
+ web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)) {}
+
+net::URLRequestContext*
+SafeBrowsingURLRequestContextGetter::GetURLRequestContext() {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ // Check if the service has been shut down.
+ if (!sb_service_)
+ return nullptr;
+
+ DCHECK(sb_service_->url_request_context_.get());
+ return sb_service_->url_request_context_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+SafeBrowsingURLRequestContextGetter::GetNetworkTaskRunner() const {
+ return network_task_runner_;
+}
+
+void SafeBrowsingURLRequestContextGetter::SafeBrowsingServiceShuttingDown() {
+ sb_service_ = nullptr;
+ URLRequestContextGetter::NotifyContextShuttingDown();
+}
+
+SafeBrowsingURLRequestContextGetter::~SafeBrowsingURLRequestContextGetter() {}
+
+// static
+SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = nullptr;
+
+// The default SafeBrowsingServiceFactory. Global, made a singleton so we
+// don't leak it.
+class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory {
+ public:
+ SafeBrowsingService* CreateSafeBrowsingService() override {
+ return new SafeBrowsingService();
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<SafeBrowsingServiceFactoryImpl>;
+
+ SafeBrowsingServiceFactoryImpl() {}
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl);
+};
+
+static base::LazyInstance<SafeBrowsingServiceFactoryImpl>::Leaky
+ g_safe_browsing_service_factory_impl = LAZY_INSTANCE_INITIALIZER;
+
+// static
+base::FilePath SafeBrowsingService::GetBaseFilename() {
+ base::FilePath path;
+ bool result = PathService::Get(ios::DIR_USER_DATA, &path);
+ DCHECK(result);
+ return path.Append(kIOSChromeSafeBrowsingBaseFilename);
+}
+
+// static
+SafeBrowsingService* SafeBrowsingService::CreateSafeBrowsingService() {
+ if (!factory_)
+ factory_ = g_safe_browsing_service_factory_impl.Pointer();
+ return factory_->CreateSafeBrowsingService();
+}
+
+SafeBrowsingService::SafeBrowsingService()
+ : ping_manager_(nullptr), enabled_(false), enabled_by_prefs_(false) {}
+
+SafeBrowsingService::~SafeBrowsingService() {
+ // We should have already been shut down. If we're still enabled, then the
+ // database isn't going to be closed properly, which could lead to corruption.
+ DCHECK(!enabled_);
+}
+
+void SafeBrowsingService::Initialize() {
+ url_request_context_getter_ = new SafeBrowsingURLRequestContextGetter(this);
+
+ ui_manager_ = CreateUIManager();
+
+ web::WebThread::PostTask(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingService::InitURLRequestContextOnIOThread, this,
+ make_scoped_refptr(
+ GetApplicationContext()->GetSystemURLRequestContext())));
+
+ // Track the safe browsing preference of existing browser states.
+ // The SafeBrowsingService will be started if any existing browser state has
+ // the preference enabled. It will also listen for updates to the preferences.
+ ios::ChromeBrowserStateManager* browser_state_manager =
+ GetApplicationContext()->GetChromeBrowserStateManager();
+ DCHECK(browser_state_manager);
+
+ std::vector<ios::ChromeBrowserState*> browser_states =
+ browser_state_manager->GetLoadedBrowserStates();
+ DCHECK_GT(browser_states.size(), 0u);
+ for (ios::ChromeBrowserState* browser_state : browser_states) {
+ if (browser_state->IsOffTheRecord())
+ continue;
+ AddPrefService(browser_state->GetPrefs());
+ }
+}
+
+void SafeBrowsingService::ShutDown() {
+ // Deletes the PrefChangeRegistrars, whose dtors also unregister |this| as an
+ // observer of the preferences.
+ STLDeleteValues(&prefs_map_);
+
+ Stop(true);
+
+ web::WebThread::PostNonNestableTask(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingService::DestroyURLRequestContextOnIOThread, this,
+ url_request_context_getter_));
+
+ // Release the URLRequestContextGetter after passing it to the IOThread. It
+ // has to be released now rather than in the destructor because it can only
+ // be deleted on the IOThread, and the SafeBrowsingService outlives the IO
+ // thread.
+ url_request_context_getter_ = nullptr;
+}
+
+// Binhash verification is only enabled for UMA users for now.
+bool SafeBrowsingService::DownloadBinHashNeeded() const {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+
+ return false;
+}
+
+net::URLRequestContextGetter* SafeBrowsingService::url_request_context() {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ return url_request_context_getter_.get();
+}
+
+const scoped_refptr<SafeBrowsingUIManager>& SafeBrowsingService::ui_manager()
+ const {
+ return ui_manager_;
+}
+
+SafeBrowsingPingManager* SafeBrowsingService::ping_manager() const {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ return ping_manager_;
+}
+
+SBThreatType SafeBrowsingService::CheckResponseFromProxyRequestHeaders(
+ scoped_refptr<net::HttpResponseHeaders> headers) {
+ SBThreatType result = SB_THREAT_TYPE_SAFE;
+
+ if (!headers.get())
+ return result;
+
+ // If safebrowsing service is disabled, don't do any check.
+ if (!enabled())
+ return result;
+
+ if (headers->HasHeader("X-Phishing-Url"))
+ result = SB_THREAT_TYPE_URL_PHISHING;
+ else if (headers->HasHeader("X-Malware-Url"))
+ result = SB_THREAT_TYPE_URL_MALWARE;
+
+ return result;
+}
+
+SafeBrowsingUIManager* SafeBrowsingService::CreateUIManager() {
+ return new SafeBrowsingUIManager(this);
+}
+
+void SafeBrowsingService::InitURLRequestContextOnIOThread(
+ net::URLRequestContextGetter* system_url_request_context_getter) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ DCHECK(!url_request_context_.get());
+
+ scoped_refptr<net::SQLitePersistentCookieStore> sqlite_store(
+ new net::SQLitePersistentCookieStore(
+ base::FilePath(GetBaseFilename().value() + kCookiesFile),
+ web::WebThread::GetTaskRunnerForThread(web::WebThread::IO),
+ web::WebThread::GetBlockingPool()->GetSequencedTaskRunner(
+ web::WebThread::GetBlockingPool()->GetSequenceToken()),
+ false, nullptr));
+ scoped_refptr<net::CookieStore> cookie_store =
+ new net::CookieMonster(sqlite_store.get(), nullptr);
+
+ url_request_context_.reset(new net::URLRequestContext);
+ // |system_url_request_context_getter| may be null during tests.
+ if (system_url_request_context_getter) {
+ url_request_context_->CopyFrom(
+ system_url_request_context_getter->GetURLRequestContext());
+ }
+ url_request_context_->set_cookie_store(cookie_store.get());
+}
+
+void SafeBrowsingService::DestroyURLRequestContextOnIOThread(
+ scoped_refptr<SafeBrowsingURLRequestContextGetter> context_getter) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ context_getter->SafeBrowsingServiceShuttingDown();
+ url_request_context_.reset();
+}
+
+SafeBrowsingProtocolConfig SafeBrowsingService::GetProtocolConfig() const {
+ SafeBrowsingProtocolConfig config;
+#if defined(GOOGLE_CHROME_BUILD)
+ config.client_name = "googlechrome";
+#else
+ config.client_name = "chromium";
+#endif
+
+ // Mark client string to allow server to differentiate mobile.
+ config.client_name.append("-i");
+
+ base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ config.disable_auto_update =
+ cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
+ cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
+ config.url_prefix = kSbDefaultURLPrefix;
+ config.backup_connect_error_url_prefix = kSbBackupConnectErrorURLPrefix;
+ config.backup_http_error_url_prefix = kSbBackupHttpErrorURLPrefix;
+ config.backup_network_error_url_prefix = kSbBackupNetworkErrorURLPrefix;
+
+ return config;
+}
+
+void SafeBrowsingService::StartOnIOThread(
+ net::URLRequestContextGetter* url_request_context_getter) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ if (enabled_)
+ return;
+ enabled_ = true;
+
+ SafeBrowsingProtocolConfig config = GetProtocolConfig();
+
+ DCHECK(!ping_manager_);
+ ping_manager_ =
+ SafeBrowsingPingManager::Create(url_request_context_getter, config);
+}
+
+void SafeBrowsingService::StopOnIOThread(bool shutdown) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ ui_manager_->StopOnIOThread(shutdown);
+
+ if (enabled_) {
+ enabled_ = false;
+
+ delete ping_manager_;
+ ping_manager_ = nullptr;
+ }
+}
+
+void SafeBrowsingService::Start() {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+
+ web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingService::StartOnIOThread,
+ this, url_request_context_getter_));
+}
+
+void SafeBrowsingService::Stop(bool shutdown) {
+ web::WebThread::PostTask(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingService::StopOnIOThread, this, shutdown));
+}
+
+void SafeBrowsingService::AddPrefService(PrefService* pref_service) {
+ DCHECK(prefs_map_.find(pref_service) == prefs_map_.end());
+ PrefChangeRegistrar* registrar = new PrefChangeRegistrar();
+ registrar->Init(pref_service);
+ registrar->Add(
+ prefs::kSafeBrowsingEnabled,
+ base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this)));
+ // ClientSideDetectionService will need to be refresh the models
+ // renderers have if extended-reporting changes.
+ registrar->Add(
+ prefs::kSafeBrowsingExtendedReportingEnabled,
+ base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this)));
+ prefs_map_[pref_service] = registrar;
+ RefreshState();
+
+ // Record the current pref state.
+ UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.General",
+ pref_service->GetBoolean(prefs::kSafeBrowsingEnabled));
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Extended",
+ pref_service->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled));
+}
+
+void SafeBrowsingService::RemovePrefService(PrefService* pref_service) {
+ if (prefs_map_.find(pref_service) != prefs_map_.end()) {
+ delete prefs_map_[pref_service];
+ prefs_map_.erase(pref_service);
+ RefreshState();
+ } else {
+ NOTREACHED();
+ }
+}
+
+scoped_ptr<SafeBrowsingService::StateSubscription>
+SafeBrowsingService::RegisterStateCallback(
+ const base::Callback<void(void)>& callback) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ return state_callback_list_.Add(callback);
+}
+
+void SafeBrowsingService::RefreshState() {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ // Check if any browser state requires the service to be active.
+ bool enable = false;
+ std::map<PrefService*, PrefChangeRegistrar*>::iterator iter;
+ for (iter = prefs_map_.begin(); iter != prefs_map_.end(); ++iter) {
+ if (iter->first->GetBoolean(prefs::kSafeBrowsingEnabled)) {
+ enable = true;
+ break;
+ }
+ }
+
+ enabled_by_prefs_ = enable;
+
+ if (enable)
+ Start();
+ else
+ Stop(false);
+
+ state_callback_list_.Notify();
+}
+
+void SafeBrowsingService::SendDownloadRecoveryReport(
+ const std::string& report) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ web::WebThread::PostTask(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingService::OnSendDownloadRecoveryReport, this,
+ report));
+}
+
+void SafeBrowsingService::OnSendDownloadRecoveryReport(
+ const std::string& report) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ if (ping_manager())
+ ping_manager()->ReportThreatDetails(report);
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_service.h b/ios/chrome/browser/safe_browsing/safe_browsing_service.h
new file mode 100644
index 0000000..78e62c1
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -0,0 +1,227 @@
+// Copyright 2015 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.
+//
+// The Safe Browsing service is responsible for downloading anti-phishing and
+// anti-malware tables and checking urls against them.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "components/safe_browsing_db/util.h"
+#include "ios/web/public/web_thread.h"
+
+class PrefChangeRegistrar;
+class PrefService;
+class TrackedPreferenceValidationDelegate;
+
+namespace base {
+class Thread;
+}
+
+namespace net {
+class HttpResponseHeaders;
+class URLRequest;
+class URLRequestContext;
+class URLRequestContextGetter;
+}
+
+namespace safe_browsing {
+class ClientSideDetectionService;
+class DownloadProtectionService;
+struct SafeBrowsingProtocolConfig;
+class SafeBrowsingPingManager;
+class SafeBrowsingServiceFactory;
+class SafeBrowsingUIManager;
+class SafeBrowsingURLRequestContextGetter;
+
+// Construction needs to happen on the main thread.
+// The SafeBrowsingService owns both the UI and Database managers which do
+// the heavylifting of safebrowsing service. Both of these managers stay
+// alive until SafeBrowsingService is destroyed, however, they are disabled
+// permanently when Shutdown method is called.
+class SafeBrowsingService
+ : public base::RefCountedThreadSafe<SafeBrowsingService,
+ web::WebThread::DeleteOnUIThread> {
+ public:
+ // Makes the passed |factory| the factory used to instanciate
+ // a SafeBrowsingService. Useful for tests.
+ static void RegisterFactory(SafeBrowsingServiceFactory* factory) {
+ factory_ = factory;
+ }
+
+ static base::FilePath GetBaseFilename();
+
+ // Create an instance of the safe browsing service.
+ static SafeBrowsingService* CreateSafeBrowsingService();
+
+ // Called on the UI thread to initialize the service.
+ void Initialize();
+
+ // Called on the main thread to let us know that the io_thread is going away.
+ void ShutDown();
+
+ // Called on UI thread to decide if the download file's sha256 hash
+ // should be calculated for safebrowsing.
+ bool DownloadBinHashNeeded() const;
+
+ // Create a protocol config struct.
+ SafeBrowsingProtocolConfig GetProtocolConfig() const;
+
+ // Get current enabled status. Must be called on IO thread.
+ bool enabled() const {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ return enabled_;
+ }
+
+ // Whether the service is enabled by the current set of profiles.
+ bool enabled_by_prefs() const {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ return enabled_by_prefs_;
+ }
+
+ net::URLRequestContextGetter* url_request_context();
+
+ const scoped_refptr<SafeBrowsingUIManager>& ui_manager() const;
+
+ SafeBrowsingPingManager* ping_manager() const;
+
+ // Checks |headers| from a request that has passed through a proxy which
+ // adds safe browsing headers, and returns the threat type indicated by those
+ // headers (or SB_THREAT_TYPE_SAFE if no threat is indicated).
+ SBThreatType CheckResponseFromProxyRequestHeaders(
+ scoped_refptr<net::HttpResponseHeaders> headers);
+
+ // Type for subscriptions to SafeBrowsing service state.
+ typedef base::CallbackList<void(void)>::Subscription StateSubscription;
+
+ // Adds a listener for when SafeBrowsing preferences might have changed.
+ // To get the current state, the callback should call enabled_by_prefs().
+ // Should only be called on the UI thread.
+ scoped_ptr<StateSubscription> RegisterStateCallback(
+ const base::Callback<void(void)>& callback);
+
+ // Sends serialized download recovery report to backend.
+ void SendDownloadRecoveryReport(const std::string& report);
+
+ protected:
+ // Creates the safe browsing service. Need to initialize before using.
+ SafeBrowsingService();
+
+ virtual ~SafeBrowsingService();
+
+ SafeBrowsingUIManager* CreateUIManager();
+
+ private:
+ friend class SafeBrowsingServiceFactoryImpl;
+ friend struct web::WebThread::DeleteOnThread<web::WebThread::UI>;
+ friend class base::DeleteHelper<SafeBrowsingService>;
+ friend class SafeBrowsingURLRequestContextGetter;
+
+ void InitURLRequestContextOnIOThread(
+ net::URLRequestContextGetter* system_url_request_context_getter);
+
+ // Destroys the URLRequest and shuts down the provided getter on the
+ // IO thread.
+ void DestroyURLRequestContextOnIOThread(
+ scoped_refptr<SafeBrowsingURLRequestContextGetter> context_getter);
+
+ // Called to initialize objects that are used on the io_thread. This may be
+ // called multiple times during the life of the SafeBrowsingService.
+ void StartOnIOThread(
+ net::URLRequestContextGetter* url_request_context_getter);
+
+ // Called to stop or shutdown operations on the io_thread. This may be called
+ // multiple times to stop during the life of the SafeBrowsingService. If
+ // shutdown is true, then the operations on the io thread are shutdown
+ // permanently and cannot be restarted.
+ void StopOnIOThread(bool shutdown);
+
+ // Start up SafeBrowsing objects. This can be called at browser start, or when
+ // the user checks the "Enable SafeBrowsing" option in the Advanced options
+ // UI.
+ void Start();
+
+ // Stops the SafeBrowsingService. This can be called when the safe browsing
+ // preference is disabled. When shutdown is true, operation is permanently
+ // shutdown and cannot be restarted.
+ void Stop(bool shutdown);
+
+ // Starts following the safe browsing preference on |pref_service|.
+ void AddPrefService(PrefService* pref_service);
+
+ // Stop following the safe browsing preference on |pref_service|.
+ void RemovePrefService(PrefService* pref_service);
+
+ // Checks if any profile is currently using the safe browsing service, and
+ // starts or stops the service accordingly.
+ void RefreshState();
+
+ void OnSendDownloadRecoveryReport(const std::string& report);
+
+ // The factory used to instanciate a SafeBrowsingService object.
+ // Useful for tests, so they can provide their own implementation of
+ // SafeBrowsingService.
+ static SafeBrowsingServiceFactory* factory_;
+
+ // The SafeBrowsingURLRequestContextGetter used to access
+ // |url_request_context_|. Accessed on UI thread.
+ scoped_refptr<SafeBrowsingURLRequestContextGetter>
+ url_request_context_getter_;
+
+ // The SafeBrowsingURLRequestContext. Accessed on IO thread.
+ scoped_ptr<net::URLRequestContext> url_request_context_;
+
+ // Provides phishing and malware statistics. Accessed on IO thread.
+ SafeBrowsingPingManager* ping_manager_;
+
+ // Whether the service is running. 'enabled_' is used by SafeBrowsingService
+ // on the IO thread during normal operations.
+ bool enabled_;
+
+ // Whether SafeBrowsing is enabled by the current set of profiles.
+ // Accessed on UI thread.
+ bool enabled_by_prefs_;
+
+ // Tracks existing PrefServices, and the safe browsing preference on each.
+ // This is used to determine if any profile is currently using the safe
+ // browsing service, and to start it up or shut it down accordingly.
+ // Accessed on UI thread.
+ std::map<PrefService*, PrefChangeRegistrar*> prefs_map_;
+
+ // Callbacks when SafeBrowsing state might have changed.
+ // Should only be accessed on the UI thread.
+ base::CallbackList<void(void)> state_callback_list_;
+
+ // The UI manager handles showing interstitials. Accessed on both UI and IO
+ // thread.
+ scoped_refptr<SafeBrowsingUIManager> ui_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingService);
+};
+
+// Factory for creating SafeBrowsingService. Useful for tests.
+class SafeBrowsingServiceFactory {
+ public:
+ SafeBrowsingServiceFactory() {}
+ virtual ~SafeBrowsingServiceFactory() {}
+ virtual SafeBrowsingService* CreateSafeBrowsingService() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactory);
+};
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_
diff --git a/ios/chrome/browser/safe_browsing/ui_manager.cc b/ios/chrome/browser/safe_browsing/ui_manager.cc
new file mode 100644
index 0000000..d36e8c0
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/ui_manager.cc
@@ -0,0 +1,335 @@
+// Copyright 2015 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.
+
+#include "ios/chrome/browser/safe_browsing/ui_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/debug/leak_tracker.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/prefs/pref_service.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/safe_browsing/metadata.pb.h"
+#include "ios/chrome/browser/safe_browsing/ping_manager.h"
+#include "ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
+#include "ios/chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#include "ios/web/public/navigation_item.h"
+#include "ios/web/public/navigation_manager.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "ios/web/public/web_thread.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/ssl/ssl_info.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+const void* const kWhitelistKey = &kWhitelistKey;
+
+class WhitelistUrlSet : public base::SupportsUserData::Data {
+ public:
+ WhitelistUrlSet() {}
+
+ bool Contains(const GURL url) {
+ auto iter = set_.find(url.GetWithEmptyPath());
+ return iter != set_.end();
+ }
+
+ void Insert(const GURL url) { set_.insert(url.GetWithEmptyPath()); }
+
+ private:
+ std::set<GURL> set_;
+
+ DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
+};
+
+web::NavigationItem* GetActiveItemForNavigationManager(
+ web::NavigationManager* navigation_manager) {
+ web::NavigationItem* active_item = navigation_manager->GetTransientItem();
+ if (!active_item)
+ active_item = navigation_manager->GetPendingItem();
+ if (!active_item)
+ active_item = navigation_manager->GetLastCommittedItem();
+ return active_item;
+}
+
+} // namespace
+
+namespace safe_browsing {
+
+// SafeBrowsingUIManager::UnsafeResource ---------------------------------------
+
+SafeBrowsingUIManager::UnsafeResource::UnsafeResource()
+ : is_subresource(false),
+ threat_type(SB_THREAT_TYPE_SAFE),
+ threat_source(safe_browsing::ThreatSource::UNKNOWN) {}
+
+SafeBrowsingUIManager::UnsafeResource::~UnsafeResource() {}
+
+bool SafeBrowsingUIManager::UnsafeResource::IsMainPageLoadBlocked() const {
+ // Subresource hits cannot happen until after main page load is committed.
+ if (is_subresource)
+ return false;
+
+ // Client-side phishing detection interstitials never block the main frame
+ // load, since they happen after the page is finished loading.
+ if (threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
+ threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
+ return false;
+ }
+
+ return true;
+}
+
+// SafeBrowsingUIManager -------------------------------------------------------
+
+SafeBrowsingUIManager::SafeBrowsingUIManager(
+ const scoped_refptr<SafeBrowsingService>& service)
+ : sb_service_(service) {}
+
+SafeBrowsingUIManager::~SafeBrowsingUIManager() {}
+
+void SafeBrowsingUIManager::StopOnIOThread(bool shutdown) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ if (shutdown)
+ sb_service_ = nullptr;
+}
+
+void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) {
+ UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
+}
+
+void SafeBrowsingUIManager::OnBlockingPageDone(
+ const std::vector<UnsafeResource>& resources,
+ bool proceed) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ for (const auto& resource : resources) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(
+ FROM_HERE, base::Bind(resource.callback, proceed));
+ }
+
+ if (proceed)
+ AddToWhitelist(resource);
+ }
+}
+
+void SafeBrowsingUIManager::DisplayBlockingPage(
+ const UnsafeResource& resource) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ if (resource.is_subresource && !resource.is_subframe) {
+ // Sites tagged as serving Unwanted Software should only show a warning for
+ // main-frame or sub-frame resource. Similar warning restrictions should be
+ // applied to malware sites tagged as "landing sites" (see "Types of
+ // Malware sites" under
+ // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings).
+ MalwarePatternType proto;
+ if (resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+ (resource.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
+ !resource.threat_metadata.empty() &&
+ proto.ParseFromString(resource.threat_metadata) &&
+ proto.pattern_type() == MalwarePatternType::LANDING)) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(FROM_HERE,
+ base::Bind(resource.callback, true));
+ }
+
+ return;
+ }
+ }
+
+ // The tab might have been closed. If it was closed, just act as if "Don't
+ // Proceed" had been chosen.
+ web::WebState* web_state = resource.weak_web_state.get();
+ if (!web_state) {
+ std::vector<UnsafeResource> resources;
+ resources.push_back(resource);
+ OnBlockingPageDone(resources, false);
+ return;
+ }
+
+ // Check if the user has already ignored a SB warning for the same WebState
+ // and top-level domain.
+ if (IsWhitelisted(resource)) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(FROM_HERE,
+ base::Bind(resource.callback, true));
+ }
+ return;
+ }
+
+ if (resource.threat_type != SB_THREAT_TYPE_SAFE) {
+ HitReport hit_report;
+ hit_report.malicious_url = resource.url;
+ hit_report.page_url = web_state->GetVisibleURL();
+ hit_report.is_subresource = resource.is_subresource;
+ hit_report.threat_type = resource.threat_type;
+ hit_report.threat_source = resource.threat_source;
+
+ web::NavigationItem* item =
+ GetActiveItemForNavigationManager(web_state->GetNavigationManager());
+ if (item) {
+ hit_report.referrer_url = item->GetReferrer().url;
+ }
+
+ // When the malicious url is on the main frame, and resource.original_url
+ // is not the same as the resource.url, that means we have a redirect from
+ // resource.original_url to resource.url.
+ // Also, at this point, page_url points to the _previous_ page that we
+ // were on. We replace page_url with resource.original_url and referrer
+ // with page_url.
+ if (!resource.is_subresource && !resource.original_url.is_empty() &&
+ resource.original_url != resource.url) {
+ hit_report.referrer_url = hit_report.page_url;
+ hit_report.page_url = resource.original_url;
+ }
+
+ ios::ChromeBrowserState* browser_state =
+ ios::ChromeBrowserState::FromBrowserState(web_state->GetBrowserState());
+ hit_report.is_extended_reporting =
+ browser_state &&
+ browser_state->GetPrefs()->GetBoolean(
+ prefs::kSafeBrowsingExtendedReportingEnabled);
+ hit_report.is_metrics_reporting_active =
+ safe_browsing::IsMetricsReportingActive();
+
+ MaybeReportSafeBrowsingHit(hit_report);
+ }
+
+ if (resource.threat_type != SB_THREAT_TYPE_SAFE) {
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnSafeBrowsingHit(resource));
+ }
+ SafeBrowsingBlockingPage::ShowBlockingPage(web_state, this, resource);
+}
+
+// A safebrowsing hit is sent after a blocking page for malware/phishing
+// or after the warning dialog for download urls, only for
+// UMA || extended_reporting users.
+void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit(
+ const HitReport& hit_report) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+
+ // Decide if we should send this report.
+ if (hit_report.is_metrics_reporting_active ||
+ hit_report.is_extended_reporting) {
+ web::WebThread::PostTask(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread,
+ this, hit_report));
+ }
+}
+
+void SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread(
+ const HitReport& hit_report) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ // The service may delete the ping manager (i.e. when user disabling service,
+ // etc). This happens on the IO thread.
+ if (!sb_service_ || !sb_service_->ping_manager())
+ return;
+
+ DVLOG(1) << "ReportSafeBrowsingHit: " << hit_report.malicious_url << " "
+ << hit_report.page_url << " " << hit_report.referrer_url << " "
+ << hit_report.is_subresource << " " << hit_report.threat_type;
+ sb_service_->ping_manager()->ReportSafeBrowsingHit(hit_report);
+}
+
+void SafeBrowsingUIManager::ReportInvalidCertificateChain(
+ const std::string& serialized_report,
+ const base::Closure& callback) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ web::WebThread::PostTaskAndReply(
+ web::WebThread::IO, FROM_HERE,
+ base::Bind(
+ &SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread, this,
+ serialized_report),
+ callback);
+}
+
+void SafeBrowsingUIManager::AddObserver(Observer* observer) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ observer_list_.AddObserver(observer);
+}
+
+void SafeBrowsingUIManager::RemoveObserver(Observer* observer) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ observer_list_.RemoveObserver(observer);
+}
+
+void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread(
+ const std::string& serialized_report) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ // The service may delete the ping manager (i.e. when user disabling service,
+ // etc). This happens on the IO thread.
+ if (!sb_service_ || !sb_service_->ping_manager())
+ return;
+
+ sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report);
+}
+
+// If the user had opted-in to send ThreatDetails, this gets called
+// when the report is ready.
+void SafeBrowsingUIManager::SendSerializedThreatDetails(
+ const std::string& serialized) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+
+ // The service may delete the ping manager (i.e. when user disabling service,
+ // etc). This happens on the IO thread.
+ if (sb_service_.get() == nullptr || sb_service_->ping_manager() == nullptr)
+ return;
+
+ if (!serialized.empty()) {
+ DVLOG(1) << "Sending serialized threat details.";
+ sb_service_->ping_manager()->ReportThreatDetails(serialized);
+ }
+}
+
+// Whitelist this domain in the current WebState. Either add the
+// domain to an existing WhitelistUrlSet, or create a new WhitelistUrlSet.
+void SafeBrowsingUIManager::AddToWhitelist(const UnsafeResource& resource) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ DCHECK(resource.weak_web_state.get());
+
+ web::WebState* web_state = resource.weak_web_state.get();
+ WhitelistUrlSet* site_list =
+ static_cast<WhitelistUrlSet*>(web_state->GetUserData(kWhitelistKey));
+ if (!site_list) {
+ site_list = new WhitelistUrlSet;
+ web_state->SetUserData(kWhitelistKey, site_list);
+ }
+
+ GURL whitelisted_url(resource.is_subresource ? web_state->GetVisibleURL()
+ : resource.url);
+ site_list->Insert(whitelisted_url);
+}
+
+// Check if the user has already ignored a SB warning for this WebState and
+// top-level domain.
+bool SafeBrowsingUIManager::IsWhitelisted(const UnsafeResource& resource) {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ DCHECK(resource.weak_web_state.get());
+
+ web::WebState* web_state = resource.weak_web_state.get();
+ GURL maybe_whitelisted_url(
+ resource.is_subresource ? web_state->GetVisibleURL() : resource.url);
+ WhitelistUrlSet* site_list =
+ static_cast<WhitelistUrlSet*>(web_state->GetUserData(kWhitelistKey));
+ if (!site_list)
+ return false;
+ return site_list->Contains(maybe_whitelisted_url);
+}
+
+} // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/ui_manager.h b/ios/chrome/browser/safe_browsing/ui_manager.h
new file mode 100644
index 0000000..8d1eb51
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/ui_manager.h
@@ -0,0 +1,164 @@
+// Copyright 2015 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.
+//
+// The Safe Browsing service is responsible for downloading anti-phishing and
+// anti-malware tables and checking urls against them.
+
+#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_
+#define IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "components/safe_browsing_db/util.h"
+#include "ios/chrome/browser/safe_browsing/hit_report.h"
+#include "url/gurl.h"
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace net {
+class SSLInfo;
+} // namespace net
+
+namespace web {
+class WebState;
+} // namespace web
+
+namespace safe_browsing {
+
+class SafeBrowsingService;
+
+// Construction needs to happen on the main thread.
+class SafeBrowsingUIManager
+ : public base::RefCountedThreadSafe<SafeBrowsingUIManager> {
+ public:
+ // Passed a boolean indicating whether or not it is OK to proceed with
+ // loading an URL.
+ typedef base::Callback<void(bool /*proceed*/)> UrlCheckCallback;
+
+ // Structure used to pass parameters between the IO and UI thread when
+ // interacting with the blocking page.
+ struct UnsafeResource {
+ UnsafeResource();
+ ~UnsafeResource();
+
+ bool IsMainPageLoadBlocked() const;
+
+ GURL url;
+ GURL original_url;
+ std::vector<GURL> redirect_urls;
+ bool is_subresource;
+ bool is_subframe;
+ SBThreatType threat_type;
+ std::string threat_metadata;
+ UrlCheckCallback callback; // This is called back on |callback_thread|.
+ scoped_refptr<base::SingleThreadTaskRunner> callback_thread;
+ base::WeakPtr<web::WebState> weak_web_state;
+ safe_browsing::ThreatSource threat_source;
+ };
+
+ // Observer class can be used to get notified when a SafeBrowsing hit
+ // was found.
+ class Observer {
+ public:
+ // The |resource| was classified as unsafe by SafeBrowsing, and is
+ // not whitelisted.
+ // The |resource| must not be accessed after OnSafeBrowsingHit returns.
+ // This method will be called on the UI thread.
+ virtual void OnSafeBrowsingHit(const UnsafeResource& resource) = 0;
+
+ protected:
+ Observer() {}
+ virtual ~Observer() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+ };
+
+ explicit SafeBrowsingUIManager(
+ const scoped_refptr<SafeBrowsingService>& service);
+
+ // Called to stop or shutdown operations on the io_thread. This may be called
+ // multiple times during the life of the UIManager. Should be called
+ // on IO thread. If shutdown is true, the manager is disabled permanently.
+ void StopOnIOThread(bool shutdown);
+
+ // Called on the UI thread to display an interstitial page.
+ // |url| is the url of the resource that matches a safe browsing list.
+ // If the request contained a chain of redirects, |url| is the last url
+ // in the chain, and |original_url| is the first one (the root of the
+ // chain). Otherwise, |original_url| = |url|.
+ virtual void DisplayBlockingPage(const UnsafeResource& resource);
+
+ // Returns true if we already displayed an interstitial for that top-level
+ // site in a given WebContents. Called on the UI thread.
+ bool IsWhitelisted(const UnsafeResource& resource);
+
+ // The blocking page on the UI thread has completed.
+ void OnBlockingPageDone(const std::vector<UnsafeResource>& resources,
+ bool proceed);
+
+ // Log the user perceived delay caused by SafeBrowsing. This delay is the time
+ // delta starting from when we would have started reading data from the
+ // network, and ending when the SafeBrowsing check completes indicating that
+ // the current page is 'safe'.
+ void LogPauseDelay(base::TimeDelta time);
+
+ // Called on the IO thread by the ThreatDetails with the serialized
+ // protocol buffer, so the service can send it over.
+ virtual void SendSerializedThreatDetails(const std::string& serialized);
+
+ // Report hits to the unsafe contents (malware, phishing, unsafe download URL)
+ // to the server. Can only be called on UI thread. If |post_data| is
+ // non-empty, the request will be sent as a POST instead of a GET.
+ // Will report only for UMA || is_extended_reporting.
+ virtual void MaybeReportSafeBrowsingHit(
+ const safe_browsing::HitReport& hit_report);
+
+ // Report an invalid TLS/SSL certificate chain to the server. Can only
+ // be called on UI thread.
+ void ReportInvalidCertificateChain(const std::string& serialized_report,
+ const base::Closure& callback);
+
+ // Add and remove observers. These methods must be invoked on the UI thread.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* remove);
+
+ protected:
+ virtual ~SafeBrowsingUIManager();
+
+ private:
+ friend class base::RefCountedThreadSafe<SafeBrowsingUIManager>;
+
+ // Call protocol manager on IO thread to report hits of unsafe contents.
+ void ReportSafeBrowsingHitOnIOThread(
+ const safe_browsing::HitReport& hit_report);
+
+ // Sends an invalid certificate chain report over the network.
+ void ReportInvalidCertificateChainOnIOThread(
+ const std::string& serialized_report);
+
+ // Updates the whitelist state. Called on the UI thread.
+ void AddToWhitelist(const UnsafeResource& resource);
+
+ // Safebrowsing service.
+ scoped_refptr<SafeBrowsingService> sb_service_;
+
+ base::ObserverList<Observer> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIManager);
+};
+
+} // namespace safe_browsing
+
+#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index 187bbb1..7f84cf3 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -54,6 +54,7 @@
'../../components/components.gyp:bookmarks_managed',
'../../components/components.gyp:browser_sync_browser',
'../../components/components.gyp:browser_sync_common',
+ '../../components/components.gyp:certificate_reporting',
'../../components/components.gyp:component_updater',
'../../components/components.gyp:content_settings_core_browser',
'../../components/components.gyp:cookie_config',
@@ -87,6 +88,7 @@
'../../components/components.gyp:pref_registry',
'../../components/components.gyp:proxy_config',
'../../components/components.gyp:rappor',
+ '../../components/components.gyp:safe_browsing_db',
'../../components/components.gyp:search',
'../../components/components.gyp:search_engines',
'../../components/components.gyp:security_interstitials_core',
@@ -619,6 +621,25 @@
'ios_chrome_browser_rlz',
],
}],
+ ['safe_browsing!=0', {
+ 'sources': [
+ 'browser/safe_browsing/hit_report.cc',
+ 'browser/safe_browsing/hit_report.h',
+ 'browser/safe_browsing/ping_manager.cc',
+ 'browser/safe_browsing/ping_manager.h',
+ 'browser/safe_browsing/protocol_manager_helper.cc',
+ 'browser/safe_browsing/protocol_manager_helper.h',
+ 'browser/safe_browsing/safe_browsing_blocking_page.cc',
+ 'browser/safe_browsing/safe_browsing_blocking_page.h',
+ 'browser/safe_browsing/safe_browsing_service.cc',
+ 'browser/safe_browsing/safe_browsing_service.h',
+ 'browser/safe_browsing/ui_manager.cc',
+ 'browser/safe_browsing/ui_manager.h',
+ ],
+ 'dependencies': [
+ 'ios_chrome_safe_browsing_proto',
+ ],
+ }]
],
},
{
@@ -785,5 +806,20 @@
},
],
}],
+ ['safe_browsing!=0', {
+ 'targets': [
+ {
+ # GN version: //ios/chrome/browser/safe_browsing:proto
+ 'target_name': 'ios_chrome_safe_browsing_proto',
+ 'type': 'static_library',
+ 'sources': [ 'browser/safe_browsing/metadata.proto' ],
+ 'variables': {
+ 'proto_in_dir': 'browser/safe_browsing',
+ 'proto_out_dir': 'ios/chrome/browser/safe_browsing',
+ },
+ 'includes': [ '../../build/protoc.gypi' ],
+ },
+ ],
+ }],
],
}