| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_LOOKALIKES_CORE_LOOKALIKE_URL_UTIL_H_ |
| #define COMPONENTS_LOOKALIKES_CORE_LOOKALIKE_URL_UTIL_H_ |
| |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/memory/raw_span.h" |
| #include "components/lookalikes/core/safety_tips.pb.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/url_formatter/url_formatter.h" |
| #include "components/version_info/channel.h" |
| #include "url/gurl.h" |
| |
| namespace lookalikes { |
| |
| // Name of the histograms recorded by the interstitial for lookalike match |
| // types. |
| extern const char kInterstitialHistogramName[]; |
| extern const char kIncognitoInterstitialHistogramName[]; |
| |
| // Register applicable preferences with the provided registry. |
| void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); |
| |
| // Returns the console message to be shown in devtools when a URL is flagged by |
| // a lookalike heuristic. If is_new_heuristic is true, the message is for a new |
| // heuristic that's not fully launched and it has an extra line about future |
| // behavior of Chrome. |
| std::string GetConsoleMessage(const GURL& lookalike_url, bool is_new_heuristic); |
| |
| // Used for |GetTargetEmbeddingType| return value. It shows if the target |
| // embedding triggers on the input domain, and if it does, what type of warning |
| // should be shown to the user. |
| enum class TargetEmbeddingType { |
| kNone = 0, |
| kInterstitial = 1, |
| kSafetyTip = 2, |
| }; |
| |
| // Used for |GetComboSquattingType| return value. |
| // It shows if the brand name in the flagged domain |
| // comes from the hard-coded brand names or from site engagements. |
| enum class ComboSquattingType { |
| kNone = 0, |
| kHardCoded = 1, |
| kSiteEngagement = 2, |
| }; |
| |
| // Used for UKM. There is only a single LookalikeUrlMatchType per navigation. |
| enum class LookalikeUrlMatchType { |
| kNone = 0, |
| // DEPRECATED: Use kSkeletonMatchTop500 or kSkeletonMatchTop5k. |
| // kTopSite = 1, |
| kSkeletonMatchSiteEngagement = 2, |
| kEditDistance = 3, |
| kEditDistanceSiteEngagement = 4, |
| kTargetEmbedding = 5, |
| kSkeletonMatchTop500 = 6, |
| kSkeletonMatchTop5k = 7, |
| kTargetEmbeddingForSafetyTips = 8, |
| |
| // The domain name failed IDN spoof checks but didn't match a safe hostname. |
| // As a result, there is no URL to suggest to the user in the form of "Did |
| // you mean <url>?". |
| kFailedSpoofChecks = 9, |
| |
| kCharacterSwapSiteEngagement = 10, |
| kCharacterSwapTop500 = 11, |
| |
| // Combo Squatting uses manually |
| // curated lists of hard-coded keywords (kPopularKeywordsforCSQ in |
| // lookalike_url_util.cc) and both manually curated hard-coded brand names |
| // (kBrandNamesforCSQ in lookalike_url_util.cc) and brand names from |
| // SiteEngagement to flag domains. |
| kComboSquatting = 12, |
| kComboSquattingSiteEngagement = 13, |
| |
| // Append new items to the end of the list above; do not modify or replace |
| // existing values. Comment out obsolete items. |
| kMaxValue = kComboSquattingSiteEngagement, |
| }; |
| |
| // Used for UKM. There is only a single LookalikeUrlBlockingPageUserAction per |
| // navigation. |
| enum class LookalikeUrlBlockingPageUserAction { |
| kInterstitialNotShown = 0, |
| kClickThrough = 1, |
| kAcceptSuggestion = 2, |
| kCloseOrBack = 3, |
| |
| // Append new items to the end of the list above; do not modify or replace |
| // existing values. Comment out obsolete items. |
| kMaxValue = kCloseOrBack, |
| }; |
| |
| // Used for metrics. Multiple events can occur per navigation. |
| enum class NavigationSuggestionEvent { |
| kNone = 0, |
| // Interstitial results recorded using security_interstitials::MetricsHelper |
| // kInfobarShown = 1, |
| // kLinkClicked = 2, |
| // DEPRECATED: Use kMatchSkeletonTop500 or kMatchSkeletonTop5k. |
| // kMatchTopSite = 3, |
| kMatchSiteEngagement = 4, |
| kMatchEditDistance = 5, |
| kMatchEditDistanceSiteEngagement = 6, |
| kMatchTargetEmbedding = 7, |
| kMatchSkeletonTop500 = 8, |
| kMatchSkeletonTop5k = 9, |
| kMatchTargetEmbeddingForSafetyTips = 10, |
| kFailedSpoofChecks = 11, |
| kMatchCharacterSwapSiteEngagement = 12, |
| kMatchCharacterSwapTop500 = 13, |
| kComboSquatting = 14, |
| kComboSquattingSiteEngagement = 15, |
| |
| // Append new items to the end of the list above; do not modify or |
| // replace existing values. Comment out obsolete items. |
| kMaxValue = kComboSquattingSiteEngagement, |
| }; |
| |
| struct TopBucketDomainsParams { |
| // Skeletons of top bucket domains. This is the top 500 or 1000 most popular |
| // domains (though, there can be fewer than 500 or 1000 skeletons in this |
| // array). |
| // This field is not a raw_ptr<> because it only ever points to statically- |
| // allocated memory which is never freed, so it cannot dangle. |
| // Spanification note: unlike the raw_spans below, the pointed-to arrays |
| // are defined in generated files, so their size isn't exposed in the header. |
| // To get compile-time safety guarantees, the header itself would have to be |
| // generated as well. |
| RAW_PTR_EXCLUSION const char* const* edit_distance_skeletons; |
| // Number of skeletons in `edit_distance_skeletons`. |
| size_t num_edit_distance_skeletons; |
| }; |
| |
| struct ComboSquattingParams { |
| // An array of brand names (such as "google", "youtube") and their skeletons |
| // (in pairs). The first item in each pair is the brand name and the second |
| // item is its skeleton. Brand names should be usable in domain names (i.e. |
| // lower case, no punctuation except for - etc.) |
| base::raw_span<const std::string_view[2]> brand_names; |
| |
| // List of popular keywords such as "login", "online". |
| base::raw_span<const std::string_view> popular_keywords; |
| |
| // Needed to provide a non-trivial destructor so NoDestructor can be used. |
| ~ComboSquattingParams() {} |
| }; |
| |
| struct DomainInfo { |
| // The full ASCII hostname, used in detecting target embedding. For |
| // "https://www.google.com/mail" this will be "www.google.com". |
| const std::string hostname; |
| // eTLD+1, used for skeleton and edit distance comparison. Must be ASCII. |
| // Empty for non-unique domains, localhost or sites whose eTLD+1 is empty. |
| const std::string domain_and_registry; |
| // eTLD+1 without the registry part, and with a trailing period. For |
| // "www.google.com", this will be "google.". Used for edit distance |
| // comparisons. Empty for non-unique domains, localhost or sites whose eTLD+1 |
| // is empty. |
| const std::string domain_without_registry; |
| |
| // Result of IDN conversion of domain_and_registry field. |
| const url_formatter::IDNConversionResult idn_result; |
| // Skeletons of domain_and_registry field. |
| const url_formatter::Skeletons skeletons; |
| // Skeletons of domain_without_registry field. |
| const url_formatter::Skeletons domain_without_registry_skeletons; |
| |
| DomainInfo( |
| const std::string& arg_hostname, |
| const std::string& arg_domain_and_registry, |
| const std::string& arg_domain_without_registry, |
| const url_formatter::IDNConversionResult& arg_idn_result, |
| const url_formatter::Skeletons& arg_skeletons, |
| const url_formatter::Skeletons& arg_domain_without_registry_skeletons); |
| ~DomainInfo(); |
| DomainInfo(const DomainInfo& other); |
| }; |
| |
| // Returns a DomainInfo instance computed from |hostname|. Will return empty |
| // fields for non-unique hostnames (e.g. site.test), localhost or sites whose |
| // eTLD+1 is empty. |
| DomainInfo GetDomainInfo(const std::string& hostname); |
| |
| // Convenience function for returning GetDomainInfo(url.host()). |
| DomainInfo GetDomainInfo(const GURL& url); |
| |
| // Returns true if the Levenshtein distance between |str1| and |str2| is at most |
| // one. This has O(max(n,m)) complexity as opposed to O(n*m) of the usual edit |
| // distance computation. |
| bool IsEditDistanceAtMostOne(const std::u16string& str1, |
| const std::u16string& str2); |
| |
| // Returns whether |navigated_domain| and |matched_domain| are likely to be edit |
| // distance false positives, and thus the user should *not* be warned. |
| // |
| // Assumes |navigated_domain| and |matched_domain| are edit distance of 1 apart. |
| bool IsLikelyEditDistanceFalsePositive(const DomainInfo& navigated_domain, |
| const DomainInfo& matched_domain); |
| |
| // Returns whether |navigated_domain| and |matched_domain| are likely to be |
| // character swap false positives, and thus the user should *not* be warned. |
| // |
| // Assumes |navigated_domain| and |matched_domain| are within 1 character swap. |
| bool IsLikelyCharacterSwapFalsePositive(const DomainInfo& navigated_domain, |
| const DomainInfo& matched_domain); |
| |
| // Returns eTLD+1 of |hostname|. This excludes private registries, and returns |
| // "blogspot.com" for "test.blogspot.com" (blogspot.com is listed as a private |
| // registry). We do this to be consistent with url_formatter's top domain list |
| // which doesn't have a notion of private registries. |
| std::string GetETLDPlusOne(const std::string& hostname); |
| |
| // Records an interstitial histogram entry for the given match type. |
| void RecordUMAFromMatchType(LookalikeUrlMatchType match_type, |
| bool is_incognito); |
| |
| using LookalikeTargetAllowlistChecker = |
| base::RepeatingCallback<bool(const std::string&)>; |
| |
| // Returns true if a domain is visually similar to the hostname of |url|. The |
| // matching domain can be a top domain or an engaged site. Similarity |
| // check is made using both visual skeleton and edit distance comparison. If |
| // this returns true, match details will be written into |matched_domain|. |
| // Pointer arguments can't be nullptr. |
| bool GetMatchingDomain( |
| const DomainInfo& navigated_domain, |
| const std::vector<DomainInfo>& engaged_sites, |
| const LookalikeTargetAllowlistChecker& in_target_allowlist, |
| const reputation::SafetyTipsConfig* config_proto, |
| std::string* matched_domain, |
| LookalikeUrlMatchType* match_type); |
| |
| // Checks to see if a URL is a target embedding lookalike. This function sets |
| // |safe_hostname| to the url of the embedded target domain. See the unit tests |
| // for what qualifies as target embedding. |
| TargetEmbeddingType GetTargetEmbeddingType( |
| const std::string& hostname, |
| const std::vector<DomainInfo>& engaged_sites, |
| const LookalikeTargetAllowlistChecker& in_target_allowlist, |
| const reputation::SafetyTipsConfig* config_proto, |
| std::string* safe_hostname); |
| |
| // Same as GetTargetEmbeddingType, but explicitly state whether or not a safety |
| // tip is permitted via |safety_tips_allowed|. Safety tips are presently only |
| // used for tail embedding (e.g. "evil-google.com"). This function may return |
| // kSafetyTip preferentially to kInterstitial -- call with !safety_tips_allowed |
| // if you're interested in determining if there's *also* an interstitial. |
| TargetEmbeddingType SearchForEmbeddings( |
| const std::string& hostname, |
| const std::vector<DomainInfo>& engaged_sites, |
| const LookalikeTargetAllowlistChecker& in_target_allowlist, |
| const reputation::SafetyTipsConfig* config_proto, |
| bool safety_tips_allowed, |
| std::string* safe_hostname); |
| |
| // Returns true if a navigation to an IDN should be blocked. |
| bool ShouldBlockBySpoofCheckResult(const DomainInfo& navigated_domain); |
| |
| // Checks whether the given url is allowlisted by enterprise policy, and |
| // thus no warnings should be shown on that host. |
| bool IsAllowedByEnterprisePolicy(const PrefService* pref_service, |
| const GURL& url); |
| |
| // Add the given hosts to the allowlist policy setting. |
| void SetEnterpriseAllowlistForTesting(PrefService* pref_service, |
| const std::vector<std::string>& hosts); |
| |
| // Returns true if |str1| and |str2| are identical except that two adjacent |
| // characters are swapped. E.g. example.com vs exapmle.com. |
| bool HasOneCharacterSwap(const std::u16string& str1, |
| const std::u16string& str2); |
| |
| // Sets information about top bucket domains for testing. |
| void SetTopBucketDomainsParamsForTesting(const TopBucketDomainsParams& params); |
| // Resets information about top bucket domains for testing. |
| void ResetTopBucketDomainsParamsForTesting(); |
| |
| // Returns true if the launch configuration provided by the component updater |
| // enables `heuristic` for the given `etld_plus_one`. |
| bool IsHeuristicEnabledForHostname( |
| const reputation::SafetyTipsConfig* config_proto, |
| reputation::HeuristicLaunchConfig::Heuristic heuristic, |
| const std::string& lookalike_etld_plus_one, |
| version_info::Channel channel); |
| |
| // Set brand names and keywords for testing Combo Squatting heuristic. |
| void SetComboSquattingParamsForTesting(const ComboSquattingParams& params); |
| |
| // Reset brand names and keywords after testing Combo Squatting heuristic. |
| void ResetComboSquattingParamsForTesting(); |
| |
| // Check if |navigated_domain| is Combo Squatting lookalike. |
| // It gets |engaged_sites| to use its brand names in addition to hard coded |
| // brand names. The function sets |matched_domain| to suggest to the user |
| // instead of the Combo Squatting domain. |
| ComboSquattingType GetComboSquattingType( |
| const DomainInfo& navigated_domain, |
| const std::vector<DomainInfo>& engaged_sites, |
| std::string* matched_domain); |
| |
| // Returns true if `etld_plus_one` has a TLD that's considered safe for |
| // lookalike checks, such as government sites. |
| bool IsSafeTLD(const std::string& hostname); |
| |
| // The action to take for a given lookalike match. |
| enum class LookalikeActionType { |
| // No action. |
| kNone, |
| // Only record metrics, don't show any UI warnings. |
| kRecordMetrics, |
| // Show a safety tip. |
| kShowSafetyTip, |
| // Show an interstitial. |
| kShowInterstitial, |
| }; |
| |
| // Returns the action to take for the given `etld_plus_one` and lookalike |
| // `match_type`. Uses `config` to check whether the heuristic UI is enabled |
| // via gradual rollout. |
| LookalikeActionType GetActionForMatchType( |
| const reputation::SafetyTipsConfig* config, |
| version_info::Channel channel, |
| const std::string& etld_plus_one, |
| LookalikeUrlMatchType match_type); |
| |
| // Returns the suggested URL for the given parameters. Returns an https URL for |
| // top domain matches because it's more likely for top sites to support https. |
| GURL GetSuggestedURL(LookalikeUrlMatchType match_type, |
| const GURL& navigated_url, |
| const std::string& matched_hostname); |
| |
| } // namespace lookalikes |
| |
| #endif // COMPONENTS_LOOKALIKES_CORE_LOOKALIKE_URL_UTIL_H_ |