Commit be93a33a authored by Aidan Beggs's avatar Aidan Beggs Committed by Commit Bot

Ensure all safety tip heuristics always run.

This CL ensures that all safety tip heuristics are always run, in lieu
of the previous short-circuiting behavior, in preparation for recording
metrics. This CL also refactors the reputation check results out into a
wrapper struct, to cut down on the number of variables that need to be
passed down the call chain. Finally, this CL fixes a few miscellaneous
typos.

Bug: 1014598
Change-Id: Ibeda24f817f46d8d36f23fab60f30615821e544f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1881126
Commit-Queue: Aidan Beggs <beggs@google.com>
Reviewed-by: default avatarJoe DeBlasio <jdeblasio@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709953}
parent cebf43bd
...@@ -99,25 +99,25 @@ bool ShouldTriggerSafetyTipFromKeywordInURL( ...@@ -99,25 +99,25 @@ bool ShouldTriggerSafetyTipFromKeywordInURL(
url, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, url, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
// Getting a registry length of 0 means that our URL has an uknown registry. // Getting a registry length of 0 means that our URL has an unknown registry.
if (registry_length == 0) { if (registry_length == 0) {
return false; return false;
} }
// "eTLD + 1 - 1": "www.google.com" -> "google.com" -> "google" // "eTLD + 1 - 1": "www.google.com" -> "google.com" -> "google".
std::string eTLD_plusminus = std::string eTLD_plusminus =
eTLD_plus_one.substr(0, eTLD_plus_one.size() - registry_length - 1); eTLD_plus_one.substr(0, eTLD_plus_one.size() - registry_length - 1);
// We should never end up with a "." in our eTLD + 1 - 1 // We should never end up with a "." in our eTLD + 1 - 1.
DCHECK_EQ(eTLD_plusminus.find("."), std::string::npos); DCHECK_EQ(eTLD_plusminus.find("."), std::string::npos);
// Any problems that would result in an empty eTLD + 1 - 1 should have been // Any problems that would result in an empty eTLD + 1 - 1 should have been
// caught bia the |eTLD_plus_one| check. // caught via the |eTLD_plus_one| check.
const std::vector<std::string> eTLD_plusminus_parts = base::SplitString( const std::vector<std::string> eTLD_plusminus_parts = base::SplitString(
eTLD_plusminus, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); eTLD_plusminus, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// We only care about finding a keyword here if there's more than one part to // We only care about finding a keyword here if there's more than one part to
// the tokenized eTLD+1-1. // the tokenized eTLD + 1 - 1.
if (eTLD_plusminus_parts.size() <= 1) { if (eTLD_plusminus_parts.size() <= 1) {
return false; return false;
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/safe_browsing/db/v4_protocol_manager_util.h" #include "components/safe_browsing/db/v4_protocol_manager_util.h"
#include "components/security_state/core/security_state.h"
#include "components/url_formatter/spoof_checks/top_domains/top500_domains.h" #include "components/url_formatter/spoof_checks/top_domains/top500_domains.h"
#include "url/url_constants.h" #include "url/url_constants.h"
...@@ -51,7 +52,7 @@ class ReputationServiceFactory : public BrowserContextKeyedServiceFactory { ...@@ -51,7 +52,7 @@ class ReputationServiceFactory : public BrowserContextKeyedServiceFactory {
"ReputationServiceFactory", "ReputationServiceFactory",
BrowserContextDependencyManager::GetInstance()) {} BrowserContextDependencyManager::GetInstance()) {}
~ReputationServiceFactory() override {} ~ReputationServiceFactory() override = default;
// BrowserContextKeyedServiceFactory: // BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor( KeyedService* BuildServiceInstanceFor(
...@@ -205,13 +206,19 @@ void ReputationService::GetReputationStatusWithEngagedSites( ...@@ -205,13 +206,19 @@ void ReputationService::GetReputationStatusWithEngagedSites(
const std::vector<DomainInfo>& engaged_sites) { const std::vector<DomainInfo>& engaged_sites) {
const DomainInfo navigated_domain = GetDomainInfo(url); const DomainInfo navigated_domain = GetDomainInfo(url);
ReputationCheckResult result;
// We evaluate every heuristic for metrics, but only display the first result
// for the UI. We use |done_checking_reputation_status| to track when we've
// settled on the safety tip to show in the UI, so as to not overwrite this
// decision with other heuristics that may trigger later.
bool done_checking_reputation_status = false;
// 0. Server-side warning suppression. // 0. Server-side warning suppression.
// If the URL is on the allowlist list, do nothing else. This is only used to // If the URL is on the allowlist list, do nothing else. This is only used to
// mitigate false positives, so no further processing should be done. // mitigate false positives, so no further processing should be done.
if (ShouldSuppressWarning(url)) { if (ShouldSuppressWarning(url)) {
std::move(callback).Run(security_state::SafetyTipStatus::kNone, done_checking_reputation_status = true;
IsIgnored(url), url, GURL());
return;
} }
// 1. Engagement check // 1. Engagement check
...@@ -225,47 +232,55 @@ void ReputationService::GetReputationStatusWithEngagedSites( ...@@ -225,47 +232,55 @@ void ReputationService::GetReputationStatusWithEngagedSites(
engaged_domain.domain_and_registry); engaged_domain.domain_and_registry);
}); });
if (already_engaged != engaged_sites.end()) { if (already_engaged != engaged_sites.end()) {
std::move(callback).Run(security_state::SafetyTipStatus::kNone, done_checking_reputation_status = true;
IsIgnored(url), url, GURL());
return;
} }
// 2. Server-side blocklist check. // 2. Server-side blocklist check.
security_state::SafetyTipStatus status = GetSafetyTipUrlBlockType(url); security_state::SafetyTipStatus status = GetSafetyTipUrlBlockType(url);
if (status != security_state::SafetyTipStatus::kNone) { if (status != security_state::SafetyTipStatus::kNone) {
std::move(callback).Run(status, IsIgnored(url), url, GURL()); if (!done_checking_reputation_status) {
return; result.safety_tip_status = status;
}
result.triggered_heuristics.blocklist_heuristic_triggered = true;
done_checking_reputation_status = true;
} }
// 3. Protect against bad false positives by allowing top domains. // 3. Protect against bad false positives by allowing top domains.
// Empty domain_and_registry happens on private domains. // Empty domain_and_registry happens on private domains.
if (navigated_domain.domain_and_registry.empty() || if (navigated_domain.domain_and_registry.empty() ||
IsTopDomain(navigated_domain)) { IsTopDomain(navigated_domain)) {
std::move(callback).Run(security_state::SafetyTipStatus::kNone, done_checking_reputation_status = true;
IsIgnored(url), url, GURL());
return;
} }
// 4. Lookalike heuristics. // 4. Lookalike heuristics.
GURL safe_url; GURL safe_url;
if (ShouldTriggerSafetyTipFromLookalike(url, navigated_domain, engaged_sites, if (already_engaged == engaged_sites.end() &&
ShouldTriggerSafetyTipFromLookalike(url, navigated_domain, engaged_sites,
&safe_url)) { &safe_url)) {
std::move(callback).Run(security_state::SafetyTipStatus::kLookalike, if (!done_checking_reputation_status) {
IsIgnored(url), url, safe_url); result.suggested_url = safe_url;
return; result.safety_tip_status = security_state::SafetyTipStatus::kLookalike;
}
result.triggered_heuristics.lookalike_heuristic_triggered = true;
done_checking_reputation_status = true;
} }
// 5. Keyword heuristics. // 5. Keyword heuristics.
if (ShouldTriggerSafetyTipFromKeywordInURL( if (ShouldTriggerSafetyTipFromKeywordInURL(
url, navigated_domain, top500_domains::kTop500Keywords, 500)) { url, navigated_domain, top500_domains::kTop500Keywords, 500)) {
std::move(callback).Run(security_state::SafetyTipStatus::kBadKeyword, if (!done_checking_reputation_status) {
IsIgnored(url), url, GURL()); result.safety_tip_status = security_state::SafetyTipStatus::kBadKeyword;
return; }
result.triggered_heuristics.keywords_heuristic_triggered = true;
done_checking_reputation_status = true;
} }
// TODO(crbug/984725): 6. Additional client-side heuristics. result.user_previously_ignored = IsIgnored(url);
std::move(callback).Run(security_state::SafetyTipStatus::kNone, result.url = url;
IsIgnored(url), url, GURL()); std::move(callback).Run(result);
} }
security_state::SafetyTipStatus GetSafetyTipUrlBlockType(const GURL& url) { security_state::SafetyTipStatus GetSafetyTipUrlBlockType(const GURL& url) {
......
...@@ -19,14 +19,33 @@ ...@@ -19,14 +19,33 @@
class Profile; class Profile;
struct DomainInfo; struct DomainInfo;
// Callback type used for retrieving reputation status. |ignored| indicates // All potential heuristics that can trigger. Used temporarily to keep track of
// whether the user has dismissed the warning and thus should not be warned // which heuristics trigger during a reputation check, and later used to decide
// again. |url| is the URL applicable for this result, // which metrics get recorded.
struct TriggeredHeuristics {
bool blocklist_heuristic_triggered = false;
bool lookalike_heuristic_triggered = false;
bool keywords_heuristic_triggered = false;
};
// Wrapper used to store the results of a reputation check. Specifically, this
// is passed to the callback given to GetReputationStatus.
// |user_previously_ignored| indicates whether the use has dismissed the warning
// previously, and thus should not be warned again. |url| is the URL applicable
// for this result.
struct ReputationCheckResult {
security_state::SafetyTipStatus safety_tip_status =
security_state::SafetyTipStatus::kNone;
bool user_previously_ignored = false;
GURL url;
GURL suggested_url;
TriggeredHeuristics triggered_heuristics;
};
// Callback type used for retrieving reputation status. The results of the
// reputation check are given in |result|.
using ReputationCheckCallback = using ReputationCheckCallback =
base::OnceCallback<void(security_state::SafetyTipStatus, base::OnceCallback<void(ReputationCheckResult result)>;
bool ignored,
const GURL& url,
const GURL& suggested_url)>;
// Provides reputation information on URLs for Safety Tips. // Provides reputation information on URLs for Safety Tips.
class ReputationService : public KeyedService { class ReputationService : public KeyedService {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/reputation/reputation_service.h"
#include "components/security_state/core/features.h" #include "components/security_state/core/features.h"
#include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_entry.h"
...@@ -135,22 +136,20 @@ void ReputationWebContentsObserver::MaybeShowSafetyTip() { ...@@ -135,22 +136,20 @@ void ReputationWebContentsObserver::MaybeShowSafetyTip() {
} }
void ReputationWebContentsObserver::HandleReputationCheckResult( void ReputationWebContentsObserver::HandleReputationCheckResult(
security_state::SafetyTipStatus safety_tip_status, ReputationCheckResult result) {
bool user_ignored,
const GURL& url,
const GURL& suggested_url) {
UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown", UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown",
safety_tip_status); result.safety_tip_status);
if (safety_tip_status == security_state::SafetyTipStatus::kNone || if (result.safety_tip_status == security_state::SafetyTipStatus::kNone ||
safety_tip_status == security_state::SafetyTipStatus::kBadKeyword) { result.safety_tip_status ==
security_state::SafetyTipStatus::kBadKeyword) {
MaybeCallReputationCheckCallback(); MaybeCallReputationCheckCallback();
return; return;
} }
if (user_ignored) { if (result.user_previously_ignored) {
UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipIgnoredPageLoad", UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipIgnoredPageLoad",
safety_tip_status); result.safety_tip_status);
MaybeCallReputationCheckCallback(); MaybeCallReputationCheckCallback();
return; return;
} }
...@@ -158,7 +157,8 @@ void ReputationWebContentsObserver::HandleReputationCheckResult( ...@@ -158,7 +157,8 @@ void ReputationWebContentsObserver::HandleReputationCheckResult(
// Set this field independent of whether the feature to show the UI is // Set this field independent of whether the feature to show the UI is
// enabled/disabled. Metrics code uses this field and we want to record // enabled/disabled. Metrics code uses this field and we want to record
// metrics regardless of the feature being enabled/disabled. // metrics regardless of the feature being enabled/disabled.
last_navigation_safety_tip_info_ = {safety_tip_status, suggested_url}; last_navigation_safety_tip_info_ = {result.safety_tip_status,
result.suggested_url};
// A navigation entry should always exist because reputation checks are only // A navigation entry should always exist because reputation checks are only
// triggered when a committed navigation finishes. // triggered when a committed navigation finishes.
...@@ -175,8 +175,10 @@ void ReputationWebContentsObserver::HandleReputationCheckResult( ...@@ -175,8 +175,10 @@ void ReputationWebContentsObserver::HandleReputationCheckResult(
return; return;
} }
ShowSafetyTipDialog( ShowSafetyTipDialog(
web_contents(), safety_tip_status, url, suggested_url, web_contents(), result.safety_tip_status, result.url,
base::BindOnce(OnSafetyTipClosed, safety_tip_status, base::Time::Now())); result.suggested_url,
base::BindOnce(OnSafetyTipClosed, result.safety_tip_status,
base::Time::Now()));
} }
void ReputationWebContentsObserver::MaybeCallReputationCheckCallback() { void ReputationWebContentsObserver::MaybeCallReputationCheckCallback() {
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
#ifndef CHROME_BROWSER_REPUTATION_REPUTATION_WEB_CONTENTS_OBSERVER_H_ #ifndef CHROME_BROWSER_REPUTATION_REPUTATION_WEB_CONTENTS_OBSERVER_H_
#define CHROME_BROWSER_REPUTATION_REPUTATION_WEB_CONTENTS_OBSERVER_H_ #define CHROME_BROWSER_REPUTATION_REPUTATION_WEB_CONTENTS_OBSERVER_H_
#include <set>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "chrome/browser/reputation/reputation_service.h" #include "chrome/browser/reputation/reputation_service.h"
#include "chrome/browser/reputation/safety_tip_ui.h" #include "chrome/browser/reputation/safety_tip_ui.h"
...@@ -53,11 +51,7 @@ class ReputationWebContentsObserver ...@@ -53,11 +51,7 @@ class ReputationWebContentsObserver
// A ReputationCheckCallback. Called by the reputation service when a // A ReputationCheckCallback. Called by the reputation service when a
// reputation result is available. // reputation result is available.
void HandleReputationCheckResult( void HandleReputationCheckResult(ReputationCheckResult result);
security_state::SafetyTipStatus safety_tip_status,
bool user_ignored,
const GURL& url,
const GURL& suggested_url);
// A helper method that calls and resets // A helper method that calls and resets
// |reputation_check_callback_for_testing_| if it is set. // |reputation_check_callback_for_testing_| if it is set.
......
...@@ -475,6 +475,7 @@ IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest, ...@@ -475,6 +475,7 @@ IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
if (ui_status() == UIStatus::kDisabled) { if (ui_status() == UIStatus::kDisabled) {
return; return;
} }
auto kNavigatedUrl = GetURL("site1.com"); auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment