summaryrefslogtreecommitdiff
path: root/chromium/components/safe_browsing
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/safe_browsing')
-rw-r--r--chromium/components/safe_browsing/BUILD.gn3
-rw-r--r--chromium/components/safe_browsing/DEPS1
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc9
-rw-r--r--chromium/components/safe_browsing/content/base_blocking_page.cc4
-rw-r--r--chromium/components/safe_browsing/content/base_blocking_page.h2
-rw-r--r--chromium/components/safe_browsing/content/base_ui_manager.cc64
-rw-r--r--chromium/components/safe_browsing/content/browser/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc60
-rw-r--r--chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h6
-rw-r--r--chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc4
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc13
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details.cc9
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details_cache.cc11
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details_history.cc7
-rw-r--r--chromium/components/safe_browsing/content/common/safe_browsing.mojom13
-rw-r--r--chromium/components/safe_browsing/content/password_protection/BUILD.gn3
-rw-r--r--chromium/components/safe_browsing/content/password_protection/DEPS3
-rw-r--r--chromium/components/safe_browsing/content/password_protection/metrics_util.cc1
-rw-r--r--chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h17
-rw-r--r--chromium/components/safe_browsing/content/password_protection/password_protection_request.cc15
-rw-r--r--chromium/components/safe_browsing/content/password_protection/password_protection_service.cc65
-rw-r--r--chromium/components/safe_browsing/content/password_protection/password_protection_service.h33
-rw-r--r--chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc36
-rw-r--r--chromium/components/safe_browsing/content/password_protection/visual_utils.cc182
-rw-r--r--chromium/components/safe_browsing/content/password_protection/visual_utils.h10
-rw-r--r--chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc259
-rw-r--r--chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc38
-rw-r--r--chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h20
-rw-r--r--chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc4
-rw-r--r--chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc4
-rw-r--r--chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc4
-rw-r--r--chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc4
-rw-r--r--chromium/components/safe_browsing/content/web_ui/BUILD.gn1
-rw-r--r--chromium/components/safe_browsing/content/web_ui/DEPS1
-rw-r--r--chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css25
-rw-r--r--chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html28
-rw-r--r--chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js77
-rw-r--r--chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc187
-rw-r--r--chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h11
-rw-r--r--chromium/components/safe_browsing/core/BUILD.gn13
-rw-r--r--chromium/components/safe_browsing/core/browser/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc2
-rw-r--r--chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc48
-rw-r--r--chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h28
-rw-r--r--chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc51
-rw-r--r--chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc73
-rw-r--r--chromium/components/safe_browsing/core/common/safe_browsing_prefs.h3
-rw-r--r--chromium/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc8
-rw-r--r--chromium/components/safe_browsing/core/db/v4_local_database_manager.cc3
-rw-r--r--chromium/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc1
-rw-r--r--chromium/components/safe_browsing/core/db/v4_store.cc45
-rw-r--r--chromium/components/safe_browsing/core/db/v4_store_fuzzer.cc1
-rw-r--r--chromium/components/safe_browsing/core/db/v4_store_unittest.cc1
-rw-r--r--chromium/components/safe_browsing/core/db/v4_update_protocol_manager.cc9
-rw-r--r--chromium/components/safe_browsing/core/db/v4_update_protocol_manager.h5
-rw-r--r--chromium/components/safe_browsing/core/features.cc27
-rw-r--r--chromium/components/safe_browsing/core/features.h6
-rw-r--r--chromium/components/safe_browsing/core/proto/client_model.proto2
-rw-r--r--chromium/components/safe_browsing/core/proto/realtimeapi.proto2
-rw-r--r--chromium/components/safe_browsing/core/proto/webprotect.proto3
-rw-r--r--chromium/components/safe_browsing/core/realtime/BUILD.gn42
-rw-r--r--chromium/components/safe_browsing/core/realtime/policy_engine.cc60
-rw-r--r--chromium/components/safe_browsing/core/realtime/policy_engine.h26
-rw-r--r--chromium/components/safe_browsing/core/realtime/policy_engine_unittest.cc86
-rw-r--r--chromium/components/safe_browsing/core/realtime/url_lookup_service.cc332
-rw-r--r--chromium/components/safe_browsing/core/realtime/url_lookup_service.h151
-rw-r--r--chromium/components/safe_browsing/core/realtime/url_lookup_service_base.cc284
-rw-r--r--chromium/components/safe_browsing/core/realtime/url_lookup_service_base.h191
-rw-r--r--chromium/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc12
-rw-r--r--chromium/components/safe_browsing/core/verdict_cache_manager.cc11
-rw-r--r--chromium/components/safe_browsing/core/verdict_cache_manager.h6
-rw-r--r--chromium/components/safe_browsing/core/verdict_cache_manager_unittest.cc32
-rw-r--r--chromium/components/safe_browsing/ios/browser/BUILD.gn18
-rw-r--r--chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h131
-rw-r--r--chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.mm159
75 files changed, 2198 insertions, 912 deletions
diff --git a/chromium/components/safe_browsing/BUILD.gn b/chromium/components/safe_browsing/BUILD.gn
index 89ed2c0243c..cc7fd1ddae6 100644
--- a/chromium/components/safe_browsing/BUILD.gn
+++ b/chromium/components/safe_browsing/BUILD.gn
@@ -11,17 +11,14 @@ buildflag_header("buildflags") {
flags = []
if (safe_browsing_mode == 0) {
flags += [ "FULL_SAFE_BROWSING=0" ]
- flags += [ "SAFE_BROWSING_CSD=0" ]
flags += [ "SAFE_BROWSING_DB_LOCAL=0" ]
flags += [ "SAFE_BROWSING_DB_REMOTE=0" ]
} else if (safe_browsing_mode == 1) {
flags += [ "FULL_SAFE_BROWSING=1" ]
- flags += [ "SAFE_BROWSING_CSD=1" ]
flags += [ "SAFE_BROWSING_DB_LOCAL=1" ]
flags += [ "SAFE_BROWSING_DB_REMOTE=0" ]
} else if (safe_browsing_mode == 2) {
flags += [ "FULL_SAFE_BROWSING=0" ]
- flags += [ "SAFE_BROWSING_CSD=0" ]
flags += [ "SAFE_BROWSING_DB_LOCAL=0" ]
flags += [ "SAFE_BROWSING_DB_REMOTE=1" ]
}
diff --git a/chromium/components/safe_browsing/DEPS b/chromium/components/safe_browsing/DEPS
index ace05e79c17..9f374d3a591 100644
--- a/chromium/components/safe_browsing/DEPS
+++ b/chromium/components/safe_browsing/DEPS
@@ -12,6 +12,7 @@ include_rules = [
"+components/sync_preferences/testing_pref_service_syncable.h",
"+components/unified_consent",
"+components/user_prefs/user_prefs.h",
+ "+components/variations",
"+content/public/browser",
"+content/public/common",
"+content/public/test",
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index 58f43251d40..41a76b2f200 100644
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -14,7 +14,6 @@
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
#include "base/trace_event/trace_event.h"
#include "components/safe_browsing/android/jni_headers/SafeBrowsingApiBridge_jni.h"
#include "components/safe_browsing/android/safe_browsing_api_handler_util.h"
@@ -41,8 +40,8 @@ void RunCallbackOnIOThread(
const ThreatMetadata& metadata) {
CHECK(callback); // Remove after fixing crbug.com/889972
CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(std::move(*callback), threat_type, metadata));
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*callback), threat_type, metadata));
}
void ReportUmaResult(safe_browsing::UmaRemoteCallResult result) {
@@ -188,8 +187,8 @@ void JNI_SafeBrowsingApiBridge_OnUrlCheckDone(
TRACE_EVENT1("safe_browsing", "SafeBrowsingApiHandlerBridge::OnUrlCheckDone",
"metadata", metadata_str);
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(&OnUrlCheckDoneOnIOThread, callback_id,
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&OnUrlCheckDoneOnIOThread, callback_id,
result_status, metadata_str));
}
diff --git a/chromium/components/safe_browsing/content/base_blocking_page.cc b/chromium/components/safe_browsing/content/base_blocking_page.cc
index 513ed5b08f7..bc7d4dc3c7d 100644
--- a/chromium/components/safe_browsing/content/base_blocking_page.cc
+++ b/chromium/components/safe_browsing/content/base_blocking_page.cc
@@ -138,10 +138,6 @@ void BaseBlockingPage::PopulateInterstitialStrings(
sb_error_ui_->PopulateStringsForHtml(load_time_data);
}
-void BaseBlockingPage::OnInterstitialClosing() {
- UpdateMetricsAfterSecurityInterstitial();
-}
-
void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
bool did_proceed,
int num_visits) {}
diff --git a/chromium/components/safe_browsing/content/base_blocking_page.h b/chromium/components/safe_browsing/content/base_blocking_page.h
index 1e35a434363..97bfb779040 100644
--- a/chromium/components/safe_browsing/content/base_blocking_page.h
+++ b/chromium/components/safe_browsing/content/base_blocking_page.h
@@ -61,7 +61,7 @@ class BaseBlockingPage
bool ShouldCreateNewNavigation() const override;
void PopulateInterstitialStrings(
base::DictionaryValue* load_time_data) override;
- void OnInterstitialClosing() override;
+ void OnInterstitialClosing() override {}
// Called when the interstitial is going away. Intentionally do nothing in
// this base class.
diff --git a/chromium/components/safe_browsing/content/base_ui_manager.cc b/chromium/components/safe_browsing/content/base_ui_manager.cc
index 6643359fd2e..680b1098f46 100644
--- a/chromium/components/safe_browsing/content/base_ui_manager.cc
+++ b/chromium/components/safe_browsing/content/base_ui_manager.cc
@@ -190,6 +190,21 @@ void BaseUIManager::OnBlockingPageDone(
}
}
+namespace {
+// In the case of nested WebContents, returns the WebContents where it is
+// suitable to show an interstitial.
+content::WebContents* GetEmbeddingWebContentsForInterstitial(
+ content::WebContents* source_contents) {
+ content::WebContents* top_level_contents = source_contents;
+ // Note that |WebContents::GetResponsibleWebContents| is not suitable here
+ // since we want to stay within any GuestViews.
+ while (top_level_contents->IsPortal()) {
+ top_level_contents = top_level_contents->GetPortalHostWebContents();
+ }
+ return top_level_contents;
+}
+} // namespace
+
void BaseUIManager::DisplayBlockingPage(
const UnsafeResource& resource) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -245,13 +260,26 @@ void BaseUIManager::DisplayBlockingPage(
}
AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource),
- resource.web_contents_getter.Run(),
- true /* A decision is now pending */,
+ web_contents, true /* A decision is now pending */,
resource.threat_type);
- GURL unsafe_url = (resource.IsMainPageLoadBlocked() ||
- !GetNavigationEntryForResource(resource))
- ? resource.url
- : GetNavigationEntryForResource(resource)->GetURL();
+
+ // |entry| can be null if we are on a brand new tab, and a resource is added
+ // via javascript without a navigation.
+ content::NavigationEntry* entry = GetNavigationEntryForResource(resource);
+
+ // If unsafe content is loaded in a portal, we treat its embedder as
+ // dangerous.
+ content::WebContents* outermost_contents =
+ GetEmbeddingWebContentsForInterstitial(web_contents);
+
+ GURL unsafe_url = resource.url;
+ if (outermost_contents != web_contents) {
+ DCHECK(outermost_contents->GetController().GetLastCommittedEntry());
+ unsafe_url =
+ outermost_contents->GetController().GetLastCommittedEntry()->GetURL();
+ } else if (entry && !resource.IsMainPageLoadBlocked()) {
+ unsafe_url = entry->GetURL();
+ }
AddUnsafeResource(unsafe_url, resource);
// If the delayed warnings experiment is not enabled, with committed
// interstitials we just cancel the load from here, the actual interstitial
@@ -273,28 +301,24 @@ void BaseUIManager::DisplayBlockingPage(
DCHECK(!resource.is_delayed_warning);
}
- if ((!resource.IsMainPageLoadBlocked() || resource.is_delayed_warning) &&
- !IsWhitelisted(resource)) {
+ if (!resource.IsMainPageLoadBlocked() || resource.is_delayed_warning ||
+ outermost_contents != web_contents) {
+ DCHECK(!IsWhitelisted(resource));
// For subresource triggered interstitials, we trigger the error page
// navigation from here since there will be no navigation to intercept
// in the throttle.
- content::WebContents* contents = resource.web_contents_getter.Run();
- content::NavigationEntry* entry = GetNavigationEntryForResource(resource);
- // entry can be null if we are on a brand new tab, and a resource is added
- // via javascript without a navigation.
- GURL blocked_url = entry ? entry->GetURL() : resource.url;
-
+ //
// Blocking pages handle both user interaction, and generation of the
// interstitial HTML. In the case of subresources, we need the HTML
// content prior to (and in a different process than when) installing the
// command handlers. For this reason we create a blocking page here just
// to generate the HTML, and immediately delete it.
- BaseBlockingPage* blocking_page =
- CreateBlockingPageForSubresource(contents, blocked_url, resource);
- contents->GetController().LoadPostCommitErrorPage(
- contents->GetMainFrame(), blocked_url, blocking_page->GetHTMLContents(),
- net::ERR_BLOCKED_BY_CLIENT);
- delete blocking_page;
+ std::unique_ptr<BaseBlockingPage> blocking_page =
+ base::WrapUnique(CreateBlockingPageForSubresource(
+ outermost_contents, unsafe_url, resource));
+ outermost_contents->GetController().LoadPostCommitErrorPage(
+ outermost_contents->GetMainFrame(), unsafe_url,
+ blocking_page->GetHTMLContents(), net::ERR_BLOCKED_BY_CLIENT);
}
}
diff --git a/chromium/components/safe_browsing/content/browser/BUILD.gn b/chromium/components/safe_browsing/content/browser/BUILD.gn
index 6111969ed2b..0b942f26ce3 100644
--- a/chromium/components/safe_browsing/content/browser/BUILD.gn
+++ b/chromium/components/safe_browsing/content/browser/BUILD.gn
@@ -33,7 +33,7 @@ jumbo_source_set("browser") {
"//components/safe_browsing/core/common:common",
"//components/safe_browsing/core/db:database_manager",
"//components/safe_browsing/core/realtime:policy_engine",
- "//components/safe_browsing/core/realtime:url_lookup_service",
+ "//components/safe_browsing/core/realtime:url_lookup_service_base",
"//components/safe_browsing/core/web_ui:constants",
"//components/security_interstitials/content:security_interstitial_page",
"//components/security_interstitials/core:unsafe_resource",
diff --git a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
index cb3aa4a82be..2fa3d9cc4c9 100644
--- a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
+++ b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
@@ -13,7 +13,7 @@
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "components/safe_browsing/core/common/utils.h"
#include "components/safe_browsing/core/realtime/policy_engine.h"
-#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service_base.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/web_contents.h"
#include "net/log/net_log_event_type.h"
@@ -34,14 +34,16 @@ class BrowserURLLoaderThrottle::CheckerOnIO
base::RepeatingCallback<content::WebContents*()> web_contents_getter,
base::WeakPtr<BrowserURLLoaderThrottle> throttle,
bool real_time_lookup_enabled,
- bool enhanced_protection_enabled,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service)
+ bool can_rt_check_subresource_url,
+ bool can_check_db,
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service)
: delegate_getter_(std::move(delegate_getter)),
frame_tree_node_id_(frame_tree_node_id),
web_contents_getter_(web_contents_getter),
throttle_(std::move(throttle)),
real_time_lookup_enabled_(real_time_lookup_enabled),
- enhanced_protection_enabled_(enhanced_protection_enabled),
+ can_rt_check_subresource_url_(can_rt_check_subresource_url),
+ can_check_db_(can_check_db),
url_lookup_service_(url_lookup_service) {}
// Starts the initial safe browsing check. This check and future checks may be
@@ -63,8 +65,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO
url, frame_tree_node_id_, -1 /* render_process_id */,
-1 /* render_frame_id */, originated_from_service_worker);
if (skip_checks_) {
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
return;
}
@@ -72,7 +74,7 @@ class BrowserURLLoaderThrottle::CheckerOnIO
url_checker_ = std::make_unique<SafeBrowsingUrlCheckerImpl>(
headers, load_flags, resource_type, has_user_gesture,
url_checker_delegate, web_contents_getter_, real_time_lookup_enabled_,
- enhanced_protection_enabled_, url_lookup_service_);
+ can_rt_check_subresource_url_, can_check_db_, url_lookup_service_);
CheckUrl(url, method);
}
@@ -81,8 +83,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO
void CheckUrl(const GURL& url, const std::string& method) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (skip_checks_) {
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
return;
}
@@ -108,8 +110,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO
return;
}
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&BrowserURLLoaderThrottle::NotifySlowCheck, throttle_));
// In this case |proceed| and |showed_interstitial| should be ignored. The
@@ -124,8 +126,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO
void OnCompleteCheck(bool slow_check,
bool proceed,
bool showed_interstitial) {
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&BrowserURLLoaderThrottle::OnCompleteCheck, throttle_,
slow_check, proceed, showed_interstitial));
}
@@ -139,8 +141,9 @@ class BrowserURLLoaderThrottle::CheckerOnIO
bool skip_checks_ = false;
base::WeakPtr<BrowserURLLoaderThrottle> throttle_;
bool real_time_lookup_enabled_ = false;
- bool enhanced_protection_enabled_ = false;
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_;
+ bool can_rt_check_subresource_url_ = false;
+ bool can_check_db_ = true;
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_;
};
// static
@@ -148,7 +151,7 @@ std::unique_ptr<BrowserURLLoaderThrottle> BrowserURLLoaderThrottle::Create(
GetDelegateCallback delegate_getter,
const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
int frame_tree_node_id,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service) {
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service) {
return base::WrapUnique<BrowserURLLoaderThrottle>(
new BrowserURLLoaderThrottle(std::move(delegate_getter),
web_contents_getter, frame_tree_node_id,
@@ -159,7 +162,7 @@ BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
GetDelegateCallback delegate_getter,
const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
int frame_tree_node_id,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service) {
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Decide whether to do real time URL lookups or not.
@@ -167,13 +170,18 @@ BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
url_lookup_service ? url_lookup_service->CanPerformFullURLLookup()
: false;
- bool enhanced_protection_enabled =
- url_lookup_service && url_lookup_service->IsUserEpOptedIn();
+ bool can_rt_check_subresource_url =
+ url_lookup_service && url_lookup_service->CanCheckSubresourceURL();
+ // Decide whether safe browsing database can be checked.
+ // If url_lookup_service is null, safe browsing database should be checked by
+ // default.
+ bool can_check_db =
+ url_lookup_service ? url_lookup_service->CanCheckSafeBrowsingDb() : true;
io_checker_ = std::make_unique<CheckerOnIO>(
std::move(delegate_getter), frame_tree_node_id, web_contents_getter,
weak_factory_.GetWeakPtr(), real_time_lookup_enabled,
- enhanced_protection_enabled, url_lookup_service);
+ can_rt_check_subresource_url, can_check_db, url_lookup_service);
}
BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() {
@@ -193,8 +201,8 @@ void BrowserURLLoaderThrottle::WillStartRequest(
original_url_ = request->url;
pending_checks_++;
- base::PostTask(
- FROM_HERE, {content::BrowserThread::IO},
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(
&BrowserURLLoaderThrottle::CheckerOnIO::Start,
io_checker_->AsWeakPtr(), request->headers, request->load_flags,
@@ -223,8 +231,8 @@ void BrowserURLLoaderThrottle::WillRedirectRequest(
return;
pending_checks_++;
- base::PostTask(
- FROM_HERE, {content::BrowserThread::IO},
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::CheckUrl,
io_checker_->AsWeakPtr(), redirect_info->new_url,
redirect_info->new_method));
@@ -324,8 +332,8 @@ void BrowserURLLoaderThrottle::NotifySlowCheck() {
}
void BrowserURLLoaderThrottle::DeleteCheckerOnIO() {
- base::DeleteSoon(FROM_HERE, {content::BrowserThread::IO},
- std::move(io_checker_));
+ content::GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE,
+ std::move(io_checker_));
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h
index d31b38a3dbc..fbd0536a4c7 100644
--- a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h
+++ b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h
@@ -28,7 +28,7 @@ namespace safe_browsing {
class UrlCheckerDelegate;
-class RealTimeUrlLookupService;
+class RealTimeUrlLookupServiceBase;
// BrowserURLLoaderThrottle is used in the browser process to query
// SafeBrowsing to determine whether a URL and also its redirect URLs are safe
@@ -49,7 +49,7 @@ class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle {
const base::RepeatingCallback<content::WebContents*()>&
web_contents_getter,
int frame_tree_node_id,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service);
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service);
~BrowserURLLoaderThrottle() override;
@@ -86,7 +86,7 @@ class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle {
const base::RepeatingCallback<content::WebContents*()>&
web_contents_getter,
int frame_tree_node_id,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service);
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service);
// |slow_check| indicates whether it reports the result of a slow check.
// (Please see comments of CheckerOnIO::OnCheckUrlResult() for what slow check
diff --git a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
index 43e5f816d0b..b9dc2d53324 100644
--- a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
+++ b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
@@ -156,7 +156,9 @@ void MojoSafeBrowsingImpl::CreateCheckerAndCheck(
delegate_,
base::BindRepeating(&GetWebContentsFromID, render_process_id_,
static_cast<int>(render_frame_id)),
- /*real_time_lookup_enabled=*/false, /*enhanced_protection_enabled=*/false,
+ /*real_time_lookup_enabled=*/false,
+ /*can_rt_check_subresource_url=*/false,
+ /*can_check_db=*/true,
/*url_lookup_service=*/nullptr);
checker_impl->CheckUrl(
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc
index 4ac3714571e..b22dc6eed7d 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc
@@ -13,7 +13,7 @@
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "components/safe_browsing/core/common/thread_utils.h"
#include "components/safe_browsing/core/realtime/policy_engine.h"
-#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service_base.h"
#include "components/safe_browsing/core/web_ui/constants.h"
namespace safe_browsing {
@@ -21,8 +21,8 @@ namespace safe_browsing {
bool SafeBrowsingUrlCheckerImpl::CanPerformFullURLLookup(const GURL& url) {
return real_time_lookup_enabled_ &&
RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
- resource_type_, enhanced_protection_enabled_) &&
- RealTimeUrlLookupService::CanCheckUrl(url);
+ resource_type_, can_rt_check_subresource_url_) &&
+ RealTimeUrlLookupServiceBase::CanCheckUrl(url);
}
void SafeBrowsingUrlCheckerImpl::OnRTLookupRequest(
@@ -48,7 +48,7 @@ void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse(
bool is_expected_resource_type =
(ResourceType::kMainFrame == resource_type_) ||
((ResourceType::kSubFrame == resource_type_) &&
- enhanced_protection_enabled_);
+ can_rt_check_subresource_url_);
DCHECK(is_expected_resource_type);
const GURL& url = urls_[next_index_].url;
@@ -75,8 +75,9 @@ void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse(
// TODO(crbug.com/1033692): Only take the first threat info into account
// because threat infos are returned in decreasing order of severity.
// Consider extend it to support multiple threat types.
- sb_threat_type = RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
- response->threat_info(0).threat_type());
+ sb_threat_type =
+ RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
+ response->threat_info(0).threat_type());
}
OnUrlResult(url, sb_threat_type, ThreatMetadata());
}
diff --git a/chromium/components/safe_browsing/content/browser/threat_details.cc b/chromium/components/safe_browsing/content/browser/threat_details.cc
index a71ac3092c4..c63087e8c8f 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details.cc
+++ b/chromium/components/safe_browsing/content/browser/threat_details.cc
@@ -20,7 +20,6 @@
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/safe_browsing/content/base_ui_manager.h"
#include "components/safe_browsing/content/browser/threat_details_cache.h"
@@ -849,8 +848,8 @@ void ThreatDetails::OnCacheCollectionReady() {
return;
}
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent,
base::Unretained(WebUIInfoSingleton::GetInstance()),
std::move(report_)));
@@ -877,8 +876,8 @@ void ThreatDetails::MaybeFillReferrerChain() {
void ThreatDetails::AllDone() {
is_all_done_ = true;
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(std::move(done_callback_),
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(done_callback_),
base::Unretained(web_contents())));
}
diff --git a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
index d0ea614c3bd..70c5ee9278c 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
+++ b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
@@ -12,7 +12,6 @@
#include "base/hash/md5.h"
#include "base/lazy_instance.h"
#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/content/browser/threat_details.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -52,8 +51,8 @@ void ThreatDetailsCacheCollector::StartCacheCollection(
// Post a task in the message loop, so the callers don't need to
// check if we call their callback immediately.
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
}
bool ThreatDetailsCacheCollector::HasStarted() {
@@ -227,15 +226,15 @@ void ThreatDetailsCacheCollector::AdvanceEntry() {
current_load_.reset();
// Create a task so we don't take over the UI thread for too long.
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
}
void ThreatDetailsCacheCollector::AllDone(bool success) {
DVLOG(1) << "AllDone";
DCHECK_CURRENTLY_ON(BrowserThread::UI);
*result_ = success;
- base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
+ content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback_));
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/browser/threat_details_history.cc b/chromium/components/safe_browsing/content/browser/threat_details_history.cc
index d434e97c4ac..c9976122b8c 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details_history.cc
+++ b/chromium/components/safe_browsing/content/browser/threat_details_history.cc
@@ -10,7 +10,6 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/content/browser/threat_details.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -41,8 +40,8 @@ void ThreatDetailsRedirectsCollector::StartHistoryCollection(
return;
}
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&ThreatDetailsRedirectsCollector::StartGetRedirects, this,
urls));
}
@@ -107,7 +106,7 @@ void ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo(
void ThreatDetailsRedirectsCollector::AllDone() {
DVLOG(1) << "AllDone";
- base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
+ content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback_));
}
void ThreatDetailsRedirectsCollector::HistoryServiceBeingDeleted(
diff --git a/chromium/components/safe_browsing/content/common/safe_browsing.mojom b/chromium/components/safe_browsing/content/common/safe_browsing.mojom
index a5cc71ad13e..72c21819f36 100644
--- a/chromium/components/safe_browsing/content/common/safe_browsing.mojom
+++ b/chromium/components/safe_browsing/content/common/safe_browsing.mojom
@@ -118,7 +118,13 @@ enum PhishingDetectorResult {
};
[EnableIf=full_safe_browsing]
+// Interface for setting the CSD model and to start phishing classification.
interface PhishingDetector {
+ // A classification model for client-side phishing detection.
+ // The string is an encoded safe_browsing::ClientSideModel protocol buffer, or
+ // empty to disable client-side phishing detection for this renderer.
+ SetPhishingModel(string model);
+
// Tells the renderer to begin phishing detection for the given toplevel URL
// which it has started loading. Returns the serialized request proto and a
// |result| enum to indicate failure. If the URL is phishing the request proto
@@ -126,10 +132,3 @@ interface PhishingDetector {
StartPhishingDetection(url.mojom.Url url)
=> (PhishingDetectorResult result, string request_proto);
};
-
-interface PhishingModelSetter {
- // A classification model for client-side phishing detection.
- // The string is an encoded safe_browsing::ClientSideModel protocol buffer, or
- // empty to disable client-side phishing detection for this renderer.
- SetPhishingModel(string model);
-};
diff --git a/chromium/components/safe_browsing/content/password_protection/BUILD.gn b/chromium/components/safe_browsing/content/password_protection/BUILD.gn
index 26d6f07d5ee..2d06ca399b5 100644
--- a/chromium/components/safe_browsing/content/password_protection/BUILD.gn
+++ b/chromium/components/safe_browsing/content/password_protection/BUILD.gn
@@ -28,6 +28,7 @@ source_set("password_protection") {
"//components/password_manager/core/browser:browser",
"//components/safe_browsing/content/common:interfaces",
"//components/safe_browsing/content/web_ui:web_ui",
+ "//components/safe_browsing/core:client_model_proto",
"//components/safe_browsing/core:csd_proto",
"//components/safe_browsing/core:features",
"//components/safe_browsing/core/browser:referrer_chain_provider",
@@ -43,7 +44,9 @@ source_set("password_protection") {
"//components/url_formatter",
"//content/public/browser:browser",
"//net:net",
+ "//third_party/opencv:emd",
"//third_party/protobuf:protobuf_lite",
+ "//ui/gfx:color_utils",
]
}
if (safe_browsing_mode == 1) {
diff --git a/chromium/components/safe_browsing/content/password_protection/DEPS b/chromium/components/safe_browsing/content/password_protection/DEPS
index c39c8a73429..776629f7b37 100644
--- a/chromium/components/safe_browsing/content/password_protection/DEPS
+++ b/chromium/components/safe_browsing/content/password_protection/DEPS
@@ -10,8 +10,9 @@ include_rules = [
"+net",
"+services/network/public",
"+third_party/blink/public/common/page/page_zoom.h",
- "+ui/gfx/geometry",
+ "+ui/gfx",
"+third_party/skia/include",
+ "+third_party/opencv",
]
specific_include_rules = {
diff --git a/chromium/components/safe_browsing/content/password_protection/metrics_util.cc b/chromium/components/safe_browsing/content/password_protection/metrics_util.cc
index 7b66bf6308b..072a0f97219 100644
--- a/chromium/components/safe_browsing/content/password_protection/metrics_util.cc
+++ b/chromium/components/safe_browsing/content/password_protection/metrics_util.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
#include "base/time/time.h"
#include "net/http/http_status_code.h"
diff --git a/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h b/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h
index 1e3783718e4..e88bcd4d1d9 100644
--- a/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h
+++ b/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h
@@ -36,13 +36,13 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MOCK_CONST_METHOD1(GetSignedInNonSyncAccount,
AccountInfo(const std::string&));
MOCK_CONST_METHOD1(IsOtherGaiaAccountGmail, bool(const std::string&));
- MOCK_CONST_METHOD2(IsURLWhitelistedForPasswordEntry,
- bool(const GURL&, RequestOutcome*));
+ MOCK_CONST_METHOD1(IsURLWhitelistedForPasswordEntry, bool(const GURL&));
MOCK_METHOD0(CanSendSamplePing, bool());
MOCK_METHOD0(IsExtendedReporting, bool());
MOCK_METHOD0(IsEnhancedProtection, bool());
MOCK_METHOD0(IsIncognito, bool());
+ MOCK_METHOD1(IsInPasswordAlertMode, bool(ReusedPasswordAccountType));
MOCK_METHOD0(IsHistorySyncEnabled, bool());
MOCK_METHOD0(IsUnderAdvancedProtection, bool());
MOCK_METHOD0(ReportPasswordChanged, void());
@@ -57,10 +57,13 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MOCK_METHOD1(
RemovePhishedSavedPasswordCredential,
void(const std::vector<password_manager::MatchingReusedCredential>&));
- MOCK_METHOD3(IsPingingEnabled,
+ MOCK_METHOD2(IsPingingEnabled,
bool(LoginReputationClientRequest::TriggerType,
- ReusedPasswordAccountType,
- RequestOutcome*));
+ ReusedPasswordAccountType));
+ MOCK_METHOD3(GetPingNotSentReason,
+ RequestOutcome(LoginReputationClientRequest::TriggerType,
+ const GURL&,
+ ReusedPasswordAccountType));
MOCK_METHOD5(ShowModalWarning,
void(content::WebContents*,
RequestOutcome,
@@ -85,8 +88,8 @@ class MockPasswordProtectionService : public PasswordProtectionService {
RequestOutcome,
PasswordType,
const safe_browsing::LoginReputationClientResponse*));
- MOCK_METHOD3(CanShowInterstitial,
- bool(RequestOutcome, ReusedPasswordAccountType, const GURL&));
+ MOCK_METHOD2(CanShowInterstitial,
+ bool(ReusedPasswordAccountType, const GURL&));
MOCK_METHOD5(MaybeStartPasswordFieldOnFocusRequest,
void(content::WebContents*,
const GURL&,
diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc
index 82b217141c6..94831c3c64c 100644
--- a/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc
+++ b/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc
@@ -11,7 +11,6 @@
#include "base/containers/flat_set.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
@@ -159,7 +158,7 @@ void PasswordProtectionRequest::CheckWhitelist() {
auto result_callback =
base::BindOnce(&OnWhitelistCheckDoneOnIO, GetWeakPtr());
tracker_.PostTask(
- base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(), FROM_HERE,
+ content::GetIOThreadTaskRunner({}).get(), FROM_HERE,
base::BindOnce(&AllowlistCheckerClient::StartCheckCsdWhitelist,
password_protection_service_->database_manager(),
main_frame_url_, std::move(result_callback)));
@@ -170,8 +169,8 @@ void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO(
base::WeakPtr<PasswordProtectionRequest> weak_request,
bool match_whitelist) {
// Don't access weak_request on IO thread. Move it back to UI thread first.
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&PasswordProtectionRequest::OnWhitelistCheckDone,
weak_request, match_whitelist));
}
@@ -332,8 +331,8 @@ void PasswordProtectionRequest::FillRequestProto(bool is_sampled_ping) {
main_frame_url_,
base::BindRepeating(&PasswordProtectionRequest::OnGetDomFeatures,
GetWeakPtr()));
- base::PostDelayedTask(
- FROM_HERE, {BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostDelayedTask(
+ FROM_HERE,
base::BindOnce(&PasswordProtectionRequest::OnGetDomFeatureTimeout,
GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kDomFeatureTimeoutMs));
@@ -520,8 +519,8 @@ void PasswordProtectionRequest::StartTimeout() {
// The weak pointer used for the timeout will be invalidated (and
// hence would prevent the timeout) if the check completes on time and
// execution reaches Finish().
- base::PostDelayedTask(
- FROM_HERE, {BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostDelayedTask(
+ FROM_HERE,
base::BindOnce(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true),
base::TimeDelta::FromMilliseconds(request_timeout_in_ms_));
}
diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc
index f93318f850b..6a180cb90ef 100644
--- a/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc
+++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc
@@ -17,7 +17,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
@@ -101,18 +100,22 @@ void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
const GURL& password_form_frame_url,
const std::string& hosted_domain) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- RequestOutcome reason;
- if (CanSendPing(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- main_frame_url,
- GetPasswordProtectionReusedPasswordAccountType(
- PasswordType::PASSWORD_TYPE_UNKNOWN,
- /*username=*/""),
- &reason)) {
+ LoginReputationClientRequest::TriggerType trigger_type =
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE;
+ ReusedPasswordAccountType reused_password_account_type =
+ GetPasswordProtectionReusedPasswordAccountType(
+ PasswordType::PASSWORD_TYPE_UNKNOWN,
+ /*username=*/"");
+ if (CanSendPing(trigger_type, main_frame_url, reused_password_account_type)) {
StartRequest(web_contents, main_frame_url, password_form_action,
password_form_frame_url, /* username */ "",
PasswordType::PASSWORD_TYPE_UNKNOWN,
{}, /* matching_reused_credentials: not used for this type */
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+ } else {
+ RequestOutcome reason = GetPingNotSentReason(trigger_type, main_frame_url,
+ reused_password_account_type);
+ LogNoPingingReason(trigger_type, reason, reused_password_account_type);
}
}
#endif
@@ -127,13 +130,10 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
matching_reused_credentials,
bool password_field_exists) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ LoginReputationClientRequest::TriggerType trigger_type =
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT;
ReusedPasswordAccountType reused_password_account_type =
GetPasswordProtectionReusedPasswordAccountType(password_type, username);
- RequestOutcome reason;
- // Need to populate |reason| to be passed into CanShowInterstitial.
- bool can_send_ping =
- CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- main_frame_url, reused_password_account_type, &reason);
if (IsSupportedPasswordTypeForPinging(password_type)) {
#if BUILDFLAG(FULL_SAFE_BROWSING)
// Collect metrics about typical page-zoom on login pages.
@@ -143,7 +143,8 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
"PasswordProtection.PageZoomFactor",
static_cast<int>(100 * blink::PageZoomLevelToZoomFactor(zoom_level)));
#endif // defined(FULL_SAFE_BROWSING)
- if (can_send_ping) {
+ if (CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ main_frame_url, reused_password_account_type)) {
saved_passwords_matching_reused_credentials_ =
matching_reused_credentials;
StartRequest(web_contents, main_frame_url, GURL(), GURL(), username,
@@ -151,6 +152,9 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
password_field_exists);
} else {
+ RequestOutcome reason = GetPingNotSentReason(
+ trigger_type, main_frame_url, reused_password_account_type);
+ LogNoPingingReason(trigger_type, reason, reused_password_account_type);
#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
if (reused_password_account_type.is_account_syncing())
MaybeLogPasswordReuseLookupEvent(web_contents, reason, password_type,
@@ -160,8 +164,9 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
}
#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
- if (CanShowInterstitial(reason, reused_password_account_type,
- main_frame_url)) {
+ if (CanShowInterstitial(reused_password_account_type, main_frame_url)) {
+ LogPasswordAlertModeOutcome(RequestOutcome::SUCCEEDED,
+ reused_password_account_type);
username_for_last_shown_warning_ = username;
reused_password_account_type_for_last_shown_warning_ =
reused_password_account_type;
@@ -247,24 +252,9 @@ void PasswordProtectionService::StartRequest(
bool PasswordProtectionService::CanSendPing(
LoginReputationClientRequest::TriggerType trigger_type,
const GURL& main_frame_url,
- ReusedPasswordAccountType password_type,
- RequestOutcome* reason) {
- *reason = RequestOutcome::UNKNOWN;
- bool is_pinging_enabled =
- IsPingingEnabled(trigger_type, password_type, reason);
- // Pinging is enabled for password_reuse trigger level; however we need to
- // make sure *reason is set appropriately.
- PasswordProtectionTrigger trigger_level =
- GetPasswordProtectionWarningTriggerPref(password_type);
- if (trigger_level == PASSWORD_REUSE) {
- *reason = RequestOutcome::PASSWORD_ALERT_MODE;
- }
- if (is_pinging_enabled &&
- !IsURLWhitelistedForPasswordEntry(main_frame_url, reason)) {
- return true;
- }
- LogNoPingingReason(trigger_type, *reason, password_type);
- return false;
+ ReusedPasswordAccountType password_type) {
+ return IsPingingEnabled(trigger_type, password_type) &&
+ !IsURLWhitelistedForPasswordEntry(main_frame_url);
}
void PasswordProtectionService::RequestFinished(
@@ -408,8 +398,8 @@ void PasswordProtectionService::FillUserPopulation(
void PasswordProtectionService::OnURLsDeleted(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindRepeating(&PasswordProtectionService::
RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
GetWeakPtr(), deletion_info.IsAllHistory(),
@@ -551,7 +541,8 @@ bool PasswordProtectionService::IsSupportedPasswordTypeForPinging(
PasswordType password_type) const {
switch (password_type) {
case PasswordType::SAVED_PASSWORD:
- return true;
+ return base::FeatureList::IsEnabled(
+ safe_browsing::kPasswordProtectionForSavedPasswords);
case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
return true;
case PasswordType::ENTERPRISE_PASSWORD:
diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service.h b/chromium/components/safe_browsing/content/password_protection/password_protection_service.h
index 307e95dbd3d..1823146c4b4 100644
--- a/chromium/components/safe_browsing/content/password_protection/password_protection_service.h
+++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service.h
@@ -208,9 +208,7 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
// If |url| matches Safe Browsing whitelist domains, password protection
// change password URL, or password protection login URLs in the enterprise
// policy.
- virtual bool IsURLWhitelistedForPasswordEntry(
- const GURL& url,
- RequestOutcome* reason) const = 0;
+ virtual bool IsURLWhitelistedForPasswordEntry(const GURL& url) const = 0;
// Persist the phished saved password credential in the "compromised
// credentials" table. Calls the password store to add a row for each
@@ -300,14 +298,12 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
friend class PasswordProtectionRequest;
// Chrome can send password protection ping if it is allowed by for the
- // |trigger_type| and if Safe Browsing can compute reputation of
- // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
- // private IP or a local host). Update |reason| if sending ping is not
- // allowed. |password_type| is used for UMA metric recording.
+ // |trigger_type| and |password_type| and if Safe Browsing can compute
+ // reputation of |main_frame_url| (e.g. Safe Browsing is not able to compute
+ // reputation of a private IP or a local host).
bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
const GURL& main_frame_url,
- ReusedPasswordAccountType password_type,
- RequestOutcome* reason);
+ ReusedPasswordAccountType password_type);
// Called by a PasswordProtectionRequest instance when it finishes to remove
// itself from |requests_|.
@@ -364,10 +360,12 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
virtual bool IsIncognito() = 0;
+ virtual bool IsInPasswordAlertMode(
+ ReusedPasswordAccountType password_type) = 0;
+
virtual bool IsPingingEnabled(
LoginReputationClientRequest::TriggerType trigger_type,
- ReusedPasswordAccountType password_type,
- RequestOutcome* reason) = 0;
+ ReusedPasswordAccountType password_type) = 0;
virtual bool IsHistorySyncEnabled() = 0;
@@ -408,10 +406,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
// Determines if we should show chrome://reset-password interstitial based on
- // previous request outcome, the reused |password_type| and the
- // |main_frame_url|.
- virtual bool CanShowInterstitial(RequestOutcome reason,
- ReusedPasswordAccountType password_type,
+ // the reused |password_type| and the |main_frame_url|.
+ virtual bool CanShowInterstitial(ReusedPasswordAccountType password_type,
const GURL& main_frame_url) = 0;
#endif
@@ -422,6 +418,13 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
GetSyncAccountType() const = 0;
+ // Returns the reason why a ping is not sent based on the |trigger_type|,
+ // |url| and |password_type|. Crash if |CanSendPing| is true.
+ virtual RequestOutcome GetPingNotSentReason(
+ LoginReputationClientRequest::TriggerType trigger_type,
+ const GURL& url,
+ ReusedPasswordAccountType password_type) = 0;
+
const std::list<std::string>& common_spoofed_domains() const {
return common_spoofed_domains_;
}
diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc
index d7c00d3fe41..26357224b45 100644
--- a/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc
+++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc
@@ -96,6 +96,8 @@ class TestPhishingDetector : public mojom::PhishingDetector {
mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle)));
}
+ void SetPhishingModel(const std::string& model) override {}
+
void StartPhishingDetection(
const GURL& url,
StartPhishingDetectionCallback callback) override {
@@ -275,7 +277,7 @@ class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> {
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_,
- IsURLWhitelistedForPasswordEntry(_, _))
+ IsURLWhitelistedForPasswordEntry(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
@@ -1166,9 +1168,13 @@ TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::SAVED_PASSWORD);
- EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+ EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- reused_password_account_type, LoginReputationClientResponse::PHISHING));
+ reused_password_account_type,
+ LoginReputationClientResponse::LOW_REPUTATION));
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ safe_browsing::kPasswordProtectionForSavedPasswords);
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
@@ -1330,38 +1336,26 @@ TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) {
.WillRepeatedly(Return(account_info));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
- PasswordType::SAVED_PASSWORD));
- EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::PRIMARY_ACCOUNT_PASSWORD));
-// kPasswordProtectionForSignedInUsers is disabled by default on Android.
-#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-#else
- EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-#endif
PasswordType::OTHER_GAIA_PASSWORD));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::ENTERPRISE_PASSWORD));
-
- EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::SAVED_PASSWORD));
- EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
- PasswordType::ENTERPRISE_PASSWORD));
{
base::test::ScopedFeatureList feature_list;
- feature_list.InitAndDisableFeature(
+ feature_list.InitAndEnableFeature(
safe_browsing::kPasswordProtectionForSignedInUsers);
- // Only ping for signed in, non-syncing users if the experiment is on.
- EXPECT_FALSE(
- password_protection_service_->IsSupportedPasswordTypeForPinging(
- PasswordType::OTHER_GAIA_PASSWORD));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordType::OTHER_GAIA_PASSWORD));
}
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
- safe_browsing::kPasswordProtectionForSignedInUsers);
+ safe_browsing::kPasswordProtectionForSavedPasswords);
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
- PasswordType::OTHER_GAIA_PASSWORD));
+ PasswordType::SAVED_PASSWORD));
}
}
diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils.cc b/chromium/components/safe_browsing/content/password_protection/visual_utils.cc
index 85349795389..c751167b8a3 100644
--- a/chromium/components/safe_browsing/content/password_protection/visual_utils.cc
+++ b/chromium/components/safe_browsing/content/password_protection/visual_utils.cc
@@ -7,9 +7,14 @@
#include "components/safe_browsing/content/password_protection/visual_utils.h"
+#include "base/check_op.h"
#include "base/numerics/checked_math.h"
+#include "components/safe_browsing/core/proto/client_model.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "third_party/opencv/src/emd_wrapper.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkPixmap.h"
+#include "ui/gfx/color_utils.h"
namespace safe_browsing {
namespace visual_utils {
@@ -22,6 +27,87 @@ const int kPHashDownsampleWidth = 288;
const int kPHashDownsampleHeight = 288;
const int kPHashBlockSize = 6;
+// Constants for computing Earth Movers Distance (EMD) between ColorHistograms.
+const float kMaxCentroidDistance = 1.414;
+const float kMaxColorDistance = 14;
+
+size_t GetBitCount(uint8_t byte) {
+ size_t count = 0;
+ for (; byte > 0; byte >>= 1) {
+ if (byte & 1)
+ count++;
+ }
+
+ return count;
+}
+
+bool GetHashDistance(const std::string& hash1,
+ const std::string& hash2,
+ size_t* out) {
+ // The beginning of the hash is a Varint representing the stride. For values
+ // at most 127, this is just the value of the first char. Chrome only
+ // generates strides less than 128, so we can simply extract the first char.
+ if (hash1[0] != hash2[0])
+ return false;
+ if (hash1[0] & 0x80)
+ return false;
+
+ size_t count = std::min(hash1.size(), hash2.size());
+ size_t distance = 8 * (std::max(hash1.size(), hash2.size()) - count);
+ for (size_t i = 1; i < count; i++) {
+ distance += GetBitCount(hash1[i] ^ hash2[i]);
+ }
+
+ *out = distance;
+ return true;
+}
+
+opencv::PointDistribution HistogramBinsToPointDistribution(
+ const google::protobuf::RepeatedPtrField<VisualFeatures::ColorHistogramBin>&
+ bins) {
+ opencv::PointDistribution distribution;
+ distribution.dimensions = 5;
+ for (const VisualFeatures::ColorHistogramBin& bin : bins) {
+ distribution.weights.push_back(bin.weight());
+ std::vector<float> position(5);
+ position[0] = bin.centroid_x() / kMaxCentroidDistance;
+ position[1] = bin.centroid_y() / kMaxCentroidDistance;
+ position[2] = bin.quantized_r() / kMaxColorDistance;
+ position[3] = bin.quantized_g() / kMaxColorDistance;
+ position[4] = bin.quantized_b() / kMaxColorDistance;
+ distribution.positions.push_back(std::move(position));
+ }
+
+ return distribution;
+}
+
+bool ImageHasColorInRange(const SkBitmap& image,
+ const MatchRule::ColorRange& color_range) {
+ for (int i = 0; i < image.width(); i++) {
+ for (int j = 0; j < image.height(); j++) {
+ SkScalar hsv[3];
+ SkColorToHSV(image.getColor(i, j), hsv);
+ if (color_range.low() <= hsv[0] && hsv[0] <= color_range.high())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint8_t GetMedian(const std::vector<uint8_t>& data) {
+ std::vector<uint8_t> buffer;
+ buffer.assign(data.begin(), data.end());
+ std::vector<uint8_t>::iterator middle = buffer.begin() + (buffer.size() / 2);
+ std::nth_element(buffer.begin(), middle, buffer.end());
+ if (buffer.size() % 2 == 1) {
+ return *middle;
+ } else {
+ // For even-sized sets, return the average of the two middle elements.
+ return (*middle + *std::max_element(buffer.begin(), middle)) / 2;
+ }
+}
+
} // namespace
// A QuantizedColor takes the highest 3 bits of R, G, and B, and concatenates
@@ -171,5 +257,101 @@ std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
return target;
}
+std::string GetHashFromBlurredImage(
+ VisualFeatures::BlurredImage blurred_image) {
+ DCHECK_EQ(blurred_image.data().size(),
+ 3u * blurred_image.width() * blurred_image.height());
+ // Convert the blurred image to grayscale.
+ std::vector<uint8_t> luma_values;
+ luma_values.resize(blurred_image.width() * blurred_image.height());
+ for (size_t i = 0; i < luma_values.size(); i++) {
+ luma_values[i] = color_utils::GetLuma(SkColorSetRGB(
+ static_cast<unsigned char>(blurred_image.data()[3 * i]),
+ static_cast<unsigned char>(blurred_image.data()[3 * i + 1]),
+ static_cast<unsigned char>(blurred_image.data()[3 * i + 2])));
+ }
+
+ uint8_t median_luma = GetMedian(luma_values);
+
+ std::string output;
+
+ // The server-side hash generation writes a Varint here, rather than just an
+ // int. These encodings coincide as long as the width is at most 127. If
+ // we begin using larger widths, we need to implement the Varint encoding used
+ // on the server.
+ DCHECK_LT(blurred_image.width(), 128);
+ output.push_back(static_cast<unsigned char>(blurred_image.width()));
+
+ // Write the output bitstring.
+ unsigned char next_byte = 0;
+ int bits_encoded_so_far = 0;
+ for (const uint8_t luma_value : luma_values) {
+ next_byte <<= 1;
+ if (luma_value >= median_luma)
+ next_byte |= 1;
+
+ ++bits_encoded_so_far;
+ if (bits_encoded_so_far == 8) {
+ output.push_back(next_byte);
+ bits_encoded_so_far = 0;
+ }
+ }
+
+ if (bits_encoded_so_far != 0) {
+ next_byte <<= 8 - bits_encoded_so_far;
+ output.push_back(next_byte);
+ }
+
+ return output;
+}
+
+base::Optional<VisionMatchResult> IsVisualMatch(const SkBitmap& image,
+ const VisualTarget& target) {
+ VisualFeatures::BlurredImage blurred_image;
+ if (!GetBlurredImage(image, &blurred_image))
+ return base::nullopt;
+ std::string hash = GetHashFromBlurredImage(blurred_image);
+ size_t hash_distance;
+ bool has_hash_distance = GetHashDistance(hash, target.hash(), &hash_distance);
+
+ VisualFeatures::ColorHistogram histogram;
+ if (!GetHistogramForImage(image, &histogram))
+ return base::nullopt;
+
+ opencv::PointDistribution point_distribution =
+ HistogramBinsToPointDistribution(histogram.bins());
+ base::Optional<double> color_distance = opencv::EMD(
+ point_distribution, HistogramBinsToPointDistribution(target.bins()));
+
+ for (const MatchRule& match_rule : target.match_config().match_rule()) {
+ bool is_match = true;
+ if (match_rule.has_hash_distance()) {
+ is_match &=
+ (has_hash_distance && hash_distance <= match_rule.hash_distance());
+ }
+
+ if (match_rule.has_color_distance()) {
+ is_match &= (color_distance.has_value() &&
+ color_distance.value() <= match_rule.color_distance());
+ }
+
+ for (const MatchRule::ColorRange& color_range : match_rule.color_range()) {
+ is_match &= ImageHasColorInRange(image, color_range);
+ }
+
+ if (is_match) {
+ VisionMatchResult result;
+ result.set_matched_target_digest(target.digest());
+ if (has_hash_distance)
+ result.set_vision_matched_phash_score(hash_distance);
+ if (color_distance.has_value())
+ result.set_vision_matched_emd_score(color_distance.value());
+ return result;
+ }
+ }
+
+ return base::nullopt;
+}
+
} // namespace visual_utils
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils.h b/chromium/components/safe_browsing/content/password_protection/visual_utils.h
index 6d2d24fdf40..34b6764efa5 100644
--- a/chromium/components/safe_browsing/content/password_protection/visual_utils.h
+++ b/chromium/components/safe_browsing/content/password_protection/visual_utils.h
@@ -7,6 +7,8 @@
#include <string>
+#include "base/optional.h"
+#include "components/safe_browsing/core/proto/client_model.pb.h"
#include "components/safe_browsing/core/proto/csd.pb.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -39,6 +41,14 @@ bool GetBlurredImage(const SkBitmap& image,
std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
int block_size);
+// Returns the hash used to compare blurred images.
+std::string GetHashFromBlurredImage(VisualFeatures::BlurredImage blurred_image);
+
+// Returns whether the given |image| is a match for the |target|. Returns
+// nullopt in the case of no match, and the VisionMatchResult if it is a match.
+base::Optional<VisionMatchResult> IsVisualMatch(const SkBitmap& image,
+ const VisualTarget& target);
+
} // namespace visual_utils
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc b/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc
index 140c97884d2..752e65dbe3e 100644
--- a/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc
+++ b/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc
@@ -252,5 +252,264 @@ TEST_F(VisualUtilsTest, BlockMeanAveragePartialBlocks) {
EXPECT_EQ(*blocks->getAddr32(1, 1), kBlue);
}
+TEST_F(VisualUtilsTest, IsVisualMatchHash) {
+ {
+ // An all-white image should hash to all 1-bits.
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+
+ std::vector<unsigned char> target_hash;
+ target_hash.push_back('\x30');
+ for (int i = 0; i < 288; i++)
+ target_hash.push_back('\xff');
+
+ VisualTarget target;
+ target.set_hash(target_hash.data(), target_hash.size());
+ target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+ }
+
+ {
+ // Make the top quarter black, and the corresponding bits of the hash should
+ // be 0.
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 250; y++)
+ *bitmap_.getAddr32(x, y) = kBlack;
+
+ std::vector<unsigned char> target_hash;
+ target_hash.push_back('\x30');
+ for (int i = 0; i < 72; i++)
+ target_hash.push_back('\x00');
+ for (int i = 0; i < 216; i++)
+ target_hash.push_back('\xff');
+ VisualTarget target;
+ target.set_hash(target_hash.data(), target_hash.size());
+
+ target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+ }
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchHashPartialMatch) {
+ // Make the top quarter black, and the corresponding bits of the hash should
+ // be 0.
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 250; y++)
+ *bitmap_.getAddr32(x, y) = kBlack;
+ for (int x = 0; x < 1000; x++)
+ for (int y = 250; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+
+ std::vector<unsigned char> target_hash;
+ target_hash.push_back('\x30');
+ for (int i = 0; i < 75; i++)
+ target_hash.push_back('\x00');
+ for (int i = 0; i < 213; i++)
+ target_hash.push_back('\xff');
+
+ VisualTarget target;
+ target.set_hash(target_hash.data(), target_hash.size());
+ target.mutable_match_config()->add_match_rule()->set_hash_distance(23.0);
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+ target.mutable_match_config()->add_match_rule()->set_hash_distance(24.0);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchHashStrideComparison) {
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+
+ std::vector<unsigned char> target_hash;
+ target_hash.push_back('\x30');
+ for (int i = 0; i < 288; i++)
+ target_hash.push_back('\xff');
+
+ VisualTarget target;
+ target.set_hash(target_hash.data(), target_hash.size());
+ target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ target_hash[0] = '\x00';
+ target.set_hash(target_hash.data(), target_hash.size());
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchHistogramOnly) {
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+
+ {
+ VisualTarget target;
+ VisualFeatures::ColorHistogramBin* bin = target.add_bins();
+ // Our coordinates range from 0 to 999, giving an average of 0.4995 instead
+ // of 0.5.
+ bin->set_centroid_x(0.4995);
+ bin->set_centroid_y(0.4995);
+ bin->set_quantized_r(7);
+ bin->set_quantized_g(7);
+ bin->set_quantized_b(7);
+ bin->set_weight(1.0);
+ target.mutable_match_config()->add_match_rule()->set_color_distance(0.0);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+ }
+
+ {
+ // Move the target histogram to have centroid 0,0. This leads to a distance
+ // just under 0.5 between the actual and target histograms.
+ VisualTarget target;
+ VisualFeatures::ColorHistogramBin* bin = target.add_bins();
+ bin->set_centroid_x(0);
+ bin->set_centroid_y(0);
+ bin->set_quantized_r(7);
+ bin->set_quantized_g(7);
+ bin->set_quantized_b(7);
+ bin->set_weight(1.0);
+
+ MatchRule* match_rule = target.mutable_match_config()->add_match_rule();
+ match_rule->set_color_distance(0.5);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ match_rule->set_color_distance(0.4);
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+ }
+
+ {
+ // Change the target histogram color slightly. This leads to a distance
+ // of roughly 0.12 between the actual and target histogram.
+ VisualTarget target;
+ VisualFeatures::ColorHistogramBin* bin = target.add_bins();
+ bin->set_centroid_x(0.4995);
+ bin->set_centroid_y(0.4995);
+ bin->set_quantized_r(6);
+ bin->set_quantized_g(6);
+ bin->set_quantized_b(6);
+ bin->set_weight(1.0);
+
+ MatchRule* match_rule = target.mutable_match_config()->add_match_rule();
+ match_rule->set_color_distance(0.2);
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ match_rule->set_color_distance(0.1);
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+ }
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchColorRange) {
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+ *bitmap_.getAddr32(0, 0) = kBlue;
+
+ SkScalar hsv[3];
+ SkColorToHSV(bitmap_.getColor(0, 0), hsv);
+ SkScalar target_hue = hsv[0];
+ VisualTarget target;
+ MatchRule::ColorRange* color_range =
+ target.mutable_match_config()->add_match_rule()->add_color_range();
+ color_range->set_low(target_hue);
+ color_range->set_high(target_hue);
+
+ // Blue hue present
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ // Color range too high
+ color_range->set_low(target_hue + 1);
+ color_range->set_high(target_hue + 1);
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+
+ // Color range too low
+ color_range->set_low(target_hue - 1);
+ color_range->set_high(target_hue - 1);
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+
+ // No blue hue present
+ *bitmap_.getAddr32(0, 0) = kWhite;
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchMultipleColorRanges) {
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+ *bitmap_.getAddr32(0, 0) = kBlue;
+ *bitmap_.getAddr32(1, 0) = kGreen;
+
+ SkScalar hsv[3];
+ SkColorToHSV(bitmap_.getColor(0, 0), hsv);
+ SkScalar blue_hue = hsv[0];
+ VisualTarget target;
+ MatchRule* match_rule = target.mutable_match_config()->add_match_rule();
+ MatchRule::ColorRange* color_range = match_rule->add_color_range();
+ color_range->set_low(blue_hue);
+ color_range->set_high(blue_hue);
+
+ SkColorToHSV(bitmap_.getColor(1, 0), hsv);
+ SkScalar green_hue = hsv[0];
+ color_range = match_rule->add_color_range();
+ color_range->set_low(green_hue);
+ color_range->set_high(green_hue);
+
+ // Both hues present
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ // No blue hue present
+ *bitmap_.getAddr32(0, 0) = kWhite;
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+
+ // No green hue present
+ *bitmap_.getAddr32(0, 0) = kBlue;
+ *bitmap_.getAddr32(1, 0) = kWhite;
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+
+ // Neither hue present
+ *bitmap_.getAddr32(0, 0) = kWhite;
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+}
+
+TEST_F(VisualUtilsTest, IsVisualMatchMultipleMatchRules) {
+ for (int x = 0; x < 1000; x++)
+ for (int y = 0; y < 1000; y++)
+ *bitmap_.getAddr32(x, y) = kWhite;
+ *bitmap_.getAddr32(0, 0) = kBlue;
+ *bitmap_.getAddr32(1, 0) = kGreen;
+
+ // Create a target with two match rules, one matching blue pixels and one
+ // matching green.
+ VisualTarget target;
+ MatchRule* match_rule_blue = target.mutable_match_config()->add_match_rule();
+ MatchRule::ColorRange* color_range = match_rule_blue->add_color_range();
+ SkScalar hsv[3];
+ SkColorToHSV(bitmap_.getColor(0, 0), hsv);
+ SkScalar blue_hue = hsv[0];
+ color_range->set_low(blue_hue);
+ color_range->set_high(blue_hue);
+
+ MatchRule* match_rule_green = target.mutable_match_config()->add_match_rule();
+ color_range = match_rule_green->add_color_range();
+ SkColorToHSV(bitmap_.getColor(1, 0), hsv);
+ SkScalar green_hue = hsv[0];
+ color_range->set_low(green_hue);
+ color_range->set_high(green_hue);
+
+ // Both hues present
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ // No blue hue present
+ *bitmap_.getAddr32(0, 0) = kWhite;
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ // No green hue present
+ *bitmap_.getAddr32(0, 0) = kBlue;
+ *bitmap_.getAddr32(1, 0) = kWhite;
+ EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value());
+
+ // Neither hue present
+ *bitmap_.getAddr32(0, 0) = kWhite;
+ EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value());
+}
+
} // namespace visual_utils
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc
index 4b92b8fcffa..d9fef3975a1 100644
--- a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc
+++ b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc
@@ -23,22 +23,9 @@ namespace safe_browsing {
WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle(
mojom::SafeBrowsing* safe_browsing,
int render_frame_id)
- : render_frame_id_(render_frame_id),
- safe_browsing_(safe_browsing),
- result_(Result::UNKNOWN) {}
+ : render_frame_id_(render_frame_id), safe_browsing_(safe_browsing) {}
-WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() {
- // ThrottleHandshake() should always be called, but since that is done all the
- // way over in Blink, just avoid logging if it is not called rather than
- // DCHECK()ing.
- if (start_time_.is_null())
- return;
- if (result_ == Result::UNKNOWN) {
- result_ = Result::ABANDONED;
- UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned",
- base::TimeTicks::Now() - start_time_);
- }
-}
+WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() = default;
void WebSocketSBHandshakeThrottle::ThrottleHandshake(
const blink::WebURL& url,
@@ -48,7 +35,8 @@ void WebSocketSBHandshakeThrottle::ThrottleHandshake(
completion_callback_ = std::move(completion_callback);
url_ = url;
int load_flags = 0;
- start_time_ = base::TimeTicks::Now();
+ DCHECK_EQ(state_, State::kInitial);
+ state_ = State::kStarted;
safe_browsing_->CreateCheckerAndCheck(
render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), url, "GET",
net::HttpRequestHeaders(), load_flags,
@@ -65,17 +53,14 @@ void WebSocketSBHandshakeThrottle::ThrottleHandshake(
void WebSocketSBHandshakeThrottle::OnCompleteCheck(bool proceed,
bool showed_interstitial) {
- DCHECK(!start_time_.is_null());
- base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
+ DCHECK_EQ(state_, State::kStarted);
if (proceed) {
- result_ = Result::SAFE;
- UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed);
+ state_ = State::kSafe;
std::move(completion_callback_).Run(base::nullopt);
} else {
// When the insterstitial is dismissed the page is navigated and this object
// is destroyed before reaching here.
- result_ = Result::BLOCKED;
- UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed);
+ state_ = State::kBlocked;
std::move(completion_callback_)
.Run(blink::WebString::FromUTF8(base::StringPrintf(
"WebSocket connection to %s failed safe browsing check",
@@ -93,7 +78,8 @@ void WebSocketSBHandshakeThrottle::OnCheckResult(
return;
}
- // TODO(yzshen): Notify the network service to pause processing response body.
+ // TODO(yzshen): Notify the network service to stop reading from the
+ // WebSocket.
if (!notifier_receiver_) {
notifier_receiver_ =
std::make_unique<mojo::Receiver<mojom::UrlCheckNotifier>>(this);
@@ -102,14 +88,12 @@ void WebSocketSBHandshakeThrottle::OnCheckResult(
}
void WebSocketSBHandshakeThrottle::OnMojoDisconnect() {
- DCHECK_EQ(result_, Result::UNKNOWN);
+ DCHECK(state_ == State::kStarted);
url_checker_.reset();
notifier_receiver_.reset();
- // Make the destructor record NOT_SUPPORTED in the result histogram.
- result_ = Result::NOT_SUPPORTED;
- // Don't record the time elapsed because it's unlikely to be meaningful.
+ state_ = State::kNotSupported;
std::move(completion_callback_).Run(base::nullopt);
// |this| is destroyed here.
}
diff --git a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h
index 53f7761c78b..c703942d2e0 100644
--- a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h
+++ b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h
@@ -35,14 +35,12 @@ class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle,
completion_callback) override;
private:
- // These values are logged to UMA so do not renumber or reuse.
- enum class Result {
- UNKNOWN = 0,
- SAFE = 1,
- BLOCKED = 2,
- ABANDONED = 3,
- NOT_SUPPORTED = 4,
- RESULT_COUNT
+ enum class State {
+ kInitial,
+ kStarted,
+ kSafe,
+ kBlocked,
+ kNotSupported,
};
// mojom::UrlCheckNotifier implementation.
@@ -60,8 +58,10 @@ class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle,
mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
mojom::SafeBrowsing* safe_browsing_;
std::unique_ptr<mojo::Receiver<mojom::UrlCheckNotifier>> notifier_receiver_;
- base::TimeTicks start_time_;
- Result result_;
+
+ // |state_| is used to validate that events happen in the right order. It
+ // isn't used to control the behaviour of the class.
+ State state_ = State::kInitial;
base::WeakPtrFactory<WebSocketSBHandshakeThrottle> weak_factory_{this};
diff --git a/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc
index ce197ab32b7..3206c87eb4e 100644
--- a/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc
+++ b/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc
@@ -14,7 +14,6 @@
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/content/triggers/trigger_util.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/triggers/trigger_manager.h"
@@ -68,8 +67,7 @@ AdPopupTrigger::AdPopupTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- task_runner_(
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+ task_runner_(content::GetUIThreadTaskRunner({})) {}
AdPopupTrigger::~AdPopupTrigger() {}
diff --git a/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc
index a281138194a..842274ef16f 100644
--- a/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc
+++ b/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc
@@ -14,7 +14,6 @@
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/content/triggers/trigger_util.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/triggers/trigger_manager.h"
@@ -67,8 +66,7 @@ AdRedirectTrigger::AdRedirectTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- task_runner_(
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+ task_runner_(content::GetUIThreadTaskRunner({})) {}
AdRedirectTrigger::~AdRedirectTrigger() {}
diff --git a/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc
index afefe48ba55..7cf7372f043 100644
--- a/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc
+++ b/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc
@@ -14,7 +14,6 @@
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/content/triggers/trigger_util.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/triggers/trigger_manager.h"
@@ -94,8 +93,7 @@ AdSamplerTrigger::AdSamplerTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- task_runner_(
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+ task_runner_(content::GetUIThreadTaskRunner({})) {}
AdSamplerTrigger::~AdSamplerTrigger() {}
diff --git a/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc b/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc
index e531a18b18b..f144ab0f24b 100644
--- a/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc
+++ b/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc
@@ -8,7 +8,6 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
-#include "base/task/post_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/triggers/trigger_manager.h"
@@ -68,8 +67,7 @@ SuspiciousSiteTrigger::SuspiciousSiteTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- task_runner_(
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+ task_runner_(content::GetUIThreadTaskRunner({})) {}
SuspiciousSiteTrigger::~SuspiciousSiteTrigger() {}
diff --git a/chromium/components/safe_browsing/content/web_ui/BUILD.gn b/chromium/components/safe_browsing/content/web_ui/BUILD.gn
index aa148b1849d..1658cb6b147 100644
--- a/chromium/components/safe_browsing/content/web_ui/BUILD.gn
+++ b/chromium/components/safe_browsing/content/web_ui/BUILD.gn
@@ -12,6 +12,7 @@ static_library("web_ui") {
deps = [
"//base",
+ "//components/enterprise/common/proto:connectors_proto",
"//components/password_manager/core/browser:hash_password_manager",
"//components/resources:components_resources_grit",
"//components/resources:components_scaled_resources_grit",
diff --git a/chromium/components/safe_browsing/content/web_ui/DEPS b/chromium/components/safe_browsing/content/web_ui/DEPS
index 4019a99d985..c4dfe28ac40 100644
--- a/chromium/components/safe_browsing/content/web_ui/DEPS
+++ b/chromium/components/safe_browsing/content/web_ui/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/enterprise/common/proto/connectors.pb.h",
"+components/grit/components_resources.h",
"+components/password_manager/core/browser/hash_password_manager.h",
"+components/user_prefs",
diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css
index 6e6c6e0b70d..2d864d2f6aa 100644
--- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css
+++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css
@@ -4,7 +4,7 @@
body {
color: rgb(48, 57, 66);
- margin:15px;
+ margin: 15px;
}
p {
white-space: pre-wrap;
@@ -13,25 +13,36 @@ p {
background-color: #fbfbfb;
border: 1px solid #cecece;
border-radius: 3px;
- padding: 19px;
line-height: 1.5;
+ padding: 19px;
}
#sb-title {
font-size: 2em;
margin-bottom: 0.8em;
}
-h1, h2, h3, p {
+h1,
+h2,
+h3,
+p {
font-weight: normal;
line-height: 1.5;
}
table.request-response {
- table-layout:fixed;
- width: 100%;
- word-break:break-all;
- white-space:pre-wrap;
border: 1px solid #cecece;
border-radius: 3px;
+ table-layout: fixed;
+ white-space: pre-wrap;
+ width: 100%;
+ word-break: break-all;
}
table.request-response td {
width: 50%;
}
+.bold-span {
+ font-weight: bold;
+}
+.result-container {
+ font-weight: normal;
+ line-height: 1.5;
+ white-space: normal;
+}
diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html
index df4d006c887..75689ec8485 100644
--- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html
+++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html
@@ -35,21 +35,21 @@
<tabpanel>
<h2>Experiments</h2>
<div class="content">
- <p id="experiments-list"></p>
+ <p id="experiments-list" class="result-container"></p>
</div>
<h2>Preferences</h2>
<div class="content">
- <p id="preferences-list"></p>
+ <p id="preferences-list" class="result-container"></p>
</div>
<h2>Safe Browsing Cookie</h2>
<div class="content">
- <p id="cookie-panel"></p>
+ <p id="cookie-panel" class="result-container"></p>
</div>
</tabpanel>
<tabpanel>
<h2>Database Manager</h2>
<div class="content">
- <p id="database-info-list"></p>
+ <p id="database-info-list" class="result-container"></p>
</div>
</tabpanel>
<tabpanel>
@@ -92,7 +92,7 @@
</tabpanel>
<tabpanel>
<h2>RT Lookup Pings</h2>
- <p id="rt-lookup-experiment-enabled"></p>
+ <p id="rt-lookup-experiment-enabled" class="result-container"></p>
<table id="rt-lookup-ping-list" class="request-response"></table>
</tabpanel>
<tabpanel>
@@ -123,6 +123,24 @@
</tabpanel>
</tabpanels>
</tabbox>
+ <template id="result-template">
+ <div>
+ <span class="bold-span"></span>
+ <span></span>
+ </div>
+ </template>
+ <template id="cookie-template">
+ <div>
+ <span class="bold-span">Value: </span>
+ <span class="result"></span>
+ </div>
+ <span class="bold-span">Created: </span>
+ <span class="result"></span>
+ </template>
+ <template id="rt-lookup-template">
+ <span class="bold-span">RT Lookup Experiment Enabled: </span>
+ <span id="experiment-bool"></span>
+ </template>
<script src="safe_browsing.js"></script>
</body>
</html>
diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js
index bdb119528f1..f5ec1b4274d 100644
--- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js
+++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js
@@ -161,62 +161,79 @@ cr.define('safe_browsing', function() {
function addExperiments(result) {
const resLength = result.length;
- let experimentsListFormatted = '';
for (let i = 0; i < resLength; i += 2) {
- experimentsListFormatted += "<div><b>" + result[i + 1] +
- "</b>: " + result[i] + "</div>";
+ const experimentsListFormatted =
+ $('result-template').content.cloneNode(true);
+ experimentsListFormatted.querySelectorAll('span')[0].textContent =
+ result[i + 1] + ': ';
+ experimentsListFormatted.querySelectorAll('span')[1].textContent =
+ result[i];
+ $('experiments-list').appendChild(experimentsListFormatted);
}
- $('experiments-list').innerHTML = experimentsListFormatted;
}
function addPrefs(result) {
const resLength = result.length;
- let preferencesListFormatted = "";
for (let i = 0; i < resLength; i += 2) {
- preferencesListFormatted += "<div><b>" + result[i + 1] + "</b>: " +
- result[i] + "</div>";
+ const preferencesListFormatted =
+ $('result-template').content.cloneNode(true);
+ preferencesListFormatted.querySelectorAll('span')[0].textContent =
+ result[i + 1] + ': ';
+ preferencesListFormatted.querySelectorAll('span')[1].textContent =
+ result[i];
+ $('preferences-list').appendChild(preferencesListFormatted);
}
- $('preferences-list').innerHTML = preferencesListFormatted;
}
function addCookie(result) {
- const cookieFormatted = '<b>Value:</b> ' + result[0] + '\n' +
- '<b>Created:</b> ' + (new Date(result[1])).toLocaleString();
- $('cookie-panel').innerHTML = cookieFormatted;
+ const cookieFormatted = $('cookie-template').content.cloneNode(true);
+ cookieFormatted.querySelectorAll('.result')[0].textContent = result[0];
+ cookieFormatted.querySelectorAll('.result')[1].textContent =
+ (new Date(result[1])).toLocaleString();
+ $('cookie-panel').appendChild(cookieFormatted);
}
function addSavedPasswords(result) {
const resLength = result.length;
- let savedPasswordFormatted = "";
for (let i = 0; i < resLength; i += 2) {
- savedPasswordFormatted += "<div>" + result[i];
- if (result[i+1]) {
- savedPasswordFormatted += " (GAIA password)";
- } else {
- savedPasswordFormatted += " (Enterprise password)";
- }
- savedPasswordFormatted += "</div>";
+ const savedPasswordFormatted = document.createElement('div');
+ const suffix = result[i + 1] ? 'GAIA password' : 'Enterprise password';
+ savedPasswordFormatted.textContent = `${result[i]} (${suffix})`;
+ $('saved-passwords').appendChild(savedPasswordFormatted);
}
-
- $('saved-passwords').innerHTML = savedPasswordFormatted;
}
function addDatabaseManagerInfo(result) {
const resLength = result.length;
- let preferencesListFormatted = "";
for (let i = 0; i < resLength; i += 2) {
- preferencesListFormatted += "<div><b>" + result[i] + "</b>: " +
- result[i + 1] + "</div>";
+ const preferencesListFormatted =
+ $('result-template').content.cloneNode(true);
+ preferencesListFormatted.querySelectorAll('span')[0].textContent =
+ result[i] + ': ';
+ const value = result[i + 1];
+ if (Array.isArray(value)) {
+ const blockQuote = document.createElement('blockquote');
+ value.forEach(item => {
+ const div = document.createElement('div');
+ div.textContent = item;
+ blockQuote.appendChild(div);
+ });
+ preferencesListFormatted.querySelectorAll('span')[1].appendChild(
+ blockQuote);
+ } else {
+ preferencesListFormatted.querySelectorAll('span')[1].textContent =
+ value;
+ }
+ $('database-info-list').appendChild(preferencesListFormatted);
}
- $('database-info-list').innerHTML = preferencesListFormatted;
}
function addFullHashCacheInfo(result) {
- $('full-hash-cache-info').innerHTML = result;
+ $('full-hash-cache-info').textContent = result;
}
function addSentClientDownloadRequestsInfo(result) {
@@ -307,8 +324,9 @@ cr.define('safe_browsing', function() {
}
function addRTLookupExperimentEnabled(enabled) {
- const enabledFormatted = '<b>RT Lookup Experiment Enabled:</b> ' + enabled;
- $('rt-lookup-experiment-enabled').innerHTML = enabledFormatted;
+ const enabledFormatted = $('rt-lookup-template').content.cloneNode(true);
+ enabledFormatted.querySelector('#experiment-bool').textContent = enabled;
+ $('rt-lookup-experiment-enabled').appendChild(enabledFormatted);
}
function addLogMessage(result) {
@@ -339,7 +357,8 @@ cr.define('safe_browsing', function() {
cr.sendWithPromise('getReferrerChain', $('referrer-chain-url').value)
.then((response) => {
- $('referrer-chain-content').innerHTML = response;
+ $('referrer-chain-content').innerHTML = trustedTypes.emptyHTML;
+ $('referrer-chain-content').textContent = response;
});
}
diff --git a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
index 8dd703da6b7..c04f24f0556 100644
--- a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
+++ b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -14,6 +14,7 @@
#include "base/base64url.h"
#include "base/bind.h"
#include "base/callback.h"
+#include "base/i18n/number_formatting.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
@@ -21,9 +22,9 @@
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/grit/components_resources.h"
#include "components/grit/components_scaled_resources.h"
#include "components/password_manager/core/browser/hash_password_manager.h"
@@ -219,8 +220,8 @@ void WebUIInfoSingleton::LogMessage(const std::string& message) {
base::Time timestamp = base::Time::Now();
log_messages_.push_back(std::make_pair(timestamp, message));
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners,
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners,
timestamp, message));
}
@@ -271,6 +272,26 @@ void WebUIInfoSingleton::AddToDeepScanRequests(
request.request_token(), deep_scan_requests_[request.request_token()]);
}
+void WebUIInfoSingleton::AddToDeepScanRequests(
+ const enterprise_connectors::ContentAnalysisRequest& request) {
+ if (!HasListener())
+ return;
+
+ // Only update the request time the first time we see a token.
+ if (deep_scan_requests_.find(request.request_token()) ==
+ deep_scan_requests_.end()) {
+ deep_scan_requests_[request.request_token()].request_time =
+ base::Time::Now();
+ }
+
+ deep_scan_requests_[request.request_token()].content_analysis_request =
+ request;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyDeepScanJsListener(
+ request.request_token(), deep_scan_requests_[request.request_token()]);
+}
+
void WebUIInfoSingleton::AddToDeepScanResponses(
const std::string& token,
const std::string& status,
@@ -286,6 +307,21 @@ void WebUIInfoSingleton::AddToDeepScanResponses(
webui_listener->NotifyDeepScanJsListener(token, deep_scan_requests_[token]);
}
+void WebUIInfoSingleton::AddToDeepScanResponses(
+ const std::string& token,
+ const std::string& status,
+ const enterprise_connectors::ContentAnalysisResponse& response) {
+ if (!HasListener())
+ return;
+
+ deep_scan_requests_[token].response_time = base::Time::Now();
+ deep_scan_requests_[token].response_status = status;
+ deep_scan_requests_[token].content_analysis_response = response;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyDeepScanJsListener(token, deep_scan_requests_[token]);
+}
+
void WebUIInfoSingleton::ClearDeepScans() {
base::flat_map<std::string, DeepScanDebugData>().swap(deep_scan_requests_);
}
@@ -369,34 +405,33 @@ void AddStoreInfo(const DatabaseManagerInfo::DatabaseInfo::StoreInfo store_info,
database_info_list->Append(base::Value("Unknown store"));
}
- std::string store_info_string = "<blockquote>";
+ base::Value store_info_list(base::Value::Type::LIST);
if (store_info.has_file_size_bytes()) {
- store_info_string +=
- "Size (in bytes): " + std::to_string(store_info.file_size_bytes()) +
- "<br>";
+ store_info_list.Append(
+ "Size (in bytes): " +
+ base::UTF16ToUTF8(base::FormatNumber(store_info.file_size_bytes())));
}
if (store_info.has_update_status()) {
- store_info_string +=
- "Update status: " + std::to_string(store_info.update_status()) + "<br>";
+ store_info_list.Append(
+ "Update status: " +
+ base::UTF16ToUTF8(base::FormatNumber(store_info.update_status())));
}
if (store_info.has_last_apply_update_time_millis()) {
- store_info_string += "Last update time: " +
- UserReadableTimeFromMillisSinceEpoch(
- store_info.last_apply_update_time_millis())
- .GetString() +
- "<br>";
+ store_info_list.Append("Last update time: " +
+ UserReadableTimeFromMillisSinceEpoch(
+ store_info.last_apply_update_time_millis())
+ .GetString());
}
if (store_info.has_checks_attempted()) {
- store_info_string += "Number of database checks: " +
- std::to_string(store_info.checks_attempted()) + "<br>";
+ store_info_list.Append(
+ "Number of database checks: " +
+ base::UTF16ToUTF8(base::FormatNumber(store_info.checks_attempted())));
}
- store_info_string += "</blockquote>";
-
- database_info_list->Append(base::Value(store_info_string));
+ database_info_list->Append(std::move(store_info_list));
}
void AddDatabaseInfo(const DatabaseManagerInfo::DatabaseInfo database_info,
@@ -1304,6 +1339,52 @@ base::Value SerializeReportingEvent(const base::Value& event) {
}
#if BUILDFLAG(FULL_SAFE_BROWSING)
+std::string SerializeContentAnalysisRequest(
+ const enterprise_connectors::ContentAnalysisRequest& request) {
+ base::DictionaryValue request_dict;
+
+ request_dict.SetKey("device_token", base::Value(request.device_token()));
+ request_dict.SetKey("fcm_notification_token",
+ base::Value(request.fcm_notification_token()));
+ switch (request.analysis_connector()) {
+ case enterprise_connectors::ANALYSIS_CONNECTOR_UNSPECIFIED:
+ request_dict.SetStringKey("analysis_connector", "UNSPECIFIED");
+ break;
+ case enterprise_connectors::FILE_ATTACHED:
+ request_dict.SetStringKey("analysis_connector", "FILE_ATTACHED");
+ break;
+ case enterprise_connectors::FILE_DOWNLOADED:
+ request_dict.SetStringKey("analysis_connector", "FILE_DOWNLOADED");
+ break;
+ case enterprise_connectors::BULK_DATA_ENTRY:
+ request_dict.SetStringKey("analysis_connector", "BULK_DATA_ENTRY");
+ break;
+ }
+
+ if (request.has_request_data()) {
+ base::DictionaryValue request_data;
+ request_data.SetStringKey("url", request.request_data().url());
+ request_data.SetStringKey("filename", request.request_data().filename());
+ request_data.SetStringKey("digest", request.request_data().digest());
+ // TODO(domfc): Improve this once csd is populated for this proto.
+ request_data.SetStringKey("csd",
+ request.request_data().csd().SerializeAsString());
+ request_dict.SetKey("request_data", std::move(request_data));
+ }
+
+ base::ListValue tags;
+ for (const std::string& tag : request.tags())
+ tags.Append(base::Value(tag));
+ request_dict.SetKey("tags", std::move(tags));
+ request_dict.SetKey("request_token", base::Value(request.request_token()));
+
+ std::string request_serialized;
+ JSONStringValueSerializer serializer(&request_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(request_dict);
+ return request_serialized;
+}
+
std::string SerializeDeepScanningRequest(
const DeepScanningClientRequest& request) {
base::DictionaryValue request_dict;
@@ -1362,6 +1443,68 @@ std::string SerializeDeepScanningRequest(
return request_serialized;
}
+std::string SerializeContentAnalysisResponse(
+ const enterprise_connectors::ContentAnalysisResponse& response) {
+ base::DictionaryValue response_dict;
+
+ response_dict.SetStringKey("token", response.request_token());
+
+ base::ListValue result_values;
+ for (const auto& result : response.results()) {
+ base::DictionaryValue result_value;
+ switch (result.status()) {
+ case enterprise_connectors::ContentAnalysisResponse::Result::
+ STATUS_UNKNOWN:
+ result_value.SetStringKey("status", "STATUS_UNKNOWN");
+ break;
+ case enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS:
+ result_value.SetStringKey("status", "SUCCESS");
+ break;
+ case enterprise_connectors::ContentAnalysisResponse::Result::FAILURE:
+ result_value.SetStringKey("status", "FAILURE");
+ break;
+ }
+ result_value.SetStringKey("tag", result.tag());
+
+ base::ListValue triggered_rules;
+ for (const auto& rule : result.triggered_rules()) {
+ base::DictionaryValue rule_value;
+
+ switch (rule.action()) {
+ case enterprise_connectors::ContentAnalysisResponse::Result::
+ TriggeredRule::ACTION_UNSPECIFIED:
+ rule_value.SetStringKey("action", "ACTION_UNSPECIFIED");
+ break;
+ case enterprise_connectors::ContentAnalysisResponse::Result::
+ TriggeredRule::REPORT_ONLY:
+ rule_value.SetStringKey("action", "REPORT_ONLY");
+ break;
+ case enterprise_connectors::ContentAnalysisResponse::Result::
+ TriggeredRule::WARN:
+ rule_value.SetStringKey("action", "WARN");
+ break;
+ case enterprise_connectors::ContentAnalysisResponse::Result::
+ TriggeredRule::BLOCK:
+ rule_value.SetStringKey("action", "BLOCK");
+ break;
+ }
+
+ rule_value.SetStringKey("rule_name", rule.rule_name());
+ rule_value.SetStringKey("rule_id", rule.rule_id());
+ triggered_rules.Append(std::move(rule_value));
+ }
+ result_value.SetKey("triggered_rules", std::move(triggered_rules));
+ result_values.Append(std::move(result_value));
+ }
+ response_dict.SetKey("results", std::move(result_values));
+
+ std::string response_serialized;
+ JSONStringValueSerializer serializer(&response_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(response_dict);
+ return response_serialized;
+}
+
std::string SerializeDeepScanningResponse(
const DeepScanningClientResponse& response) {
base::DictionaryValue response_dict;
@@ -1471,6 +1614,9 @@ base::Value SerializeDeepScanDebugData(const std::string& token,
if (data.request.has_value()) {
value.SetStringKey("request",
SerializeDeepScanningRequest(data.request.value()));
+ } else if (data.content_analysis_request.has_value()) {
+ value.SetStringKey("request", SerializeContentAnalysisRequest(
+ data.content_analysis_request.value()));
}
if (!data.response_time.is_null()) {
@@ -1484,6 +1630,9 @@ base::Value SerializeDeepScanDebugData(const std::string& token,
if (data.response.has_value()) {
value.SetStringKey("response",
SerializeDeepScanningResponse(data.response.value()));
+ } else if (data.content_analysis_response.has_value()) {
+ value.SetStringKey("response", SerializeContentAnalysisResponse(
+ data.content_analysis_response.value()));
}
return std::move(value);
diff --git a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h
index d7b296688be..8e10f4700ff 100644
--- a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h
+++ b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/macros.h"
+#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
#include "components/safe_browsing/core/proto/csd.pb.h"
@@ -42,10 +43,14 @@ struct DeepScanDebugData {
base::Time request_time;
base::Optional<DeepScanningClientRequest> request;
+ base::Optional<enterprise_connectors::ContentAnalysisRequest>
+ content_analysis_request;
base::Time response_time;
std::string response_status;
base::Optional<DeepScanningClientResponse> response;
+ base::Optional<enterprise_connectors::ContentAnalysisResponse>
+ content_analysis_response;
};
#endif
@@ -327,12 +332,18 @@ class WebUIInfoSingleton {
// identifier that can be used in |AddToDeepScanResponses| to correlate a ping
// and response.
void AddToDeepScanRequests(const DeepScanningClientRequest& request);
+ void AddToDeepScanRequests(
+ const enterprise_connectors::ContentAnalysisRequest& request);
// Add the new response to |deep_scan_requests_| and send it to all the open
// chrome://safe-browsing tabs.
void AddToDeepScanResponses(const std::string& token,
const std::string& status,
const DeepScanningClientResponse& response);
+ void AddToDeepScanResponses(
+ const std::string& token,
+ const std::string& status,
+ const enterprise_connectors::ContentAnalysisResponse& response);
// Clear the list of deep scan requests and responses.
void ClearDeepScans();
diff --git a/chromium/components/safe_browsing/core/BUILD.gn b/chromium/components/safe_browsing/core/BUILD.gn
index 550f077ab5e..65ef314d0e7 100644
--- a/chromium/components/safe_browsing/core/BUILD.gn
+++ b/chromium/components/safe_browsing/core/BUILD.gn
@@ -16,7 +16,17 @@ static_library("features") {
]
}
+# Because csd.proto is included from a proto file outside its own directory,
+# proto_in_dir is required so that import can follow the Chromium style guide
+# that all imports start from the source root.
+#
+# However, due to the way that protoc generates C++ code, this directive is
+# also required in all build targets for protos that import csd.proto, including
+# the protos in the same directory. Those protos also need to import csd.proto
+# using a path from the source root, otherwise they won't compile.
+
proto_library("csd_proto") {
+ proto_in_dir = "//"
sources = [ "proto/csd.proto" ]
}
@@ -25,6 +35,7 @@ proto_library("webui_proto") {
}
proto_library("realtimeapi_proto") {
+ proto_in_dir = "//"
sources = [ "proto/realtimeapi.proto" ]
deps = [ ":csd_proto" ]
}
@@ -34,6 +45,7 @@ proto_library("webprotect_proto") {
}
proto_library("client_model_proto") {
+ proto_in_dir = "//"
sources = [ "proto/client_model.proto" ]
deps = [ ":csd_proto" ]
}
@@ -98,6 +110,7 @@ source_set("verdict_cache_manager_unittest") {
":realtimeapi_proto",
":verdict_cache_manager",
"//base",
+ "//base/test:test_support",
"//components/content_settings/core/browser",
"//components/safe_browsing/core/common:test_support",
"//components/sync_preferences:test_support",
diff --git a/chromium/components/safe_browsing/core/browser/BUILD.gn b/chromium/components/safe_browsing/core/browser/BUILD.gn
index f2d6ceef6a4..658c9328194 100644
--- a/chromium/components/safe_browsing/core/browser/BUILD.gn
+++ b/chromium/components/safe_browsing/core/browser/BUILD.gn
@@ -20,7 +20,7 @@ jumbo_source_set("browser") {
"//components/safe_browsing/core/db:database_manager",
"//components/safe_browsing/core/db:util",
"//components/safe_browsing/core/realtime:policy_engine",
- "//components/safe_browsing/core/realtime:url_lookup_service",
+ "//components/safe_browsing/core/realtime:url_lookup_service_base",
"//components/safe_browsing/core/web_ui:constants",
"//components/security_interstitials/core:unsafe_resource",
"//net:extras",
diff --git a/chromium/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc b/chromium/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc
index f0c2b40af94..f2148fa26e0 100644
--- a/chromium/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc
+++ b/chromium/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc
@@ -24,7 +24,7 @@ namespace {
const char kAPIScope[] = "https://www.googleapis.com/auth/chrome-safe-browsing";
-const int kTimeoutDelaySeconds = 5 * 60;
+const int kTimeoutDelaySeconds = 1;
} // namespace
diff --git a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
index 5d5875349f3..3c4861b3d9e 100644
--- a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
+++ b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
@@ -15,7 +15,7 @@
#include "components/safe_browsing/core/common/thread_utils.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/realtime/policy_engine.h"
-#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service_base.h"
#include "components/safe_browsing/core/web_ui/constants.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -102,8 +102,9 @@ SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
bool real_time_lookup_enabled,
- bool enhanced_protection_enabled,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_on_ui)
+ bool can_rt_check_subresource_url,
+ bool can_check_db,
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui)
: headers_(headers),
load_flags_(load_flags),
resource_type_(static_cast<ResourceType>(resource_type)),
@@ -112,9 +113,12 @@ SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
url_checker_delegate_(std::move(url_checker_delegate)),
database_manager_(url_checker_delegate_->GetDatabaseManager()),
real_time_lookup_enabled_(real_time_lookup_enabled),
- enhanced_protection_enabled_(enhanced_protection_enabled),
+ can_rt_check_subresource_url_(can_rt_check_subresource_url),
+ can_check_db_(can_check_db),
url_lookup_service_on_ui_(url_lookup_service_on_ui) {
DCHECK(!web_contents_getter_.is_null());
+ DCHECK(!can_rt_check_subresource_url_ || real_time_lookup_enabled_);
+ DCHECK(real_time_lookup_enabled_ || can_check_db_);
}
SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
@@ -127,7 +131,9 @@ SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
web_state_getter_(web_state_getter),
url_checker_delegate_(url_checker_delegate),
database_manager_(url_checker_delegate_->GetDatabaseManager()),
- real_time_lookup_enabled_(false) {
+ real_time_lookup_enabled_(false),
+ can_rt_check_subresource_url_(false),
+ can_check_db_(true) {
DCHECK(!web_state_getter_.is_null());
}
@@ -135,7 +141,9 @@ SafeBrowsingUrlCheckerImpl::~SafeBrowsingUrlCheckerImpl() {
DCHECK(CurrentlyOnThread(ThreadID::IO));
if (state_ == STATE_CHECKING_URL) {
- database_manager_->CancelCheck(this);
+ if (can_check_db_) {
+ database_manager_->CancelCheck(this);
+ }
const GURL& url = urls_[next_index_].url;
TRACE_EVENT_ASYNC_END1("safe_browsing", "CheckUrl", this, "url",
url.spec());
@@ -269,7 +277,9 @@ void SafeBrowsingUrlCheckerImpl::OnUrlResult(const GURL& url,
void SafeBrowsingUrlCheckerImpl::OnTimeout() {
RecordCheckUrlTimeout(/*timed_out=*/true);
- database_manager_->CancelCheck(this);
+ if (can_check_db_) {
+ database_manager_->CancelCheck(this);
+ }
// Any pending callbacks on this URL check should be skipped.
weak_factory_.InvalidateWeakPtrs();
@@ -362,7 +372,9 @@ void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
resource_type_);
safe_synchronously = false;
AsyncMatch match =
- database_manager_->CheckUrlForHighConfidenceAllowlist(url, this);
+ can_check_db_
+ ? database_manager_->CheckUrlForHighConfidenceAllowlist(url, this)
+ : AsyncMatch::NO_MATCH;
UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.LocalMatch.Result", match);
switch (match) {
case AsyncMatch::ASYNC:
@@ -381,8 +393,8 @@ void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
/*did_match_allowlist=*/true));
break;
case AsyncMatch::NO_MATCH:
- // No match found locally. Queue the call to
- // |OnCheckUrlForHighConfidenceAllowlist| to perform the full URL
+ // No match found locally or |can_check_db_| is false. Queue the call
+ // to |OnCheckUrlForHighConfidenceAllowlist| to perform the full URL
// lookup.
base::PostTask(
FROM_HERE, CreateTaskTraits(ThreadID::IO),
@@ -393,8 +405,13 @@ void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
break;
}
} else {
- safe_synchronously = database_manager_->CheckBrowseUrl(
- url, url_checker_delegate_->GetThreatTypes(), this);
+ // TODO(crbug.com/1085261): Add a metric to track how often
+ // |can_check_db_| is false.
+ safe_synchronously =
+ can_check_db_
+ ? database_manager_->CheckBrowseUrl(
+ url, url_checker_delegate_->GetThreatTypes(), this)
+ : true;
}
if (safe_synchronously) {
@@ -487,7 +504,7 @@ void SafeBrowsingUrlCheckerImpl::OnCheckUrlForHighConfidenceAllowlist(
bool is_expected_resource_type =
(ResourceType::kMainFrame == resource_type_) ||
((ResourceType::kSubFrame == resource_type_) &&
- enhanced_protection_enabled_);
+ can_rt_check_subresource_url_);
DCHECK(is_expected_resource_type);
const GURL& url = urls_[next_index_].url;
@@ -513,7 +530,7 @@ void SafeBrowsingUrlCheckerImpl::SetWebUIToken(int token) {
void SafeBrowsingUrlCheckerImpl::StartLookupOnUIThread(
base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
const GURL& url,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_on_ui,
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
DCHECK(CurrentlyOnThread(ThreadID::UI));
bool is_lookup_service_available =
@@ -540,7 +557,8 @@ void SafeBrowsingUrlCheckerImpl::StartLookupOnUIThread(
void SafeBrowsingUrlCheckerImpl::PerformHashBasedCheck(const GURL& url) {
DCHECK(CurrentlyOnThread(ThreadID::IO));
- if (database_manager_->CheckBrowseUrl(
+ if (!can_check_db_ ||
+ database_manager_->CheckBrowseUrl(
url, url_checker_delegate_->GetThreatTypes(), this)) {
// No match found in the local database. Safe to call |OnUrlResult| here
// directly.
diff --git a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
index 6a76e9c06bc..af0ab7be5a3 100644
--- a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
+++ b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
@@ -35,7 +35,7 @@ enum class ResourceType;
class UrlCheckerDelegate;
-class RealTimeUrlLookupService;
+class RealTimeUrlLookupServiceBase;
// A SafeBrowsingUrlCheckerImpl instance is used to perform SafeBrowsing check
// for a URL and its redirect URLs. It implements Mojo interface so that it can
@@ -72,10 +72,10 @@ class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
// indicates whether or not the profile has enabled real time URL lookups, as
// computed by the RealTimePolicyEngine. This must be computed in advance,
// since this class only exists on the IO thread.
- // TODO(crbug.com/1050859): Move |real_time_lookup_enabled|,
- // |cache_manager_on_ui| and |identity_manager_on_ui| into
- // url_lookup_service_on_ui, and reduce the number of parameters in this
- // constructor.
+ // |can_rt_check_subresource_url| indicates whether or not the profile has
+ // enabled real time URL lookups for subresource URLs.
+ // |real_time_lookup_enabled| must be true if |can_rt_check_subresource_url|
+ // is true.
SafeBrowsingUrlCheckerImpl(
const net::HttpRequestHeaders& headers,
int load_flags,
@@ -85,8 +85,9 @@ class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
const base::RepeatingCallback<content::WebContents*()>&
web_contents_getter,
bool real_time_lookup_enabled,
- bool enhanced_protection_enabled,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_on_ui);
+ bool can_rt_check_subresource_url,
+ bool can_check_db,
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui);
// Constructor that takes only a ResourceType and a UrlCheckerDelegate,
// omitting other arguments that never have non-default values on iOS.
@@ -178,7 +179,7 @@ class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
static void StartLookupOnUIThread(
base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
const GURL& url,
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_on_ui,
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
scoped_refptr<SafeBrowsingDatabaseManager> database_manager);
// Called when the |request| from the real-time lookup service is sent.
@@ -255,12 +256,17 @@ class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
// Whether real time lookup is enabled for this request.
bool real_time_lookup_enabled_;
- // Whether enhanced protection is enabled for this profile.
- bool enhanced_protection_enabled_;
+ // Whether non mainframe url can be checked for this profile.
+ bool can_rt_check_subresource_url_;
+
+ // Whether safe browsing database can be checked. It is set to false when
+ // enterprise real time URL lookup is enabled and safe browsing is disabled
+ // for this profile.
+ bool can_check_db_;
// This object is used to perform real time url check. Can only be accessed in
// UI thread.
- base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_on_ui_;
+ base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui_;
base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_{this};
diff --git a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
index 7b01f2d8d18..92e1fe44b0c 100644
--- a/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
@@ -163,7 +163,8 @@ class MockRealTimeUrlLookupService : public RealTimeUrlLookupService {
/*pref_service=*/nullptr,
ChromeUserPopulation::NOT_MANAGED,
/*is_under_advanced_protection=*/false,
- /*is_off_the_record=*/false) {}
+ /*is_off_the_record=*/false,
+ /*variations_service=*/nullptr) {}
// Returns the threat type previously set by |SetThreatTypeForUrl|. It crashes
// if the threat type for the |gurl| is not set in advance.
void StartLookup(const GURL& gurl,
@@ -221,7 +222,8 @@ class SafeBrowsingUrlCheckerTest : public PlatformTest {
}
std::unique_ptr<SafeBrowsingUrlCheckerImpl> CreateSafeBrowsingUrlChecker(
- bool real_time_lookup_enabled) {
+ bool real_time_lookup_enabled,
+ bool can_check_safe_browsing_db) {
base::MockCallback<base::RepeatingCallback<content::WebContents*()>>
mock_web_contents_getter;
return std::make_unique<SafeBrowsingUrlCheckerImpl>(
@@ -229,7 +231,7 @@ class SafeBrowsingUrlCheckerTest : public PlatformTest {
blink::mojom::ResourceType::kMainFrame,
/*has_user_gesture=*/false, url_checker_delegate_,
mock_web_contents_getter.Get(), real_time_lookup_enabled,
- /*enhanced_protection_enabled=*/false,
+ /*can_rt_check_subresource_url=*/false, can_check_safe_browsing_db,
real_time_lookup_enabled ? url_lookup_service_->GetWeakPtr() : nullptr);
}
@@ -241,8 +243,8 @@ class SafeBrowsingUrlCheckerTest : public PlatformTest {
};
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_SafeUrl) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/false);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
@@ -260,8 +262,8 @@ TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_SafeUrl) {
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_DangerousUrl) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/false);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
@@ -279,8 +281,8 @@ TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_DangerousUrl) {
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RedirectUrlsSafe) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/false);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL origin_url("https://example.test/");
database_manager_->SetThreatTypeForUrl(origin_url, SB_THREAT_TYPE_SAFE,
@@ -311,8 +313,8 @@ TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RedirectUrlsSafe) {
TEST_F(SafeBrowsingUrlCheckerTest,
CheckUrl_RedirectUrlsOriginDangerousRedirectSafe) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/false);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL origin_url("https://example.test/");
database_manager_->SetThreatTypeForUrl(
@@ -347,8 +349,8 @@ TEST_F(SafeBrowsingUrlCheckerTest,
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledAllowlistMatch) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/true);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, AsyncMatch::MATCH);
@@ -371,8 +373,8 @@ TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledAllowlistMatch) {
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledSafeUrl) {
- auto safe_browsing_url_checker =
- CreateSafeBrowsingUrlChecker(/*real_time_lookup_enabled=*/true);
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, AsyncMatch::NO_MATCH);
@@ -390,4 +392,23 @@ TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledSafeUrl) {
task_environment_->RunUntilIdle();
}
+TEST_F(SafeBrowsingUrlCheckerTest,
+ CheckUrl_RealTimeEnabledSafeBrowsingDisabled) {
+ auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
+ /*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
+
+ GURL url("https://example.test/");
+ url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING);
+
+ base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
+ callback;
+ // Should still show blocking page because real time lookup is enabled.
+ EXPECT_CALL(*url_checker_delegate_,
+ StartDisplayingBlockingPageHelper(_, _, _, _, _))
+ .Times(1);
+ safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
+
+ task_environment_->RunUntilIdle();
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
index 2b86cf129b1..c66170bc2e6 100644
--- a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
+++ b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -19,16 +19,6 @@
namespace {
-// The Extended Reporting pref that is currently active, used for UMA metrics.
-// These values are written to logs. New enum values can be added, but
-// existing enums must never be renumbered or deleted and reused.
-enum ActiveExtendedReportingPref {
- SBER1_PREF = 0,
- SBER2_PREF = 1,
- // New prefs must be added before MAX_SBER_PREF
- MAX_SBER_PREF
-};
-
// Update the correct UMA metric based on which pref was changed and which UI
// the change was made on.
void RecordExtendedReportingPrefChanged(
@@ -249,7 +239,7 @@ void SetExtendedReportingPrefAndMetric(
RecordExtendedReportingPrefChanged(*prefs, location);
}
-void SetExtendedReportingPref(PrefService* prefs, bool value) {
+void SetExtendedReportingPrefForTests(PrefService* prefs, bool value) {
prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
}
@@ -260,67 +250,6 @@ void SetEnhancedProtectionPref(PrefService* prefs, bool value) {
prefs->SetBoolean(prefs::kSafeBrowsingEnhanced, value);
}
-void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
- bool on_show_pref_existed,
- bool on_show_pref_value) {
- const bool cur_pref_value = IsExtendedReportingEnabled(prefs);
-
- if (!on_show_pref_existed) {
- if (!ExtendedReportingPrefExists(prefs)) {
- // User seeing pref for the first time, didn't touch the checkbox (left it
- // unchecked).
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.Decision.First_LeftUnchecked", SBER2_PREF,
- MAX_SBER_PREF);
- return;
- }
-
- // Pref currently exists so user did something to the checkbox
- if (cur_pref_value) {
- // User turned the pref on.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.Decision.First_Enabled", SBER2_PREF,
- MAX_SBER_PREF);
- return;
- }
-
- // Otherwise, user turned the pref off, but because it didn't exist when
- // the interstitial was first shown, they must have turned it on and then
- // off before the interstitial was closed.
- UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.First_Disabled",
- SBER2_PREF, MAX_SBER_PREF);
- return;
- }
-
- // At this point, the pref existed when the interstitial was shown so this is
- // a repeat appearance of the opt-in. Existence can't be removed during an
- // interstitial so no need to check whether the pref currently exists.
- if (on_show_pref_value && cur_pref_value) {
- // User left the pref on.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftEnabled", SBER2_PREF,
- MAX_SBER_PREF);
- return;
- } else if (on_show_pref_value && !cur_pref_value) {
- // User turned the pref off.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.Decision.Repeat_Disabled", SBER2_PREF,
- MAX_SBER_PREF);
- return;
- } else if (!on_show_pref_value && cur_pref_value) {
- // User turned the pref on.
- UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.Repeat_Enabled",
- SBER2_PREF, MAX_SBER_PREF);
- return;
- } else {
- // Both on_show and cur values are false - user left the pref off.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftDisabled", SBER2_PREF,
- MAX_SBER_PREF);
- return;
- }
-}
-
void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
// Remember that this user saw an interstitial.
prefs->SetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting, true);
diff --git a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
index 656ee9bc2ec..f0dac127bc6 100644
--- a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
+++ b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
@@ -171,7 +171,6 @@ enum PasswordProtectionTrigger {
// Password protection is off.
PASSWORD_PROTECTION_OFF = 0,
// Password protection triggered by password reuse event.
- // Not used for now.
PASSWORD_REUSE = 1,
// Password protection triggered by password reuse event on phishing page.
PHISHING_REUSE = 2,
@@ -294,7 +293,7 @@ void SetExtendedReportingPrefAndMetric(PrefService* prefs,
ExtendedReportingOptInLocation location);
// This variant is used to simplify test code by omitting the location.
-void SetExtendedReportingPref(PrefService* prefs, bool value);
+void SetExtendedReportingPrefForTests(PrefService* prefs, bool value);
// Sets the currently active Safe Browsing Enhanced Protection to the specified
// value.
diff --git a/chromium/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc b/chromium/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc
index 59ee1f19b28..924977760c2 100644
--- a/chromium/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc
+++ b/chromium/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc
@@ -127,18 +127,14 @@ TEST_F(SafeBrowsingPrefsTest, EnhancedProtection) {
EXPECT_FALSE(IsEnhancedProtectionEnabled(prefs_));
SetEnhancedProtectionPref(&prefs_, true);
- // If experiment is not on, the pref is not turned on.
- EXPECT_FALSE(IsEnhancedProtectionEnabled(prefs_));
{
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- safe_browsing::kEnhancedProtection);
+ scoped_feature_list.InitAndEnableFeature(kEnhancedProtection);
EXPECT_TRUE(IsEnhancedProtectionEnabled(prefs_));
}
{
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- safe_browsing::kEnhancedProtection);
+ scoped_feature_list.InitAndEnableFeature(kEnhancedProtection);
prefs_.SetBoolean(prefs::kSafeBrowsingEnabled, false);
EXPECT_FALSE(IsEnhancedProtectionEnabled(prefs_));
}
diff --git a/chromium/components/safe_browsing/core/db/v4_local_database_manager.cc b/chromium/components/safe_browsing/core/db/v4_local_database_manager.cc
index 4fbf277960b..94b469c5a2f 100644
--- a/chromium/components/safe_browsing/core/db/v4_local_database_manager.cc
+++ b/chromium/components/safe_browsing/core/db/v4_local_database_manager.cc
@@ -652,8 +652,7 @@ void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
continue;
}
task_runner_->PostTask(
- FROM_HERE, base::BindOnce(base::IgnoreResult(&base::DeleteFile),
- store_path, false /* recursive */));
+ FROM_HERE, base::BindOnce(base::GetDeleteFileCallback(), store_path));
} else {
NOTREACHED() << "Trying to delete a store file that's in use: "
<< store_filename_to_delete;
diff --git a/chromium/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc b/chromium/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc
index 7b6cbb1b076..b0073821500 100644
--- a/chromium/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc
+++ b/chromium/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "base/base64.h"
+#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
diff --git a/chromium/components/safe_browsing/core/db/v4_store.cc b/chromium/components/safe_browsing/core/db/v4_store.cc
index 2d12ab5a29c..43a59cfdc80 100644
--- a/chromium/components/safe_browsing/core/db/v4_store.cc
+++ b/chromium/components/safe_browsing/core/db/v4_store.cc
@@ -10,6 +10,7 @@
#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_util.h"
+#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
@@ -64,43 +65,17 @@ void RecordEnumWithAndWithoutSuffix(const std::string& metric,
int32_t value,
int32_t maximum,
const base::FilePath& file_path) {
- // The histograms below are an expansion of the UMA_HISTOGRAM_ENUMERATION
- // macro adapted to allow for a dynamically suffixed histogram name.
- // Note: The factory creates and owns the histogram.
- base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
- metric + kResult, 1, maximum, maximum + 1,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- if (histogram) {
- histogram->Add(value);
- }
-
+ base::UmaHistogramExactLinear(metric + kResult, value, maximum);
std::string suffix = GetUmaSuffixForStore(file_path);
- base::HistogramBase* histogram_suffix = base::LinearHistogram::FactoryGet(
- metric + kResult + suffix, 1, maximum, maximum + 1,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- if (histogram_suffix) {
- histogram_suffix->Add(value);
- }
+ base::UmaHistogramExactLinear(metric + kResult + suffix, value, maximum);
}
void RecordBooleanWithAndWithoutSuffix(const std::string& metric,
bool value,
const base::FilePath& file_path) {
- // The histograms below are an expansion of the UMA_HISTOGRAM_BOOLEAN
- // macro adapted to allow for a dynamically suffixed histogram name.
- // Note: The factory creates and owns the histogram.
- base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet(
- metric, base::HistogramBase::kUmaTargetedHistogramFlag);
- if (histogram) {
- histogram->Add(value);
- }
-
+ base::UmaHistogramBoolean(metric, value);
std::string suffix = GetUmaSuffixForStore(file_path);
- base::HistogramBase* histogram_suffix = base::BooleanHistogram::FactoryGet(
- metric + suffix, base::HistogramBase::kUmaTargetedHistogramFlag);
- if (histogram_suffix) {
- histogram_suffix->Add(value);
- }
+ base::UmaHistogramBoolean(metric + suffix, value);
}
void RecordCountWithAndWithoutSuffix(const std::string& metric,
@@ -841,14 +816,8 @@ bool V4Store::VerifyChecksum() {
int64_t V4Store::RecordAndReturnFileSize(const std::string& base_metric) {
std::string suffix = GetUmaSuffixForStore(store_path_);
- // Histogram properties as in UMA_HISTOGRAM_COUNTS_1M macro.
- base::HistogramBase* histogram = base::Histogram::FactoryGet(
- base_metric + suffix, 1, 1000000, 50,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- if (histogram) {
- const int64_t file_size_kilobytes = file_size_ / 1024;
- histogram->Add(file_size_kilobytes);
- }
+ const int64_t file_size_kilobytes = file_size_ / 1024;
+ base::UmaHistogramCounts1M(base_metric + suffix, file_size_kilobytes);
return file_size_;
}
diff --git a/chromium/components/safe_browsing/core/db/v4_store_fuzzer.cc b/chromium/components/safe_browsing/core/db/v4_store_fuzzer.cc
index 01ceb181c5e..fd7acd7a092 100644
--- a/chromium/components/safe_browsing/core/db/v4_store_fuzzer.cc
+++ b/chromium/components/safe_browsing/core/db/v4_store_fuzzer.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/files/file_path.h"
+#include "base/logging.h"
#include "base/test/test_simple_task_runner.h"
#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/db/v4_store.h"
diff --git a/chromium/components/safe_browsing/core/db/v4_store_unittest.cc b/chromium/components/safe_browsing/core/db/v4_store_unittest.cc
index 372a2f08eb2..86ad27613de 100644
--- a/chromium/components/safe_browsing/core/db/v4_store_unittest.cc
+++ b/chromium/components/safe_browsing/core/db/v4_store_unittest.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
#include "base/run_loop.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.h"
diff --git a/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.cc b/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.cc
index c36864b66c6..4eeb85f7ea4 100644
--- a/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.cc
+++ b/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.cc
@@ -213,6 +213,7 @@ void V4UpdateProtocolManager::ScheduleNextUpdateAfterInterval(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(interval >= base::TimeDelta());
+ next_update_time_ = Time::Now() + interval;
// Unschedule any current timer.
update_timer_.Stop();
update_timer_.Start(FROM_HERE, interval, this,
@@ -441,11 +442,11 @@ void V4UpdateProtocolManager::CollectUpdateInfo(
if (last_response_time_.ToJavaTime()) {
update_info->set_last_update_time_millis(last_response_time_.ToJavaTime());
+ }
- // We should only find the next update if the last_response is valid.
- base::Time next_update = last_response_time_ + next_update_interval_;
- if (next_update.ToJavaTime())
- update_info->set_next_update_time_millis(next_update.ToJavaTime());
+ if (next_update_time_) {
+ update_info->set_next_update_time_millis(
+ next_update_time_.value().ToJavaTime());
}
}
diff --git a/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.h b/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.h
index 068b245469e..79c39d0996e 100644
--- a/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.h
+++ b/chromium/components/safe_browsing/core/db/v4_update_protocol_manager.h
@@ -18,6 +18,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -178,6 +179,10 @@ class V4UpdateProtocolManager {
// The server can set it by setting the minimum_wait_duration.
base::TimeDelta next_update_interval_;
+ // The time when the next update is scheduled to be requested. This is valid
+ // only when |update_timer_| is running.
+ base::Optional<base::Time> next_update_time_ = base::nullopt;
+
// The config of the client making Pver4 requests.
const V4ProtocolConfig config_;
diff --git a/chromium/components/safe_browsing/core/features.cc b/chromium/components/safe_browsing/core/features.cc
index 45ff1fd627a..0417501fcfa 100644
--- a/chromium/components/safe_browsing/core/features.cc
+++ b/chromium/components/safe_browsing/core/features.cc
@@ -41,38 +41,42 @@ const base::Feature kDelayedWarnings{"SafeBrowsingDelayedWarnings",
const base::Feature kDownloadRequestWithToken{
"SafeBrowsingDownloadRequestWithToken", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kEnhancedProtection{"SafeBrowsingEnhancedProtection",
- base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnhancedProtection {
+ "SafeBrowsingEnhancedProtection",
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+ base::FEATURE_ENABLED_BY_DEFAULT
+#else
+ base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
const base::Feature kMalwareScanEnabled{"SafeBrowsingMalwareScanEnabled",
base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kPasswordProtectionForSavedPasswords{
"SafeBrowsingPasswordProtectionForSavedPasswords",
- base::FEATURE_ENABLED_BY_DEFAULT};
+ base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kPasswordProtectionShowDomainsForSavedPasswords{
"SafeBrowsingPasswordProtectionShowDomainsForSavedPasswords",
- base::FEATURE_ENABLED_BY_DEFAULT};
+ base::FEATURE_DISABLED_BY_DEFAULT};
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-const base::Feature kPasswordProtectionForSignedInUsers{
- "SafeBrowsingPasswordProtectionForSignedInUsers",
- base::FEATURE_ENABLED_BY_DEFAULT};
-#else
const base::Feature kPasswordProtectionForSignedInUsers{
"SafeBrowsingPasswordProtectionForSignedInUsers",
base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
const base::Feature kPromptAppForDeepScanning{
"SafeBrowsingPromptAppForDeepScanning", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kRealTimeUrlLookupEnabled{
- "SafeBrowsingRealTimeUrlLookupEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+ "SafeBrowsingRealTimeUrlLookupEnabled", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kRealTimeUrlLookupEnabledForAllAndroidDevices{
"SafeBrowsingRealTimeUrlLookupEnabledForAllAndroidDevices",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kRealTimeUrlLookupEnabledForEnterprise{
+ "SafeBrowsingRealTimeUrlLookupEnabledForEnterprise",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kRealTimeUrlLookupEnabledForEP{
@@ -137,6 +141,7 @@ constexpr struct {
{&kRealTimeUrlLookupEnabled, true},
{&kRealTimeUrlLookupEnabledForAllAndroidDevices, true},
{&kRealTimeUrlLookupEnabledForEP, true},
+ {&kRealTimeUrlLookupEnabledForEnterprise, true},
{&kRealTimeUrlLookupEnabledForEPWithToken, true},
{&kRealTimeUrlLookupEnabledWithToken, true},
{&kRealTimeUrlLookupNonMainframeEnabledForEP, true},
diff --git a/chromium/components/safe_browsing/core/features.h b/chromium/components/safe_browsing/core/features.h
index 805ab14cc6d..25c49599c11 100644
--- a/chromium/components/safe_browsing/core/features.h
+++ b/chromium/components/safe_browsing/core/features.h
@@ -84,6 +84,12 @@ extern const base::Feature kRealTimeUrlLookupEnabled;
// This flag is in effect only if |kRealTimeUrlLookupEnabled| is true.
extern const base::Feature kRealTimeUrlLookupEnabledForAllAndroidDevices;
+// Controls whether to do real time URL lookup for enterprise users. If both
+// this feature and the enterprise policies are enabled, the enterprise real
+// time URL lookup will be enabled and the consumer real time URL lookup will be
+// disabled.
+extern const base::Feature kRealTimeUrlLookupEnabledForEnterprise;
+
// Controls whether the real time URL lookup is enabled for Enhanced Protection
// users.
extern const base::Feature kRealTimeUrlLookupEnabledForEP;
diff --git a/chromium/components/safe_browsing/core/proto/client_model.proto b/chromium/components/safe_browsing/core/proto/client_model.proto
index ab507222548..6f6d975cba2 100644
--- a/chromium/components/safe_browsing/core/proto/client_model.proto
+++ b/chromium/components/safe_browsing/core/proto/client_model.proto
@@ -18,7 +18,7 @@ option optimize_for = LITE_RUNTIME;
package safe_browsing;
-import 'csd.proto';
+import "components/safe_browsing/core/proto/csd.proto";
// This protocol buffer represents a machine learning model that is used in
// client-side phishing detection (in Chrome). The client extracts a set
diff --git a/chromium/components/safe_browsing/core/proto/realtimeapi.proto b/chromium/components/safe_browsing/core/proto/realtimeapi.proto
index 99661be049d..32ca4e2cebd 100644
--- a/chromium/components/safe_browsing/core/proto/realtimeapi.proto
+++ b/chromium/components/safe_browsing/core/proto/realtimeapi.proto
@@ -11,7 +11,7 @@ option optimize_for = LITE_RUNTIME;
package safe_browsing;
-import "csd.proto";
+import "components/safe_browsing/core/proto/csd.proto";
message RTLookupRequest {
// If applicable, URL submitted from Chrome.
diff --git a/chromium/components/safe_browsing/core/proto/webprotect.proto b/chromium/components/safe_browsing/core/proto/webprotect.proto
index 7c1e4280f08..283f6ba07e6 100644
--- a/chromium/components/safe_browsing/core/proto/webprotect.proto
+++ b/chromium/components/safe_browsing/core/proto/webprotect.proto
@@ -69,6 +69,9 @@ message DeepScanningClientRequest {
message MalwareDeepScanningVerdict {
reserved 2;
+ // These values are persisted to UMA logs in the
+ // SafeBrowsingMalwareDeepScanningVerdict enum. Entries should not be
+ // renumbered and numeric values should never be reused.
enum Verdict {
VERDICT_UNSPECIFIED = 0;
CLEAN = 1;
diff --git a/chromium/components/safe_browsing/core/realtime/BUILD.gn b/chromium/components/safe_browsing/core/realtime/BUILD.gn
index cf3a9933cb4..f1aeb3530f9 100644
--- a/chromium/components/safe_browsing/core/realtime/BUILD.gn
+++ b/chromium/components/safe_browsing/core/realtime/BUILD.gn
@@ -2,6 +2,27 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+static_library("policy_engine") {
+ sources = [
+ "policy_engine.cc",
+ "policy_engine.h",
+ ]
+
+ deps = [
+ "//base:base",
+ "//components/prefs",
+ "//components/safe_browsing/core:features",
+ "//components/safe_browsing/core:realtimeapi_proto",
+ "//components/safe_browsing/core/common",
+ "//components/safe_browsing/core/common:safe_browsing_prefs",
+ "//components/signin/public/identity_manager",
+ "//components/sync",
+ "//components/unified_consent",
+ "//components/user_prefs",
+ "//components/variations/service",
+ ]
+}
+
static_library("url_lookup_service") {
sources = [
"url_lookup_service.cc",
@@ -10,6 +31,7 @@ static_library("url_lookup_service") {
deps = [
":policy_engine",
+ ":url_lookup_service_base",
"//base:base",
"//components/prefs",
"//components/safe_browsing:buildflags",
@@ -22,28 +44,30 @@ static_library("url_lookup_service") {
"//components/safe_browsing/core/db:v4_protocol_manager_util",
"//components/signin/public/identity_manager",
"//components/sync",
+ "//components/variations/service:service",
"//services/network/public/cpp:cpp",
"//url:url",
]
}
-static_library("policy_engine") {
+static_library("url_lookup_service_base") {
sources = [
- "policy_engine.cc",
- "policy_engine.h",
+ "url_lookup_service_base.cc",
+ "url_lookup_service_base.h",
]
deps = [
"//base:base",
"//components/prefs",
- "//components/safe_browsing/core:features",
+ "//components/safe_browsing:buildflags",
"//components/safe_browsing/core:realtimeapi_proto",
- "//components/safe_browsing/core/common",
+ "//components/safe_browsing/core:verdict_cache_manager",
"//components/safe_browsing/core/common:safe_browsing_prefs",
+ "//components/safe_browsing/core/common:thread_utils",
+ "//components/safe_browsing/core/db:v4_protocol_manager_util",
"//components/signin/public/identity_manager",
- "//components/sync",
- "//components/unified_consent",
- "//components/user_prefs",
+ "//services/network/public/cpp:cpp",
+ "//url:url",
]
}
@@ -69,8 +93,10 @@ source_set("unit_tests") {
"//components/sync_preferences:test_support",
"//components/unified_consent",
"//components/user_prefs",
+ "//components/variations/service:service",
"//services/network:test_support",
"//services/network/public/cpp:cpp",
"//testing/gtest",
+ "//third_party/googletest:gmock",
]
}
diff --git a/chromium/components/safe_browsing/core/realtime/policy_engine.cc b/chromium/components/safe_browsing/core/realtime/policy_engine.cc
index 4493293cac0..1b5e90cc625 100644
--- a/chromium/components/safe_browsing/core/realtime/policy_engine.cc
+++ b/chromium/components/safe_browsing/core/realtime/policy_engine.cc
@@ -6,6 +6,7 @@
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -20,6 +21,7 @@
#include "components/sync/driver/sync_user_settings.h"
#include "components/unified_consent/pref_names.h"
#include "components/user_prefs/user_prefs.h"
+#include "components/variations/service/variations_service.h"
#if defined(OS_ANDROID)
#include "base/metrics/field_trial_params.h"
@@ -28,11 +30,27 @@
namespace safe_browsing {
+namespace {
+
+// This is a list of countries where real-time checks need to be disabled.
+static const std::vector<std::string> GetExcludedCountries() {
+ // The endpoint isn't available
+ return {"cn"};
+}
+
+} // namespace
+
#if defined(OS_ANDROID)
const int kDefaultMemoryThresholdMb = 4096;
#endif
// static
+bool RealTimePolicyEngine::IsInExcludedCountry(
+ const std::string& country_code) {
+ return base::Contains(GetExcludedCountries(), country_code);
+}
+
+// static
bool RealTimePolicyEngine::IsUrlLookupEnabled() {
if (!base::FeatureList::IsEnabled(kRealTimeUrlLookupEnabled))
return false;
@@ -85,11 +103,18 @@ bool RealTimePolicyEngine::IsPrimaryAccountSignedIn(
}
// static
-bool RealTimePolicyEngine::CanPerformFullURLLookup(PrefService* pref_service,
- bool is_off_the_record) {
+bool RealTimePolicyEngine::CanPerformFullURLLookup(
+ PrefService* pref_service,
+ bool is_off_the_record,
+ variations::VariationsService* variations_service) {
if (is_off_the_record)
return false;
+ // |variations_service| can be nullptr in tests.
+ if (variations_service &&
+ IsInExcludedCountry(variations_service->GetStoredPermanentCountry()))
+ return false;
+
if (IsUrlLookupEnabledForEp() && IsUserEpOptedIn(pref_service))
return true;
@@ -101,8 +126,10 @@ bool RealTimePolicyEngine::CanPerformFullURLLookupWithToken(
PrefService* pref_service,
bool is_off_the_record,
syncer::SyncService* sync_service,
- signin::IdentityManager* identity_manager) {
- if (!CanPerformFullURLLookup(pref_service, is_off_the_record)) {
+ signin::IdentityManager* identity_manager,
+ variations::VariationsService* variations_service) {
+ if (!CanPerformFullURLLookup(pref_service, is_off_the_record,
+ variations_service)) {
return false;
}
@@ -131,15 +158,36 @@ bool RealTimePolicyEngine::CanPerformFullURLLookupWithToken(
}
// static
+bool RealTimePolicyEngine::CanPerformEnterpriseFullURLLookup(
+ bool has_valid_dm_token,
+ bool is_off_the_record) {
+ if (is_off_the_record) {
+ return false;
+ }
+
+ if (!base::FeatureList::IsEnabled(kRealTimeUrlLookupEnabledForEnterprise)) {
+ return false;
+ }
+
+ if (!has_valid_dm_token) {
+ return false;
+ }
+
+ // TODO(crbug.com/1085261): Check the enterprise real time URL check policy.
+ return false;
+}
+
+// static
bool RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
ResourceType resource_type,
- bool enhanced_protection_enabled) {
+ bool can_rt_check_subresource_url) {
UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Requested",
resource_type);
if (resource_type == ResourceType::kMainFrame) {
return true;
}
- if (resource_type == ResourceType::kSubFrame && enhanced_protection_enabled &&
+ if (resource_type == ResourceType::kSubFrame &&
+ can_rt_check_subresource_url &&
base::FeatureList::IsEnabled(
kRealTimeUrlLookupNonMainframeEnabledForEP)) {
return true;
diff --git a/chromium/components/safe_browsing/core/realtime/policy_engine.h b/chromium/components/safe_browsing/core/realtime/policy_engine.h
index 93e2ce208a2..f6150432f01 100644
--- a/chromium/components/safe_browsing/core/realtime/policy_engine.h
+++ b/chromium/components/safe_browsing/core/realtime/policy_engine.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_SAFE_BROWSING_CORE_REALTIME_POLICY_ENGINE_H_
#define COMPONENTS_SAFE_BROWSING_CORE_REALTIME_POLICY_ENGINE_H_
+#include <string>
+
#include "build/build_config.h"
class PrefService;
@@ -17,6 +19,10 @@ namespace signin {
class IdentityManager;
}
+namespace variations {
+class VariationsService;
+}
+
namespace safe_browsing {
enum class ResourceType;
@@ -39,16 +45,20 @@ class RealTimePolicyEngine {
RealTimePolicyEngine() = delete;
~RealTimePolicyEngine() = delete;
- // Return true if full URL lookups are enabled for |resource_type|.
+ // Return true if full URL lookups are enabled for |resource_type|. If
+ // |can_rt_check_subresource_url| is set to false, return true only if
+ // |resource_type| is |kMainFrame|.
static bool CanPerformFullURLLookupForResourceType(
ResourceType resource_type,
- bool enhanced_protection_enabled);
+ bool can_rt_check_subresource_url);
// Return true if the feature to enable full URL lookups is enabled and the
// allowlist fetch is enabled for the profile represented by
// |pref_service|.
- static bool CanPerformFullURLLookup(PrefService* pref_service,
- bool is_off_the_record);
+ static bool CanPerformFullURLLookup(
+ PrefService* pref_service,
+ bool is_off_the_record,
+ variations::VariationsService* variations_service);
// Return true if the OAuth token should be associated with the URL lookup
// pings.
@@ -56,12 +66,18 @@ class RealTimePolicyEngine {
PrefService* pref_service,
bool is_off_the_record,
syncer::SyncService* sync_service,
- signin::IdentityManager* identity_manager);
+ signin::IdentityManager* identity_manager,
+ variations::VariationsService* variations_service);
+
+ static bool CanPerformEnterpriseFullURLLookup(bool has_valid_dm_token,
+ bool is_off_the_record);
friend class SafeBrowsingService;
friend class SafeBrowsingUIHandler;
private:
+ static bool IsInExcludedCountry(const std::string& country_code);
+
// Is the feature to perform real-time URL lookup enabled?
static bool IsUrlLookupEnabled();
diff --git a/chromium/components/safe_browsing/core/realtime/policy_engine_unittest.cc b/chromium/components/safe_browsing/core/realtime/policy_engine_unittest.cc
index 668cff6d0b7..17bd531707b 100644
--- a/chromium/components/safe_browsing/core/realtime/policy_engine_unittest.cc
+++ b/chromium/components/safe_browsing/core/realtime/policy_engine_unittest.cc
@@ -40,8 +40,8 @@ class RealTimePolicyEngineTest : public PlatformTest {
}
bool CanPerformFullURLLookup(bool is_off_the_record) {
- return RealTimePolicyEngine::CanPerformFullURLLookup(&pref_service_,
- is_off_the_record);
+ return RealTimePolicyEngine::CanPerformFullURLLookup(
+ &pref_service_, is_off_the_record, /*variations_service=*/nullptr);
}
bool CanPerformFullURLLookupWithToken(
@@ -49,7 +49,18 @@ class RealTimePolicyEngineTest : public PlatformTest {
syncer::SyncService* sync_service,
signin::IdentityManager* identity_manager) {
return RealTimePolicyEngine::CanPerformFullURLLookupWithToken(
- &pref_service_, is_off_the_record, sync_service, identity_manager);
+ &pref_service_, is_off_the_record, sync_service, identity_manager,
+ /*variations_service=*/nullptr);
+ }
+
+ bool CanPerformEnterpriseFullURLLookup(bool has_valid_dm_token,
+ bool is_off_the_record) {
+ return RealTimePolicyEngine::CanPerformEnterpriseFullURLLookup(
+ has_valid_dm_token, is_off_the_record);
+ }
+
+ bool IsInExcludedCountry(const std::string& country_code) {
+ return RealTimePolicyEngine::IsInExcludedCountry(country_code);
}
std::unique_ptr<base::test::TaskEnvironment> task_environment_;
@@ -84,7 +95,8 @@ TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_SmallMemorySize) {
{{kRealTimeUrlLookupMemoryThresholdMb,
base::NumberToString(
memory_size_threshold)}}}},
- /* disabled_features */ {});
+ /* disabled_features */ {
+ {kRealTimeUrlLookupEnabledForAllAndroidDevices, {}}});
pref_service_.SetUserPref(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
std::make_unique<base::Value>(true));
@@ -163,11 +175,17 @@ TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_EnabledUserOptin) {
TEST_F(RealTimePolicyEngineTest,
TestCanPerformFullURLLookup_EnhancedProtection) {
- base::test::ScopedFeatureList feature_list;
pref_service_.SetBoolean(prefs::kSafeBrowsingEnhanced, true);
- ASSERT_FALSE(CanPerformFullURLLookup(/* is_off_the_record */ false));
- feature_list.InitAndEnableFeature(kEnhancedProtection);
- ASSERT_TRUE(CanPerformFullURLLookup(/* is_off_the_record */ false));
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kEnhancedProtection);
+ ASSERT_FALSE(CanPerformFullURLLookup(/* is_off_the_record */ false));
+ }
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kEnhancedProtection);
+ ASSERT_TRUE(CanPerformFullURLLookup(/* is_off_the_record */ false));
+ }
}
TEST_F(RealTimePolicyEngineTest,
@@ -323,12 +341,37 @@ TEST_F(RealTimePolicyEngineTest,
/*is_off_the_record=*/false, &sync_service, identity_manager));
}
-TEST_F(RealTimePolicyEngineTest,
- TestCanPerformFullURLLookup_EnabledMainFrameOnlyForNonEpUser) {
+TEST_F(RealTimePolicyEngineTest, TestCanPerformEnterpriseFullURLLookup) {
+ // Is off the record profile.
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kRealTimeUrlLookupEnabledForEnterprise);
+ EXPECT_FALSE(CanPerformEnterpriseFullURLLookup(/*has_valid_dm_token=*/true,
+ /*is_off_the_record=*/true));
+ }
+ // Feature flag disabled.
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kRealTimeUrlLookupEnabledForEnterprise);
+ EXPECT_FALSE(CanPerformEnterpriseFullURLLookup(
+ /*has_valid_dm_token=*/true, /*is_off_the_record=*/false));
+ }
+ // No valid DM token.
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kRealTimeUrlLookupEnabledForEnterprise);
+ EXPECT_FALSE(CanPerformEnterpriseFullURLLookup(
+ /*has_valid_dm_token=*/false, /*is_off_the_record=*/false));
+ }
+}
+
+TEST_F(
+ RealTimePolicyEngineTest,
+ TestCanPerformFullURLLookup_EnabledMainFrameOnlyForSubresourceDisabledUser) {
for (int i = 0; i <= static_cast<int>(ResourceType::kMaxValue); i++) {
ResourceType resource_type = static_cast<ResourceType>(i);
bool enabled = RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
- resource_type, /*enhanced_protection_enabled=*/false);
+ resource_type, /*can_rt_check_subresource_url=*/false);
switch (resource_type) {
case ResourceType::kMainFrame:
EXPECT_TRUE(enabled);
@@ -340,12 +383,13 @@ TEST_F(RealTimePolicyEngineTest,
}
}
-TEST_F(RealTimePolicyEngineTest,
- TestCanPerformFullURLLookup_EnabledNonMainFrameForEpUser) {
+TEST_F(
+ RealTimePolicyEngineTest,
+ TestCanPerformFullURLLookup_EnabledNonMainFrameForSubresourceEnabledUser) {
for (int i = 0; i <= static_cast<int>(ResourceType::kMaxValue); i++) {
ResourceType resource_type = static_cast<ResourceType>(i);
bool enabled = RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
- resource_type, /*enhanced_protection_enabled=*/true);
+ resource_type, /*can_rt_check_subresource_url=*/true);
switch (resource_type) {
case ResourceType::kMainFrame:
case ResourceType::kSubFrame:
@@ -358,4 +402,18 @@ TEST_F(RealTimePolicyEngineTest,
}
}
+TEST_F(RealTimePolicyEngineTest, TestIsInExcludedCountry) {
+ const std::string non_excluded_countries[] = {"be", "br", "ca", "de", "es",
+ "fr", "ie", "in", "jp", "nl",
+ "ru", "se", "us"};
+ for (auto country : non_excluded_countries) {
+ EXPECT_FALSE(IsInExcludedCountry(country));
+ }
+
+ const std::string excluded_countries[] = {"cn"};
+ for (auto country : excluded_countries) {
+ EXPECT_TRUE(IsInExcludedCountry(country));
+ }
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/realtime/url_lookup_service.cc b/chromium/components/safe_browsing/core/realtime/url_lookup_service.cc
index 27c24830aa0..439439b0042 100644
--- a/chromium/components/safe_browsing/core/realtime/url_lookup_service.cc
+++ b/chromium/components/safe_browsing/core/realtime/url_lookup_service.cc
@@ -18,7 +18,6 @@
#include "components/safe_browsing/core/common/thread_utils.h"
#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/realtime/policy_engine.h"
-#include "components/safe_browsing/core/verdict_cache_manager.h"
#include "components/signin/public/identity_manager/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/sync/driver/sync_service.h"
@@ -29,32 +28,11 @@
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
namespace safe_browsing {
namespace {
-const char kRealTimeLookupUrlPrefix[] =
- "https://safebrowsing.google.com/safebrowsing/clientreport/realtime";
-
-const size_t kMaxFailuresToEnforceBackoff = 3;
-
-const size_t kMinBackOffResetDurationInSeconds = 5 * 60; // 5 minutes.
-const size_t kMaxBackOffResetDurationInSeconds = 30 * 60; // 30 minutes.
-
-const size_t kURLLookupTimeoutDurationInSeconds = 10; // 10 seconds.
-
-// Fragements, usernames and passwords are removed, becuase fragments are only
-// used for local navigations and usernames/passwords are too privacy sensitive.
-GURL SanitizeURL(const GURL& url) {
- GURL::Replacements replacements;
- replacements.ClearRef();
- replacements.ClearUsername();
- replacements.ClearPassword();
- return url.ReplaceComponents(replacements);
-}
-
constexpr char kAuthHeaderBearer[] = "Bearer ";
} // namespace
@@ -68,15 +46,16 @@ RealTimeUrlLookupService::RealTimeUrlLookupService(
const ChromeUserPopulation::ProfileManagementStatus&
profile_management_status,
bool is_under_advanced_protection,
- bool is_off_the_record)
- : url_loader_factory_(url_loader_factory),
- cache_manager_(cache_manager),
+ bool is_off_the_record,
+ variations::VariationsService* variations_service)
+ : RealTimeUrlLookupServiceBase(url_loader_factory, cache_manager),
identity_manager_(identity_manager),
sync_service_(sync_service),
pref_service_(pref_service),
profile_management_status_(profile_management_status),
is_under_advanced_protection_(is_under_advanced_protection),
- is_off_the_record_(is_off_the_record) {
+ is_off_the_record_(is_off_the_record),
+ variations_(variations_service) {
token_fetcher_ =
std::make_unique<SafeBrowsingTokenFetcher>(identity_manager_);
}
@@ -140,43 +119,8 @@ void RealTimeUrlLookupService::SendRequest(
ChromeUserPopulation::UserPopulation_MAX + 1);
std::string req_data;
request->SerializeToString(&req_data);
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("safe_browsing_realtime_url_lookup",
- R"(
- semantics {
- sender: "Safe Browsing"
- description:
- "When Safe Browsing can't detect that a URL is safe based on its "
- "local database, it sends the top-level URL to Google to verify it "
- "before showing a warning to the user."
- trigger:
- "When a main frame URL fails to match the local hash-prefix "
- "database of known safe URLs and a valid result from a prior "
- "lookup is not already cached, this will be sent."
- data: "The main frame URL that did not match the local safelist."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: YES
- cookies_store: "Safe Browsing cookie store"
- setting:
- "Users can disable Safe Browsing real time URL checks by "
- "unchecking 'Protect you and your device from dangerous sites' in "
- "Chromium settings under Privacy, or by unchecking 'Make searches "
- "and browsing better (Sends URLs of pages you visit to Google)' in "
- "Chromium settings under Privacy."
- chrome_policy {
- UrlKeyedAnonymizedDataCollectionEnabled {
- policy_options {mode: MANDATORY}
- UrlKeyedAnonymizedDataCollectionEnabled: false
- }
- }
- })");
- auto resource_request = std::make_unique<network::ResourceRequest>();
- resource_request->url = GURL(kRealTimeLookupUrlPrefix);
- resource_request->load_flags = net::LOAD_DISABLE_CACHE;
- resource_request->method = "POST";
+ auto resource_request = GetResourceRequest();
if (access_token_info.has_value()) {
resource_request->headers.SetHeader(
net::HttpRequestHeaders::kAuthorization,
@@ -185,19 +129,8 @@ void RealTimeUrlLookupService::SendRequest(
base::UmaHistogramBoolean("SafeBrowsing.RT.HasTokenInRequest",
access_token_info.has_value());
- std::unique_ptr<network::SimpleURLLoader> owned_loader =
- network::SimpleURLLoader::Create(std::move(resource_request),
- traffic_annotation);
- network::SimpleURLLoader* loader = owned_loader.get();
- owned_loader->AttachStringForUpload(req_data, "application/octet-stream");
- owned_loader->SetTimeoutDuration(
- base::TimeDelta::FromSeconds(kURLLookupTimeoutDurationInSeconds));
- owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
- url_loader_factory_.get(),
- base::BindOnce(&RealTimeUrlLookupService::OnURLLoaderComplete,
- GetWeakPtr(), url, loader, base::TimeTicks::Now()));
-
- pending_requests_[owned_loader.release()] = std::move(response_callback);
+ SendRequestInternal(std::move(resource_request), req_data, url,
+ std::move(response_callback));
base::PostTask(FROM_HERE, CreateTaskTraits(ThreadID::IO),
base::BindOnce(std::move(request_callback), std::move(request),
@@ -206,124 +139,8 @@ void RealTimeUrlLookupService::SendRequest(
: ""));
}
-std::unique_ptr<RTLookupResponse>
-RealTimeUrlLookupService::GetCachedRealTimeUrlVerdict(const GURL& url) {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
- std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info =
- std::make_unique<RTLookupResponse::ThreatInfo>();
-
- base::UmaHistogramBoolean("SafeBrowsing.RT.HasValidCacheManager",
- !!cache_manager_);
-
- base::TimeTicks get_cache_start_time = base::TimeTicks::Now();
-
- RTLookupResponse::ThreatInfo::VerdictType verdict_type =
- cache_manager_ ? cache_manager_->GetCachedRealTimeUrlVerdict(
- url, cached_threat_info.get())
- : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED;
-
- base::UmaHistogramSparse("SafeBrowsing.RT.GetCacheResult", verdict_type);
- UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.GetCache.Time",
- base::TimeTicks::Now() - get_cache_start_time);
-
- if (verdict_type == RTLookupResponse::ThreatInfo::SAFE ||
- verdict_type == RTLookupResponse::ThreatInfo::DANGEROUS) {
- auto cache_response = std::make_unique<RTLookupResponse>();
- RTLookupResponse::ThreatInfo* new_threat_info =
- cache_response->add_threat_info();
- *new_threat_info = *cached_threat_info;
- return cache_response;
- }
- return nullptr;
-}
-
-void RealTimeUrlLookupService::Shutdown() {
- for (auto& pending : pending_requests_) {
- // Treat all pending requests as safe.
- auto response = std::make_unique<RTLookupResponse>();
- std::move(pending.second)
- .Run(/* is_rt_lookup_successful */ true, std::move(response));
- delete pending.first;
- }
- pending_requests_.clear();
-}
-
RealTimeUrlLookupService::~RealTimeUrlLookupService() {}
-void RealTimeUrlLookupService::OnURLLoaderComplete(
- const GURL& url,
- network::SimpleURLLoader* url_loader,
- base::TimeTicks request_start_time,
- std::unique_ptr<std::string> response_body) {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
-
- auto it = pending_requests_.find(url_loader);
- DCHECK(it != pending_requests_.end()) << "Request not found";
-
- UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.Network.Time",
- base::TimeTicks::Now() - request_start_time);
-
- int net_error = url_loader->NetError();
- int response_code = 0;
- if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
- response_code = url_loader->ResponseInfo()->headers->response_code();
- V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
- "SafeBrowsing.RT.Network.Result", net_error, response_code);
-
- auto response = std::make_unique<RTLookupResponse>();
- bool is_rt_lookup_successful = (net_error == net::OK) &&
- (response_code == net::HTTP_OK) &&
- response->ParseFromString(*response_body);
- base::UmaHistogramBoolean("SafeBrowsing.RT.IsLookupSuccessful",
- is_rt_lookup_successful);
- is_rt_lookup_successful ? HandleLookupSuccess() : HandleLookupError();
-
- MayBeCacheRealTimeUrlVerdict(url, *response);
-
- UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.RT.ThreatInfoSize",
- response->threat_info_size());
-
- base::PostTask(FROM_HERE, CreateTaskTraits(ThreadID::IO),
- base::BindOnce(std::move(it->second), is_rt_lookup_successful,
- std::move(response)));
-
- delete it->first;
- pending_requests_.erase(it);
-}
-
-void RealTimeUrlLookupService::MayBeCacheRealTimeUrlVerdict(
- const GURL& url,
- RTLookupResponse response) {
- if (response.threat_info_size() > 0) {
- base::PostTask(FROM_HERE, CreateTaskTraits(ThreadID::UI),
- base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
- base::Unretained(cache_manager_), url,
- response, base::Time::Now(),
- /* store_old_cache */ false));
- }
-}
-
-// static
-bool RealTimeUrlLookupService::CanCheckUrl(const GURL& url) {
- if (!url.SchemeIsHTTPOrHTTPS()) {
- return false;
- }
-
- if (net::IsLocalhost(url)) {
- // Includes: "//localhost/", "//localhost.localdomain/", "//127.0.0.1/"
- return false;
- }
-
- net::IPAddress ip_address;
- if (url.HostIsIPAddress() && ip_address.AssignFromIPLiteral(url.host()) &&
- !ip_address.IsPubliclyRoutable()) {
- // Includes: "//192.168.1.1/", "//172.16.2.2/", "//10.1.1.1/"
- return false;
- }
-
- return true;
-}
-
std::unique_ptr<RTLookupRequest> RealTimeUrlLookupService::FillRequestProto(
const GURL& url) {
auto request = std::make_unique<RTLookupRequest>();
@@ -357,103 +174,66 @@ bool RealTimeUrlLookupService::IsHistorySyncEnabled() {
syncer::HISTORY_DELETE_DIRECTIVES);
}
-size_t RealTimeUrlLookupService::GetBackoffDurationInSeconds() const {
- return did_successful_lookup_since_last_backoff_
- ? kMinBackOffResetDurationInSeconds
- : std::min(kMaxBackOffResetDurationInSeconds,
- 2 * next_backoff_duration_secs_);
-}
-
-void RealTimeUrlLookupService::HandleLookupError() {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
- consecutive_failures_++;
-
- // Any successful lookup clears both |consecutive_failures_| as well as
- // |did_successful_lookup_since_last_backoff_|.
- // On a failure, the following happens:
- // 1) if |consecutive_failures_| < |kMaxFailuresToEnforceBackoff|:
- // Do nothing more.
- // 2) if already in the backoff mode:
- // Do nothing more. This can happen if we had some outstanding real time
- // requests in flight when we entered the backoff mode.
- // 3) if |did_successful_lookup_since_last_backoff_| is true:
- // Enter backoff mode for |kMinBackOffResetDurationInSeconds| seconds.
- // 4) if |did_successful_lookup_since_last_backoff_| is false:
- // This indicates that we've had |kMaxFailuresToEnforceBackoff| since
- // exiting the last backoff with no successful lookups since so do an
- // exponential backoff.
-
- if (consecutive_failures_ < kMaxFailuresToEnforceBackoff)
- return;
-
- if (IsInBackoffMode()) {
- return;
- }
-
- // Enter backoff mode, calculate duration.
- next_backoff_duration_secs_ = GetBackoffDurationInSeconds();
- backoff_timer_.Start(
- FROM_HERE, base::TimeDelta::FromSeconds(next_backoff_duration_secs_),
- this, &RealTimeUrlLookupService::ResetFailures);
- did_successful_lookup_since_last_backoff_ = false;
-}
-
-void RealTimeUrlLookupService::HandleLookupSuccess() {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
- ResetFailures();
-
- // |did_successful_lookup_since_last_backoff_| is set to true only when we
- // complete a lookup successfully.
- did_successful_lookup_since_last_backoff_ = true;
-}
-
-bool RealTimeUrlLookupService::IsInBackoffMode() const {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
- bool in_backoff = backoff_timer_.IsRunning();
- UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.RT.Backoff.State", in_backoff);
- return in_backoff;
-}
-
-void RealTimeUrlLookupService::ResetFailures() {
- DCHECK(CurrentlyOnThread(ThreadID::UI));
- consecutive_failures_ = 0;
- backoff_timer_.Stop();
-}
-
bool RealTimeUrlLookupService::CanPerformFullURLLookup() const {
- return RealTimePolicyEngine::CanPerformFullURLLookup(pref_service_,
- is_off_the_record_);
+ return RealTimePolicyEngine::CanPerformFullURLLookup(
+ pref_service_, is_off_the_record_, variations_);
}
bool RealTimeUrlLookupService::CanPerformFullURLLookupWithToken() const {
return RealTimePolicyEngine::CanPerformFullURLLookupWithToken(
- pref_service_, is_off_the_record_, sync_service_, identity_manager_);
+ pref_service_, is_off_the_record_, sync_service_, identity_manager_,
+ variations_);
}
-bool RealTimeUrlLookupService::IsUserEpOptedIn() const {
+bool RealTimeUrlLookupService::CanCheckSubresourceURL() const {
return IsEnhancedProtectionEnabled(*pref_service_);
}
-// static
-SBThreatType RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
- RTLookupResponse::ThreatInfo::ThreatType rt_threat_type) {
- switch (rt_threat_type) {
- case RTLookupResponse::ThreatInfo::WEB_MALWARE:
- return SB_THREAT_TYPE_URL_MALWARE;
- case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
- return SB_THREAT_TYPE_URL_PHISHING;
- case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
- return SB_THREAT_TYPE_URL_UNWANTED;
- case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
- return SB_THREAT_TYPE_BILLING;
- case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
- NOTREACHED() << "Unexpected RTLookupResponse::ThreatType encountered";
- return SB_THREAT_TYPE_SAFE;
- }
+bool RealTimeUrlLookupService::CanCheckSafeBrowsingDb() const {
+ // Always return true, because consumer real time URL check only works when
+ // safe browsing is enabled.
+ return true;
+}
+
+net::NetworkTrafficAnnotationTag
+RealTimeUrlLookupService::GetTrafficAnnotationTag() const {
+ return net::DefineNetworkTrafficAnnotation(
+ "safe_browsing_realtime_url_lookup",
+ R"(
+ semantics {
+ sender: "Safe Browsing"
+ description:
+ "When Safe Browsing can't detect that a URL is safe based on its "
+ "local database, it sends the top-level URL to Google to verify it "
+ "before showing a warning to the user."
+ trigger:
+ "When a main frame URL fails to match the local hash-prefix "
+ "database of known safe URLs and a valid result from a prior "
+ "lookup is not already cached, this will be sent."
+ data: "The main frame URL that did not match the local safelist."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: YES
+ cookies_store: "Safe Browsing cookie store"
+ setting:
+ "Users can disable Safe Browsing real time URL checks by "
+ "unchecking 'Protect you and your device from dangerous sites' in "
+ "Chromium settings under Privacy, or by unchecking 'Make searches "
+ "and browsing better (Sends URLs of pages you visit to Google)' in "
+ "Chromium settings under Privacy."
+ chrome_policy {
+ UrlKeyedAnonymizedDataCollectionEnabled {
+ policy_options {mode: MANDATORY}
+ UrlKeyedAnonymizedDataCollectionEnabled: false
+ }
+ }
+ })");
}
-base::WeakPtr<RealTimeUrlLookupService> RealTimeUrlLookupService::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
+GURL RealTimeUrlLookupService::GetRealTimeLookupUrl() const {
+ return GURL(
+ "https://safebrowsing.google.com/safebrowsing/clientreport/realtime");
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/realtime/url_lookup_service.h b/chromium/components/safe_browsing/core/realtime/url_lookup_service.h
index 70aa7bdec50..34197d63420 100644
--- a/chromium/components/safe_browsing/core/realtime/url_lookup_service.h
+++ b/chromium/components/safe_browsing/core/realtime/url_lookup_service.h
@@ -14,15 +14,18 @@
#include "base/optional.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
-#include "components/keyed_service/core/keyed_service.h"
#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/proto/csd.pb.h"
#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service_base.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "url/gurl.h"
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}
+
namespace network {
-class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
@@ -34,24 +37,20 @@ namespace syncer {
class SyncService;
}
+namespace variations {
+class VariationsService;
+}
+
class PrefService;
namespace safe_browsing {
-using RTLookupRequestCallback =
- base::OnceCallback<void(std::unique_ptr<RTLookupRequest>, std::string)>;
-
-using RTLookupResponseCallback =
- base::OnceCallback<void(bool, std::unique_ptr<RTLookupResponse>)>;
-
-class VerdictCacheManager;
-
class SafeBrowsingTokenFetcher;
-// This class implements the logic to decide whether the real time lookup
-// feature is enabled for a given user/profile.
-// TODO(crbug.com/1050859): Add RTLookupService check flow.
-class RealTimeUrlLookupService : public KeyedService {
+// This class implements the real time lookup feature for a given user/profile.
+// It is separated from the base class for logic that is related to consumer
+// users.(See: go/chrome-protego-enterprise-dd)
+class RealTimeUrlLookupService : public RealTimeUrlLookupServiceBase {
public:
// |cache_manager|, |identity_manager|, |sync_service| and |pref_service| may
// be null in tests.
@@ -64,58 +63,26 @@ class RealTimeUrlLookupService : public KeyedService {
const ChromeUserPopulation::ProfileManagementStatus&
profile_management_status,
bool is_under_advanced_protection,
- bool is_off_the_record);
+ bool is_off_the_record,
+ variations::VariationsService* variations_service);
~RealTimeUrlLookupService() override;
- // Returns true if |url|'s scheme can be checked.
- static bool CanCheckUrl(const GURL& url);
-
- // Returns true if real time URL lookup is enabled. The check is based on
- // pref settings of the associated profile, whether the profile is an off the
- // record profile and the finch flag.
- bool CanPerformFullURLLookup() const;
-
- // Returns true if real time URL lookup with token is enabled.
- bool CanPerformFullURLLookupWithToken() const;
-
- // Returns true if the real time lookups are currently in backoff mode due to
- // too many prior errors. If this happens, the checking falls back to
- // local hash-based method.
- bool IsInBackoffMode() const;
-
- // Returns true if this profile has opted-in to Enhanced Protection.
- bool IsUserEpOptedIn() const;
-
- // Start the full URL lookup for |url|, call |request_callback| on the same
- // thread when request is sent, call |response_callback| on the same thread
- // when response is received.
- // Note that |request_callback| is not called if there's a valid entry in the
- // cache for |url|.
- // This function is overridden in unit tests.
- virtual void StartLookup(const GURL& url,
- RTLookupRequestCallback request_callback,
- RTLookupResponseCallback response_callback);
+ // RealTimeUrlLookupServiceBase:
+ bool CanPerformFullURLLookup() const override;
- // KeyedService:
- // Called before the actual deletion of the object.
- void Shutdown() override;
+ bool CanCheckSubresourceURL() const override;
- // Returns the SBThreatType for a given
- // RTLookupResponse::ThreatInfo::ThreatType
- static SBThreatType GetSBThreatTypeForRTThreatType(
- RTLookupResponse::ThreatInfo::ThreatType rt_threat_type);
+ bool CanCheckSafeBrowsingDb() const override;
- // Helper function to return a weak pointer.
- base::WeakPtr<RealTimeUrlLookupService> GetWeakPtr();
+ void StartLookup(const GURL& url,
+ RTLookupRequestCallback request_callback,
+ RTLookupResponseCallback response_callback) override;
private:
- using PendingRTLookupRequests =
- base::flat_map<network::SimpleURLLoader*, RTLookupResponseCallback>;
+ // RealTimeUrlLookupServiceBase:
+ net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag() const override;
- // Called to get cache from |cache_manager|. Returns the cached response if
- // there's a cache hit; nullptr otherwise.
- std::unique_ptr<RTLookupResponse> GetCachedRealTimeUrlVerdict(
- const GURL& url);
+ GURL GetRealTimeLookupUrl() const override;
// Called when the access token is obtained from |token_fetcher_|.
void OnGetAccessToken(
@@ -133,72 +100,12 @@ class RealTimeUrlLookupService : public KeyedService {
RTLookupRequestCallback request_callback,
RTLookupResponseCallback response_callback);
- // Called to get cache from |cache_manager|. If a cache is found, return true.
- // Otherwise, return false.
- bool GetCachedRealTimeUrlVerdict(const GURL& url,
- RTLookupResponse* out_response);
-
- // Called to post a task to store the response keyed by the |url| in
- // |cache_manager|.
- void MayBeCacheRealTimeUrlVerdict(const GURL& url, RTLookupResponse response);
-
bool IsHistorySyncEnabled();
- // Returns the duration of the next backoff. Starts at
- // |kMinBackOffResetDurationInSeconds| and increases exponentially until it
- // reaches |kMaxBackOffResetDurationInSeconds|.
- size_t GetBackoffDurationInSeconds() const;
-
- // Called when the request to remote endpoint fails. May initiate or extend
- // backoff.
- void HandleLookupError();
-
- // Called when the request to remote endpoint succeeds. Resets error count and
- // ends backoff.
- void HandleLookupSuccess();
-
- // Resets the error count and ends backoff mode. Functionally same as
- // |HandleLookupSuccess| for now.
- void ResetFailures();
-
- // Called when the response from the real-time lookup remote endpoint is
- // received. |url_loader| is the unowned loader that was used to send the
- // request. |request_start_time| is the time when the request was sent.
- // |response_body| is the response received. |url| is used for calling
- // |MayBeCacheRealTimeUrlVerdict|.
- void OnURLLoaderComplete(const GURL& url,
- network::SimpleURLLoader* url_loader,
- base::TimeTicks request_start_time,
- std::unique_ptr<std::string> response_body);
-
std::unique_ptr<RTLookupRequest> FillRequestProto(const GURL& url);
- PendingRTLookupRequests pending_requests_;
-
- // Count of consecutive failures to complete URL lookup requests. When it
- // reaches |kMaxFailuresToEnforceBackoff|, we enter the backoff mode. It gets
- // reset when we complete a lookup successfully or when the backoff reset
- // timer fires.
- size_t consecutive_failures_ = 0;
-
- // If true, represents that one or more real time lookups did complete
- // successfully since the last backoff or Chrome never entered the breakoff;
- // if false and Chrome re-enters backoff period, the backoff duration is
- // increased exponentially (capped at |kMaxBackOffResetDurationInSeconds|).
- bool did_successful_lookup_since_last_backoff_ = true;
-
- // The current duration of backoff. Increases exponentially until it reaches
- // |kMaxBackOffResetDurationInSeconds|.
- size_t next_backoff_duration_secs_ = 0;
-
- // If this timer is running, backoff is in effect.
- base::OneShotTimer backoff_timer_;
-
- // The URLLoaderFactory we use to issue network requests.
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
- // Unowned object used for getting and storing real time url check cache.
- VerdictCacheManager* cache_manager_;
+ // Returns true if real time URL lookup with GAIA token is enabled.
+ bool CanPerformFullURLLookupWithToken() const;
// Unowned object used for getting access token when real time url check with
// token is enabled.
@@ -223,6 +130,10 @@ class RealTimeUrlLookupService : public KeyedService {
// |url_lookup_service| is an off the record profile.
bool is_off_the_record_;
+ // Unowned. For checking whether real-time checks can be enabled in a given
+ // location.
+ variations::VariationsService* variations_;
+
friend class RealTimeUrlLookupServiceTest;
base::WeakPtrFactory<RealTimeUrlLookupService> weak_factory_{this};
diff --git a/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.cc b/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.cc
new file mode 100644
index 00000000000..be6cc99e8b9
--- /dev/null
+++ b/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.cc
@@ -0,0 +1,284 @@
+// Copyright 2020 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 "components/safe_browsing/core/realtime/url_lookup_service_base.h"
+
+#include "base/base64url.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/core/common/thread_utils.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "net/base/ip_address.h"
+#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+namespace {
+
+const size_t kMaxFailuresToEnforceBackoff = 3;
+
+const size_t kMinBackOffResetDurationInSeconds = 5 * 60; // 5 minutes.
+const size_t kMaxBackOffResetDurationInSeconds = 30 * 60; // 30 minutes.
+
+const size_t kURLLookupTimeoutDurationInSeconds = 10; // 10 seconds.
+
+} // namespace
+
+RealTimeUrlLookupServiceBase::RealTimeUrlLookupServiceBase(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ VerdictCacheManager* cache_manager)
+ : url_loader_factory_(url_loader_factory), cache_manager_(cache_manager) {}
+
+RealTimeUrlLookupServiceBase::~RealTimeUrlLookupServiceBase() = default;
+
+// static
+bool RealTimeUrlLookupServiceBase::CanCheckUrl(const GURL& url) {
+ if (!url.SchemeIsHTTPOrHTTPS()) {
+ return false;
+ }
+
+ if (net::IsLocalhost(url)) {
+ // Includes: "//localhost/", "//localhost.localdomain/", "//127.0.0.1/"
+ return false;
+ }
+
+ net::IPAddress ip_address;
+ if (url.HostIsIPAddress() && ip_address.AssignFromIPLiteral(url.host()) &&
+ !ip_address.IsPubliclyRoutable()) {
+ // Includes: "//192.168.1.1/", "//172.16.2.2/", "//10.1.1.1/"
+ return false;
+ }
+
+ return true;
+}
+
+// static
+SBThreatType RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
+ RTLookupResponse::ThreatInfo::ThreatType rt_threat_type) {
+ switch (rt_threat_type) {
+ case RTLookupResponse::ThreatInfo::WEB_MALWARE:
+ return SB_THREAT_TYPE_URL_MALWARE;
+ case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
+ return SB_THREAT_TYPE_URL_PHISHING;
+ case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
+ return SB_THREAT_TYPE_URL_UNWANTED;
+ case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
+ return SB_THREAT_TYPE_BILLING;
+ case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
+ NOTREACHED() << "Unexpected RTLookupResponse::ThreatType encountered";
+ return SB_THREAT_TYPE_SAFE;
+ }
+}
+
+// static
+GURL RealTimeUrlLookupServiceBase::SanitizeURL(const GURL& url) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ replacements.ClearUsername();
+ replacements.ClearPassword();
+ return url.ReplaceComponents(replacements);
+}
+
+base::WeakPtr<RealTimeUrlLookupServiceBase>
+RealTimeUrlLookupServiceBase::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+size_t RealTimeUrlLookupServiceBase::GetBackoffDurationInSeconds() const {
+ return did_successful_lookup_since_last_backoff_
+ ? kMinBackOffResetDurationInSeconds
+ : std::min(kMaxBackOffResetDurationInSeconds,
+ 2 * next_backoff_duration_secs_);
+}
+
+void RealTimeUrlLookupServiceBase::HandleLookupError() {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+ consecutive_failures_++;
+
+ // Any successful lookup clears both |consecutive_failures_| as well as
+ // |did_successful_lookup_since_last_backoff_|.
+ // On a failure, the following happens:
+ // 1) if |consecutive_failures_| < |kMaxFailuresToEnforceBackoff|:
+ // Do nothing more.
+ // 2) if already in the backoff mode:
+ // Do nothing more. This can happen if we had some outstanding real time
+ // requests in flight when we entered the backoff mode.
+ // 3) if |did_successful_lookup_since_last_backoff_| is true:
+ // Enter backoff mode for |kMinBackOffResetDurationInSeconds| seconds.
+ // 4) if |did_successful_lookup_since_last_backoff_| is false:
+ // This indicates that we've had |kMaxFailuresToEnforceBackoff| since
+ // exiting the last backoff with no successful lookups since so do an
+ // exponential backoff.
+
+ if (consecutive_failures_ < kMaxFailuresToEnforceBackoff)
+ return;
+
+ if (IsInBackoffMode()) {
+ return;
+ }
+
+ // Enter backoff mode, calculate duration.
+ next_backoff_duration_secs_ = GetBackoffDurationInSeconds();
+ backoff_timer_.Start(
+ FROM_HERE, base::TimeDelta::FromSeconds(next_backoff_duration_secs_),
+ this, &RealTimeUrlLookupServiceBase::ResetFailures);
+ did_successful_lookup_since_last_backoff_ = false;
+}
+
+void RealTimeUrlLookupServiceBase::HandleLookupSuccess() {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+ ResetFailures();
+
+ // |did_successful_lookup_since_last_backoff_| is set to true only when we
+ // complete a lookup successfully.
+ did_successful_lookup_since_last_backoff_ = true;
+}
+
+bool RealTimeUrlLookupServiceBase::IsInBackoffMode() const {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+ bool in_backoff = backoff_timer_.IsRunning();
+ UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.RT.Backoff.State", in_backoff);
+ return in_backoff;
+}
+
+void RealTimeUrlLookupServiceBase::ResetFailures() {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+ consecutive_failures_ = 0;
+ backoff_timer_.Stop();
+}
+
+std::unique_ptr<RTLookupResponse>
+RealTimeUrlLookupServiceBase::GetCachedRealTimeUrlVerdict(const GURL& url) {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+ std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info =
+ std::make_unique<RTLookupResponse::ThreatInfo>();
+
+ base::UmaHistogramBoolean("SafeBrowsing.RT.HasValidCacheManager",
+ !!cache_manager_);
+
+ base::TimeTicks get_cache_start_time = base::TimeTicks::Now();
+
+ RTLookupResponse::ThreatInfo::VerdictType verdict_type =
+ cache_manager_ ? cache_manager_->GetCachedRealTimeUrlVerdict(
+ url, cached_threat_info.get())
+ : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED;
+
+ base::UmaHistogramSparse("SafeBrowsing.RT.GetCacheResult", verdict_type);
+ UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.GetCache.Time",
+ base::TimeTicks::Now() - get_cache_start_time);
+
+ if (verdict_type == RTLookupResponse::ThreatInfo::SAFE ||
+ verdict_type == RTLookupResponse::ThreatInfo::DANGEROUS) {
+ auto cache_response = std::make_unique<RTLookupResponse>();
+ RTLookupResponse::ThreatInfo* new_threat_info =
+ cache_response->add_threat_info();
+ *new_threat_info = *cached_threat_info;
+ return cache_response;
+ }
+ return nullptr;
+}
+
+void RealTimeUrlLookupServiceBase::MayBeCacheRealTimeUrlVerdict(
+ const GURL& url,
+ RTLookupResponse response) {
+ if (response.threat_info_size() > 0) {
+ base::PostTask(FROM_HERE, CreateTaskTraits(ThreadID::UI),
+ base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
+ base::Unretained(cache_manager_), url,
+ response, base::Time::Now(),
+ /* store_old_cache */ false));
+ }
+}
+
+std::unique_ptr<network::ResourceRequest>
+RealTimeUrlLookupServiceBase::GetResourceRequest() {
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = GetRealTimeLookupUrl();
+ resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+ resource_request->method = "POST";
+ return resource_request;
+}
+
+void RealTimeUrlLookupServiceBase::SendRequestInternal(
+ std::unique_ptr<network::ResourceRequest> resource_request,
+ const std::string& req_data,
+ const GURL& url,
+ RTLookupResponseCallback response_callback) {
+ std::unique_ptr<network::SimpleURLLoader> owned_loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ GetTrafficAnnotationTag());
+ network::SimpleURLLoader* loader = owned_loader.get();
+ owned_loader->AttachStringForUpload(req_data, "application/octet-stream");
+ owned_loader->SetTimeoutDuration(
+ base::TimeDelta::FromSeconds(kURLLookupTimeoutDurationInSeconds));
+ owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&RealTimeUrlLookupServiceBase::OnURLLoaderComplete,
+ GetWeakPtr(), url, loader, base::TimeTicks::Now()));
+
+ pending_requests_[owned_loader.release()] = std::move(response_callback);
+}
+
+void RealTimeUrlLookupServiceBase::OnURLLoaderComplete(
+ const GURL& url,
+ network::SimpleURLLoader* url_loader,
+ base::TimeTicks request_start_time,
+ std::unique_ptr<std::string> response_body) {
+ DCHECK(CurrentlyOnThread(ThreadID::UI));
+
+ auto it = pending_requests_.find(url_loader);
+ DCHECK(it != pending_requests_.end()) << "Request not found";
+
+ UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.Network.Time",
+ base::TimeTicks::Now() - request_start_time);
+
+ int net_error = url_loader->NetError();
+ int response_code = 0;
+ if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+ response_code = url_loader->ResponseInfo()->headers->response_code();
+ V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+ "SafeBrowsing.RT.Network.Result", net_error, response_code);
+
+ auto response = std::make_unique<RTLookupResponse>();
+ bool is_rt_lookup_successful = (net_error == net::OK) &&
+ (response_code == net::HTTP_OK) &&
+ response->ParseFromString(*response_body);
+ base::UmaHistogramBoolean("SafeBrowsing.RT.IsLookupSuccessful",
+ is_rt_lookup_successful);
+ is_rt_lookup_successful ? HandleLookupSuccess() : HandleLookupError();
+
+ MayBeCacheRealTimeUrlVerdict(url, *response);
+
+ UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.RT.ThreatInfoSize",
+ response->threat_info_size());
+
+ base::PostTask(FROM_HERE, CreateTaskTraits(ThreadID::IO),
+ base::BindOnce(std::move(it->second), is_rt_lookup_successful,
+ std::move(response)));
+
+ delete it->first;
+ pending_requests_.erase(it);
+}
+
+void RealTimeUrlLookupServiceBase::Shutdown() {
+ for (auto& pending : pending_requests_) {
+ // Treat all pending requests as safe.
+ auto response = std::make_unique<RTLookupResponse>();
+ std::move(pending.second)
+ .Run(/* is_rt_lookup_successful */ true, std::move(response));
+ delete pending.first;
+ }
+ pending_requests_.clear();
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.h b/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.h
new file mode 100644
index 00000000000..8300147dbee
--- /dev/null
+++ b/chromium/components/safe_browsing/core/realtime/url_lookup_service_base.h
@@ -0,0 +1,191 @@
+// Copyright 2020 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 COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_BASE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_BASE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "url/gurl.h"
+
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}
+
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+} // namespace network
+
+namespace safe_browsing {
+
+using RTLookupRequestCallback =
+ base::OnceCallback<void(std::unique_ptr<RTLookupRequest>, std::string)>;
+
+using RTLookupResponseCallback =
+ base::OnceCallback<void(bool, std::unique_ptr<RTLookupResponse>)>;
+
+class VerdictCacheManager;
+
+// This base class implements the backoff and cache logic for real time URL
+// lookup feature.
+class RealTimeUrlLookupServiceBase : public KeyedService {
+ public:
+ explicit RealTimeUrlLookupServiceBase(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ VerdictCacheManager* cache_manager);
+ ~RealTimeUrlLookupServiceBase() override;
+
+ // Returns true if |url|'s scheme can be checked.
+ static bool CanCheckUrl(const GURL& url);
+
+ // Returns the SBThreatType for a given
+ // RTLookupResponse::ThreatInfo::ThreatType
+ static SBThreatType GetSBThreatTypeForRTThreatType(
+ RTLookupResponse::ThreatInfo::ThreatType rt_threat_type);
+
+ // Returns true if the real time lookups are currently in backoff mode due to
+ // too many prior errors. If this happens, the checking falls back to
+ // local hash-based method.
+ bool IsInBackoffMode() const;
+
+ // Helper function to return a weak pointer.
+ base::WeakPtr<RealTimeUrlLookupServiceBase> GetWeakPtr();
+
+ // Returns true if real time URL lookup is enabled. The check is based on
+ // pref settings of the associated profile, whether the profile is an off the
+ // record profile and the finch flag.
+ virtual bool CanPerformFullURLLookup() const = 0;
+
+ // Returns true if this profile has opted-in to check subresource URLs.
+ virtual bool CanCheckSubresourceURL() const = 0;
+
+ // Returns whether safe browsing database can be checked when real time URL
+ // check is enabled.
+ virtual bool CanCheckSafeBrowsingDb() const = 0;
+
+ // Start the full URL lookup for |url|, call |request_callback| on the same
+ // thread when request is sent, call |response_callback| on the same thread
+ // when response is received.
+ // Note that |request_callback| is not called if there's a valid entry in the
+ // cache for |url|.
+ // This function is overridden in unit tests.
+ virtual void StartLookup(const GURL& url,
+ RTLookupRequestCallback request_callback,
+ RTLookupResponseCallback response_callback) = 0;
+
+ // KeyedService:
+ // Called before the actual deletion of the object.
+ void Shutdown() override;
+
+ protected:
+ // Fragments, usernames and passwords are removed, because fragments are only
+ // used for local navigations and usernames/passwords are too privacy
+ // sensitive.
+ static GURL SanitizeURL(const GURL& url);
+
+ // Returns the traffic annotation tag that is attached in the simple URL
+ // loader.
+ virtual net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag() const = 0;
+
+ // Returns the endpoint that the URL lookup will be sent to.
+ virtual GURL GetRealTimeLookupUrl() const = 0;
+
+ // Returns the duration of the next backoff. Starts at
+ // |kMinBackOffResetDurationInSeconds| and increases exponentially until it
+ // reaches |kMaxBackOffResetDurationInSeconds|.
+ size_t GetBackoffDurationInSeconds() const;
+
+ // Called when the request to remote endpoint fails. May initiate or extend
+ // backoff.
+ void HandleLookupError();
+
+ // Called when the request to remote endpoint succeeds. Resets error count and
+ // ends backoff.
+ void HandleLookupSuccess();
+
+ // Resets the error count and ends backoff mode. Functionally same as
+ // |HandleLookupSuccess| for now.
+ void ResetFailures();
+
+ // Called to get cache from |cache_manager|. Returns the cached response if
+ // there's a cache hit; nullptr otherwise.
+ std::unique_ptr<RTLookupResponse> GetCachedRealTimeUrlVerdict(
+ const GURL& url);
+
+ // Called to post a task to store the response keyed by the |url| in
+ // |cache_manager|.
+ void MayBeCacheRealTimeUrlVerdict(const GURL& url, RTLookupResponse response);
+
+ // Get a resource request with URL, load_flags and method set.
+ std::unique_ptr<network::ResourceRequest> GetResourceRequest();
+
+ void SendRequestInternal(
+ std::unique_ptr<network::ResourceRequest> resource_request,
+ const std::string& req_data,
+ const GURL& url,
+ RTLookupResponseCallback response_callback);
+
+ private:
+ using PendingRTLookupRequests =
+ base::flat_map<network::SimpleURLLoader*, RTLookupResponseCallback>;
+
+ // Called when the response from the real-time lookup remote endpoint is
+ // received. |url_loader| is the unowned loader that was used to send the
+ // request. |request_start_time| is the time when the request was sent.
+ // |response_body| is the response received. |url| is used for calling
+ // |MayBeCacheRealTimeUrlVerdict|.
+ void OnURLLoaderComplete(const GURL& url,
+ network::SimpleURLLoader* url_loader,
+ base::TimeTicks request_start_time,
+ std::unique_ptr<std::string> response_body);
+
+ // Count of consecutive failures to complete URL lookup requests. When it
+ // reaches |kMaxFailuresToEnforceBackoff|, we enter the backoff mode. It gets
+ // reset when we complete a lookup successfully or when the backoff reset
+ // timer fires.
+ size_t consecutive_failures_ = 0;
+
+ // If true, represents that one or more real time lookups did complete
+ // successfully since the last backoff or Chrome never entered the breakoff;
+ // if false and Chrome re-enters backoff period, the backoff duration is
+ // increased exponentially (capped at |kMaxBackOffResetDurationInSeconds|).
+ bool did_successful_lookup_since_last_backoff_ = true;
+
+ // The current duration of backoff. Increases exponentially until it reaches
+ // |kMaxBackOffResetDurationInSeconds|.
+ size_t next_backoff_duration_secs_ = 0;
+
+ // If this timer is running, backoff is in effect.
+ base::OneShotTimer backoff_timer_;
+
+ // The URLLoaderFactory we use to issue network requests.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+ // Unowned object used for getting and storing real time url check cache.
+ VerdictCacheManager* cache_manager_;
+
+ // All requests that are sent but haven't received a response yet.
+ PendingRTLookupRequests pending_requests_;
+
+ base::WeakPtrFactory<RealTimeUrlLookupServiceBase> weak_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(RealTimeUrlLookupServiceBase);
+
+}; // class RealTimeUrlLookupServiceBase
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_BASE_H_
diff --git a/chromium/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc b/chromium/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc
index 2bcffa75a45..8ca8d0135dc 100644
--- a/chromium/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc
+++ b/chromium/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc
@@ -67,7 +67,7 @@ class RealTimeUrlLookupServiceTest : public PlatformTest {
identity_test_env_->identity_manager(), &test_sync_service_,
&test_pref_service_, ChromeUserPopulation::NOT_MANAGED,
/*is_under_advanced_protection=*/true,
- /*is_off_the_record=*/false);
+ /*is_off_the_record=*/false, /*variations_service=*/nullptr);
}
void TearDown() override {
@@ -76,7 +76,7 @@ class RealTimeUrlLookupServiceTest : public PlatformTest {
}
bool CanCheckUrl(const GURL& url) {
- return RealTimeUrlLookupService::CanCheckUrl(url);
+ return RealTimeUrlLookupServiceBase::CanCheckUrl(url);
}
void HandleLookupError() { rt_service_->HandleLookupError(); }
void HandleLookupSuccess() { rt_service_->HandleLookupSuccess(); }
@@ -500,16 +500,16 @@ TEST_F(RealTimeUrlLookupServiceTest, TestExponentialBackoffWithResetOnSuccess) {
TEST_F(RealTimeUrlLookupServiceTest, TestGetSBThreatTypeForRTThreatType) {
EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE,
- RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+ RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
RTLookupResponse::ThreatInfo::WEB_MALWARE));
EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING,
- RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+ RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING));
EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED,
- RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+ RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE));
EXPECT_EQ(SB_THREAT_TYPE_BILLING,
- RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+ RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType(
RTLookupResponse::ThreatInfo::UNCLEAR_BILLING));
}
diff --git a/chromium/components/safe_browsing/core/verdict_cache_manager.cc b/chromium/components/safe_browsing/core/verdict_cache_manager.cc
index 64694b39f3e..64eee7e22ce 100644
--- a/chromium/components/safe_browsing/core/verdict_cache_manager.cc
+++ b/chromium/components/safe_browsing/core/verdict_cache_manager.cc
@@ -5,6 +5,7 @@
#include "components/safe_browsing/core/verdict_cache_manager.h"
#include "base/base64.h"
+#include "base/metrics/histogram_functions.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
@@ -544,6 +545,9 @@ void VerdictCacheManager::CacheRealTimeUrlVerdict(
hostname, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
std::string(), std::move(cache_dictionary));
}
+ base::UmaHistogramCounts10000(
+ "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount",
+ stored_verdict_count_real_time_url_check_);
}
RTLookupResponse::ThreatInfo::VerdictType
@@ -608,6 +612,9 @@ void VerdictCacheManager::CleanUpExpiredPhishGuardVerdicts() {
}
void VerdictCacheManager::CleanUpExpiredRealTimeUrlCheckVerdicts() {
+ if (stored_verdict_count_real_time_url_check_ == 0) {
+ return;
+ }
ContentSettingsForOneType safe_browsing_url_check_data_settings;
content_settings_->GetSettingsForOneType(
ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
@@ -796,8 +803,8 @@ size_t VerdictCacheManager::GetRealTimeUrlCheckVerdictCountForURL(
const GURL& url) {
std::unique_ptr<base::DictionaryValue> cache_dictionary =
base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
- url, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
- nullptr));
+ url, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
+ std::string(), nullptr));
if (!cache_dictionary || cache_dictionary->empty())
return 0;
base::Value* verdict_dictionary =
diff --git a/chromium/components/safe_browsing/core/verdict_cache_manager.h b/chromium/components/safe_browsing/core/verdict_cache_manager.h
index 915b6577374..1865adf6c69 100644
--- a/chromium/components/safe_browsing/core/verdict_cache_manager.h
+++ b/chromium/components/safe_browsing/core/verdict_cache_manager.h
@@ -128,8 +128,8 @@ class VerdictCacheManager : public history::HistoryServiceObserver,
// This method is only used for testing.
size_t GetRealTimeUrlCheckVerdictCountForURL(const GURL& url);
- // This method is only used for testing.
- int GetStoredRealTimeUrlCheckVerdictCount() {
+ // This method is only used for testing and logging metrics.
+ int stored_verdict_count_real_time_url_check() {
return stored_verdict_count_real_time_url_check_;
}
@@ -141,7 +141,7 @@ class VerdictCacheManager : public history::HistoryServiceObserver,
base::Optional<size_t> stored_verdict_count_password_entry_;
// Number of verdict stored for this profile for real time url check pings.
- // This is only used for testing.
+ // This is used for testing, logging metrics and cleaning up during shutdown.
int stored_verdict_count_real_time_url_check_ = 0;
ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
diff --git a/chromium/components/safe_browsing/core/verdict_cache_manager_unittest.cc b/chromium/components/safe_browsing/core/verdict_cache_manager_unittest.cc
index 1f254d48600..db01ae4b247 100644
--- a/chromium/components/safe_browsing/core/verdict_cache_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/verdict_cache_manager_unittest.cc
@@ -6,6 +6,7 @@
#include "base/base64.h"
#include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/values.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/safe_browsing/core/common/test_task_environment.h"
@@ -326,6 +327,9 @@ TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+ // Prepare 2 verdicts for SAFE_BROWSING_URL_CHECK_DATA:
+ // (1) "www.example.com/" expired
+ // (2) "www.example.com/path" valid
RTLookupResponse response;
AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
@@ -338,7 +342,7 @@ TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
cache_manager_->CacheRealTimeUrlVerdict(GURL("https://www.example.com/"),
response, base::Time::Now(),
/* store_old_cache */ false);
- ASSERT_EQ(2, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
+ ASSERT_EQ(2, cache_manager_->stored_verdict_count_real_time_url_check());
cache_manager_->CleanUpExpiredVerdicts();
@@ -346,7 +350,7 @@ TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
- ASSERT_EQ(1, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
+ ASSERT_EQ(1, cache_manager_->stored_verdict_count_real_time_url_check());
LoginReputationClientResponse actual_verdict;
password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
// Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc/.
@@ -388,6 +392,18 @@ TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
GURL("https://bar.com/xyz/index.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
password_type, &actual_verdict));
+
+ RTLookupResponse::ThreatInfo actual_real_time_threat_info;
+ // No cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/.
+ EXPECT_EQ(
+ RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
+ cache_manager_->GetCachedRealTimeUrlVerdict(
+ GURL("https://www.example.com/"), &actual_real_time_threat_info));
+ // Has cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/path.
+ EXPECT_EQ(
+ RTLookupResponse::ThreatInfo::DANGEROUS,
+ cache_manager_->GetCachedRealTimeUrlVerdict(
+ GURL("https://www.example.com/path"), &actual_real_time_threat_info));
}
TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdictWithInvalidEntry) {
@@ -443,6 +459,7 @@ TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdictWithInvalidEntry) {
}
TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedRealTimeUrlCheckVerdict) {
+ base::HistogramTester histograms;
GURL url("https://www.example.com/path");
RTLookupResponse response;
@@ -465,6 +482,9 @@ TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedRealTimeUrlCheckVerdict) {
EXPECT_EQ(60, out_verdict.cache_duration_sec());
EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
out_verdict.threat_type());
+ histograms.ExpectUniqueSample(
+ "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount",
+ /* sample */ 2, /* expected_count */ 1);
}
TEST_F(VerdictCacheManagerTest,
@@ -545,6 +565,7 @@ TEST_F(VerdictCacheManagerTest,
cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */,
deleted_urls);
+ EXPECT_EQ(0, cache_manager_->stored_verdict_count_real_time_url_check());
EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
}
@@ -629,6 +650,7 @@ TEST_F(VerdictCacheManagerTest, TestExactMatching) {
}
TEST_F(VerdictCacheManagerTest, TestMatchingTypeNotSet) {
+ base::HistogramTester histograms;
std::string cache_expression = "a.example.test/path1";
GURL url("https://a.example.test/path1");
@@ -646,6 +668,9 @@ TEST_F(VerdictCacheManagerTest, TestMatchingTypeNotSet) {
// If |cache_expression_match_type| is not set, ignore this cache.
EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+ histograms.ExpectBucketCount(
+ "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount",
+ /* sample */ 0, /* expected_count */ 1);
new_threat_info->set_cache_expression_match_type(
RTLookupResponse::ThreatInfo::EXACT_MATCH);
@@ -654,6 +679,9 @@ TEST_F(VerdictCacheManagerTest, TestMatchingTypeNotSet) {
// Should be able to get the cache if |cache_expression_match_type| is set.
EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+ histograms.ExpectBucketCount(
+ "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount",
+ /* sample */ 1, /* expected_count */ 1);
}
TEST_F(VerdictCacheManagerTest, TestReadOldRealTimeUrlCheckCacheNotCrash) {
diff --git a/chromium/components/safe_browsing/ios/browser/BUILD.gn b/chromium/components/safe_browsing/ios/browser/BUILD.gn
new file mode 100644
index 00000000000..87ca89f183a
--- /dev/null
+++ b/chromium/components/safe_browsing/ios/browser/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2020 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.
+
+source_set("allow_list") {
+ sources = [
+ "safe_browsing_url_allow_list.h",
+ "safe_browsing_url_allow_list.mm",
+ ]
+
+ deps = [
+ "//components/safe_browsing/core/db:v4_protocol_manager_util",
+ "//ios/web/public",
+ "//url",
+ ]
+
+ configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h b/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h
new file mode 100644
index 00000000000..3253e3bdea6
--- /dev/null
+++ b/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h
@@ -0,0 +1,131 @@
+// Copyright 2020 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 COMPONENTS_SAFE_BROWSING_IOS_BROWSER_SAFE_BROWSING_URL_ALLOW_LIST_H_
+#define COMPONENTS_SAFE_BROWSING_IOS_BROWSER_SAFE_BROWSING_URL_ALLOW_LIST_H_
+
+#include <map>
+#include <set>
+
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#import "ios/web/public/web_state_user_data.h"
+#include "url/gurl.h"
+
+// SafeBrowsingUrlAllowList tracks the whitelist decisions for URLs for a given
+// threat type, as well as decisions that are pending. Decisions are stored for
+// URLs with empty paths, meaning that whitelisted threats are allowed for the
+// entire domain.
+class SafeBrowsingUrlAllowList
+ : public web::WebStateUserData<SafeBrowsingUrlAllowList> {
+ public:
+ // Enum describing the policy for navigations with a particular threat type to
+ // a URL.
+ enum class Policy : short { kDisallowed, kPending, kAllowed };
+
+ // Observer class for the allow list.
+ class Observer : public base::CheckedObserver {
+ public:
+ // Called when the policy for navigations to |url| with |threat_type| is
+ // updated to |policy|.
+ virtual void ThreatPolicyUpdated(SafeBrowsingUrlAllowList* allow_list,
+ const GURL& url,
+ safe_browsing::SBThreatType threat_type,
+ Policy policy) {}
+
+ // Called when the policies for navigations to |url| with the threats in
+ // |threat_types| are updated to |policy|.
+ virtual void ThreatPolicyBatchUpdated(
+ SafeBrowsingUrlAllowList* allow_list,
+ const GURL& url,
+ const std::set<safe_browsing::SBThreatType>& threat_type,
+ Policy policy) {}
+
+ // Called when |allow_list| is about to be destroyed.
+ virtual void SafeBrowsingAllowListDestroyed(
+ SafeBrowsingUrlAllowList* allow_list) {}
+ };
+
+ ~SafeBrowsingUrlAllowList() override;
+
+ // Adds and removes observers.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Returns whether unsafe navigations to |url| are allowed. If |threat_types|
+ // is non-null, it is populated with the allowed threat types.
+ bool AreUnsafeNavigationsAllowed(
+ const GURL& url,
+ std::set<safe_browsing::SBThreatType>* threat_types = nullptr) const;
+
+ // Allows future unsafe navigations to |url| that encounter threats with
+ // |threat_type|.
+ void AllowUnsafeNavigations(const GURL& url,
+ safe_browsing::SBThreatType threat_type);
+
+ // Prohibits all previously allowed navigations for |url|.
+ void DisallowUnsafeNavigations(const GURL& url);
+
+ // Returns whether there are pending unsafe navigation decisions for |url|.
+ // If |threat_types| is non-null, it is populated with the pending threat
+ // types.
+ bool IsUnsafeNavigationDecisionPending(
+ const GURL& url,
+ std::set<safe_browsing::SBThreatType>* threat_types = nullptr) const;
+
+ // Records that a navigation to |url| has encountered |threat_type|, but the
+ // user has not yet chosen whether to allow the navigation.
+ void AddPendingUnsafeNavigationDecision(
+ const GURL& url,
+ safe_browsing::SBThreatType threat_type);
+
+ // Removes all pending decisions for |url|.
+ void RemovePendingUnsafeNavigationDecisions(const GURL& url);
+
+ private:
+ explicit SafeBrowsingUrlAllowList(web::WebState* web_state);
+ friend class web::WebStateUserData<SafeBrowsingUrlAllowList>;
+ WEB_STATE_USER_DATA_KEY_DECL();
+
+ // Struct storing the threat types that have been allowed and those for
+ // which the user has not made a decision yet.
+ struct UnsafeNavigationDecisions {
+ UnsafeNavigationDecisions();
+ ~UnsafeNavigationDecisions();
+ std::set<safe_browsing::SBThreatType> allowed_threats;
+ std::set<safe_browsing::SBThreatType> pending_threats;
+ };
+
+ // Returns a reference to the UnsafeNavigationDecisions for |url|. The path
+ // is stripped from the URLs before accessing |decisions_| to allow unafe
+ // navigation decisions to be shared across all URLs for a given domain.
+ UnsafeNavigationDecisions& GetUnsafeNavigationDecisions(const GURL& url);
+ const UnsafeNavigationDecisions& GetUnsafeNavigationDecisions(
+ const GURL& url) const;
+
+ // Setter for the policy for navigations to |url| with |threat_type|.
+ void SetThreatPolicy(const GURL& url,
+ safe_browsing::SBThreatType threat_type,
+ Policy policy);
+
+ // Returns whether the list contains any |policy| decisions for |url|.
+ // Populates |threat_types| with found threats if provided.
+ bool ContainsThreats(
+ const GURL& url,
+ Policy policy,
+ std::set<safe_browsing::SBThreatType>* threat_types) const;
+
+ // Disallows all threats that for |url| that are currently allowed under
+ // |policy|.
+ void RevertPolicy(const GURL& url, Policy policy);
+
+ // The WebState whose allowed navigations are recorded by this list.
+ web::WebState* web_state_ = nullptr;
+ // Map storing the whitelist decisions for each URL.
+ std::map<GURL, UnsafeNavigationDecisions> decisions_;
+ base::ObserverList<Observer, /*check_empty=*/true> observers_;
+};
+
+#endif // COMPONENTS_SAFE_BROWSING_IOS_BROWSER_SAFE_BROWSING_URL_ALLOW_LIST_H_
diff --git a/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.mm b/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.mm
new file mode 100644
index 00000000000..763f01145e0
--- /dev/null
+++ b/chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.mm
@@ -0,0 +1,159 @@
+// Copyright 2020 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 "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h"
+
+#import "ios/web/public/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using safe_browsing::SBThreatType;
+
+WEB_STATE_USER_DATA_KEY_IMPL(SafeBrowsingUrlAllowList)
+
+SafeBrowsingUrlAllowList::SafeBrowsingUrlAllowList(web::WebState* web_state)
+ : web_state_(web_state) {}
+
+SafeBrowsingUrlAllowList::~SafeBrowsingUrlAllowList() {
+ for (auto& observer : observers_) {
+ observer.SafeBrowsingAllowListDestroyed(this);
+ }
+}
+
+void SafeBrowsingUrlAllowList::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void SafeBrowsingUrlAllowList::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool SafeBrowsingUrlAllowList::AreUnsafeNavigationsAllowed(
+ const GURL& url,
+ std::set<SBThreatType>* threat_types) const {
+ return ContainsThreats(url, Policy::kAllowed, threat_types);
+}
+
+void SafeBrowsingUrlAllowList::AllowUnsafeNavigations(
+ const GURL& url,
+ SBThreatType threat_type) {
+ SetThreatPolicy(url, threat_type, Policy::kAllowed);
+}
+
+void SafeBrowsingUrlAllowList::DisallowUnsafeNavigations(const GURL& url) {
+ RevertPolicy(url, Policy::kAllowed);
+}
+
+bool SafeBrowsingUrlAllowList::IsUnsafeNavigationDecisionPending(
+ const GURL& url,
+ std::set<SBThreatType>* threat_types) const {
+ return ContainsThreats(url, Policy::kPending, threat_types);
+}
+
+void SafeBrowsingUrlAllowList::AddPendingUnsafeNavigationDecision(
+ const GURL& url,
+ SBThreatType threat_type) {
+ SetThreatPolicy(url, threat_type, Policy::kPending);
+}
+
+void SafeBrowsingUrlAllowList::RemovePendingUnsafeNavigationDecisions(
+ const GURL& url) {
+ RevertPolicy(url, Policy::kPending);
+}
+
+#pragma mark - Private
+
+SafeBrowsingUrlAllowList::UnsafeNavigationDecisions&
+SafeBrowsingUrlAllowList::GetUnsafeNavigationDecisions(const GURL& url) {
+ return decisions_[url.GetWithEmptyPath()];
+}
+
+const SafeBrowsingUrlAllowList::UnsafeNavigationDecisions&
+SafeBrowsingUrlAllowList::GetUnsafeNavigationDecisions(const GURL& url) const {
+ static UnsafeNavigationDecisions kEmptyDecisions;
+ const auto& it = decisions_.find(url.GetWithEmptyPath());
+ if (it == decisions_.end())
+ return kEmptyDecisions;
+ return it->second;
+}
+
+void SafeBrowsingUrlAllowList::SetThreatPolicy(
+ const GURL& url,
+ safe_browsing::SBThreatType threat_type,
+ Policy policy) {
+ auto& decisions = GetUnsafeNavigationDecisions(url);
+ if (policy == Policy::kDisallowed) {
+ decisions.allowed_threats.erase(threat_type);
+ decisions.pending_threats.erase(threat_type);
+ }
+ if (policy == Policy::kPending) {
+ decisions.allowed_threats.erase(threat_type);
+ decisions.pending_threats.insert(threat_type);
+ }
+ if (policy == Policy::kAllowed) {
+ decisions.allowed_threats.insert(threat_type);
+ decisions.pending_threats.erase(threat_type);
+ }
+ for (auto& observer : observers_) {
+ observer.ThreatPolicyUpdated(this, url, threat_type, policy);
+ }
+ web_state_->DidChangeVisibleSecurityState();
+}
+
+bool SafeBrowsingUrlAllowList::ContainsThreats(
+ const GURL& url,
+ Policy policy,
+ std::set<SBThreatType>* threat_types) const {
+ const std::set<SBThreatType>* threats_with_policy = nullptr;
+ switch (policy) {
+ case Policy::kAllowed:
+ threats_with_policy = &GetUnsafeNavigationDecisions(url).allowed_threats;
+ break;
+ case Policy::kPending:
+ threats_with_policy = &GetUnsafeNavigationDecisions(url).pending_threats;
+ break;
+ case Policy::kDisallowed:
+ break;
+ }
+ if (threats_with_policy && !threats_with_policy->empty()) {
+ if (threat_types)
+ *threat_types = *threats_with_policy;
+ return true;
+ }
+ return false;
+}
+
+void SafeBrowsingUrlAllowList::RevertPolicy(const GURL& url, Policy policy) {
+ std::set<SBThreatType>* decisions_to_revert = nullptr;
+ switch (policy) {
+ case Policy::kAllowed:
+ decisions_to_revert = &GetUnsafeNavigationDecisions(url).allowed_threats;
+ break;
+ case Policy::kPending:
+ decisions_to_revert = &GetUnsafeNavigationDecisions(url).pending_threats;
+ break;
+ case Policy::kDisallowed:
+ break;
+ }
+ if (!decisions_to_revert)
+ return;
+
+ std::set<SBThreatType> disallowed_threats;
+ disallowed_threats.swap(*decisions_to_revert);
+ for (auto& observer : observers_) {
+ observer.ThreatPolicyBatchUpdated(this, url, disallowed_threats,
+ Policy::kDisallowed);
+ }
+ web_state_->DidChangeVisibleSecurityState();
+}
+
+#pragma mark - SafeBrowsingUrlAllowList::UnsafeNavigationDecisions
+
+SafeBrowsingUrlAllowList::UnsafeNavigationDecisions::
+ UnsafeNavigationDecisions() = default;
+
+SafeBrowsingUrlAllowList::UnsafeNavigationDecisions::
+ ~UnsafeNavigationDecisions() = default;