safety_tips_config.cc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/reputation/core/safety_tips_config.h"
  5. #include "base/no_destructor.h"
  6. #include "base/ranges/algorithm.h"
  7. #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
  8. #include "third_party/re2/src/re2/re2.h"
  9. #include "url/gurl.h"
  10. using safe_browsing::V4ProtocolManagerUtil;
  11. namespace reputation {
  12. namespace {
  13. class SafetyTipsConfigSingleton {
  14. public:
  15. void SetProto(std::unique_ptr<SafetyTipsConfig> proto) {
  16. proto_ = std::move(proto);
  17. }
  18. SafetyTipsConfig* GetProto() const { return proto_.get(); }
  19. static SafetyTipsConfigSingleton& GetInstance() {
  20. static base::NoDestructor<SafetyTipsConfigSingleton> instance;
  21. return *instance;
  22. }
  23. private:
  24. std::unique_ptr<SafetyTipsConfig> proto_;
  25. };
  26. // Given a URL, generates all possible variant URLs to check the blocklist for.
  27. // This is conceptually almost identical to safe_browsing::UrlToFullHashes, but
  28. // without the hashing step.
  29. //
  30. // Note: Blocking "a.b/c/" does NOT block http://a.b/c without the trailing /.
  31. void UrlToSafetyTipPatterns(const GURL& url,
  32. std::vector<std::string>* patterns) {
  33. std::string canon_host;
  34. std::string canon_path;
  35. std::string canon_query;
  36. V4ProtocolManagerUtil::CanonicalizeUrl(url, &canon_host, &canon_path,
  37. &canon_query);
  38. std::vector<std::string> hosts;
  39. if (url.HostIsIPAddress()) {
  40. hosts.push_back(url.host());
  41. } else {
  42. V4ProtocolManagerUtil::GenerateHostVariantsToCheck(canon_host, &hosts);
  43. }
  44. std::vector<std::string> paths;
  45. V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canon_path, canon_query,
  46. &paths);
  47. for (const std::string& host : hosts) {
  48. for (const std::string& path : paths) {
  49. DCHECK(path.length() == 0 || path[0] == '/');
  50. patterns->push_back(host + path);
  51. }
  52. }
  53. }
  54. security_state::SafetyTipStatus FlagTypeToSafetyTipStatus(
  55. FlaggedPage::FlagType type) {
  56. switch (type) {
  57. case FlaggedPage::FlagType::FlaggedPage_FlagType_UNKNOWN:
  58. case FlaggedPage::FlagType::FlaggedPage_FlagType_YOUNG_DOMAIN:
  59. // Reached if component includes these flags, which might happen to
  60. // support newer Chrome releases.
  61. return security_state::SafetyTipStatus::kNone;
  62. case FlaggedPage::FlagType::FlaggedPage_FlagType_BAD_REP:
  63. return security_state::SafetyTipStatus::kBadReputation;
  64. }
  65. NOTREACHED();
  66. return security_state::SafetyTipStatus::kNone;
  67. }
  68. // Return whether |canonical_url| is a member of the designated cohort.
  69. bool IsUrlAllowedByCohort(const SafetyTipsConfig* proto,
  70. const GURL& canonical_url,
  71. unsigned cohort_index) {
  72. DCHECK(proto);
  73. DCHECK(canonical_url.is_valid());
  74. // Ensure that the cohort index is valid before using it. If it isn't valid,
  75. // we just pretend the cohort didn't include the canonical URL.
  76. if (cohort_index >= static_cast<unsigned>(proto->cohort_size())) {
  77. return false;
  78. }
  79. const auto& cohort = proto->cohort(cohort_index);
  80. // For each possible URL pattern, see if any of the indicated allowed_index or
  81. // canonical_index entries correspond to a matching pattern since both sets of
  82. // indices are considered valid spoof targets.
  83. std::vector<std::string> patterns;
  84. UrlToSafetyTipPatterns(canonical_url, &patterns);
  85. for (const auto& search_pattern : patterns) {
  86. for (const unsigned allowed_index : cohort.allowed_index()) {
  87. // Skip over invalid indices.
  88. if (allowed_index >=
  89. static_cast<unsigned>(proto->allowed_pattern_size())) {
  90. continue;
  91. }
  92. const auto& pattern = proto->allowed_pattern(allowed_index).pattern();
  93. if (pattern == search_pattern) {
  94. return true;
  95. }
  96. }
  97. for (const unsigned canonical_index : cohort.canonical_index()) {
  98. // Skip over invalid indices.
  99. if (canonical_index >=
  100. static_cast<unsigned>(proto->canonical_pattern_size())) {
  101. continue;
  102. }
  103. const auto& pattern = proto->canonical_pattern(canonical_index).pattern();
  104. if (pattern == search_pattern) {
  105. return true;
  106. }
  107. }
  108. }
  109. return false;
  110. }
  111. } // namespace
  112. // static
  113. void SetSafetyTipsRemoteConfigProto(std::unique_ptr<SafetyTipsConfig> proto) {
  114. SafetyTipsConfigSingleton::GetInstance().SetProto(std::move(proto));
  115. }
  116. // static
  117. const SafetyTipsConfig* GetSafetyTipsRemoteConfigProto() {
  118. return SafetyTipsConfigSingleton::GetInstance().GetProto();
  119. }
  120. bool IsUrlAllowlistedBySafetyTipsComponent(const SafetyTipsConfig* proto,
  121. const GURL& visited_url,
  122. const GURL& canonical_url) {
  123. DCHECK(proto);
  124. DCHECK(visited_url.is_valid());
  125. std::vector<std::string> patterns;
  126. UrlToSafetyTipPatterns(visited_url, &patterns);
  127. auto allowed_patterns = proto->allowed_pattern();
  128. for (const auto& pattern : patterns) {
  129. UrlPattern search_target;
  130. search_target.set_pattern(pattern);
  131. auto maybe_before = std::lower_bound(
  132. allowed_patterns.begin(), allowed_patterns.end(), search_target,
  133. [](const UrlPattern& a, const UrlPattern& b) -> bool {
  134. return a.pattern() < b.pattern();
  135. });
  136. if (maybe_before != allowed_patterns.end() &&
  137. pattern == maybe_before->pattern()) {
  138. // If no cohorts are given, it's a universal allowlist entry.
  139. if (maybe_before->cohort_index_size() == 0) {
  140. return true;
  141. }
  142. for (const unsigned cohort_index : maybe_before->cohort_index()) {
  143. if (IsUrlAllowedByCohort(proto, canonical_url, cohort_index)) {
  144. return true;
  145. }
  146. }
  147. }
  148. }
  149. return false;
  150. }
  151. bool IsTargetHostAllowlistedBySafetyTipsComponent(const SafetyTipsConfig* proto,
  152. const std::string& hostname) {
  153. DCHECK(!hostname.empty());
  154. if (proto == nullptr) {
  155. return false;
  156. }
  157. for (const auto& host_pattern : proto->allowed_target_pattern()) {
  158. if (!host_pattern.has_regex()) {
  159. continue;
  160. }
  161. DCHECK(!host_pattern.regex().empty());
  162. const re2::RE2 regex(host_pattern.regex());
  163. DCHECK(regex.ok());
  164. if (re2::RE2::FullMatch(hostname, regex)) {
  165. return true;
  166. }
  167. }
  168. return false;
  169. }
  170. security_state::SafetyTipStatus GetSafetyTipUrlBlockType(const GURL& url) {
  171. auto* proto = GetSafetyTipsRemoteConfigProto();
  172. if (!proto) {
  173. return security_state::SafetyTipStatus::kNone;
  174. }
  175. std::vector<std::string> patterns;
  176. UrlToSafetyTipPatterns(url, &patterns);
  177. auto flagged_pages = proto->flagged_page();
  178. for (const auto& pattern : patterns) {
  179. FlaggedPage search_target;
  180. search_target.set_pattern(pattern);
  181. auto lower = std::lower_bound(
  182. flagged_pages.begin(), flagged_pages.end(), search_target,
  183. [](const FlaggedPage& a, const FlaggedPage& b) -> bool {
  184. return a.pattern() < b.pattern();
  185. });
  186. while (lower != flagged_pages.end() && pattern == lower->pattern()) {
  187. // Skip over sites with unexpected flag types and keep looking for other
  188. // matches. This allows components to include flag types not handled by
  189. // this release.
  190. auto type = FlagTypeToSafetyTipStatus(lower->type());
  191. if (type != security_state::SafetyTipStatus::kNone) {
  192. return type;
  193. }
  194. ++lower;
  195. }
  196. }
  197. return security_state::SafetyTipStatus::kNone;
  198. }
  199. bool IsCommonWordInConfigProto(const SafetyTipsConfig* proto,
  200. const std::string& word) {
  201. // proto is nullptr when running in non-Lookalike tests.
  202. if (proto == nullptr) {
  203. return false;
  204. }
  205. auto common_words = proto->common_word();
  206. DCHECK(base::ranges::is_sorted(common_words.begin(), common_words.end()));
  207. auto lower = std::lower_bound(
  208. common_words.begin(), common_words.end(), word,
  209. [](const std::string& a, const std::string& b) -> bool { return a < b; });
  210. return lower != common_words.end() && word == *lower;
  211. }
  212. } // namespace reputation