url_request_rewrite_rules_validation.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright 2021 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/url_rewrite/browser/url_request_rewrite_rules_validation.h"
  5. #include "base/ranges/algorithm.h"
  6. #include "base/strings/strcat.h"
  7. #include "net/http/http_util.h"
  8. #include "url/url_constants.h"
  9. namespace url_rewrite {
  10. namespace {
  11. bool IsValidUrlHost(base::StringPiece host) {
  12. return GURL(base::StrCat({url::kHttpScheme, "://", host})).is_valid();
  13. }
  14. bool ValidateAddHeaders(
  15. const mojom::UrlRequestRewriteAddHeadersPtr& add_headers) {
  16. if (!add_headers || add_headers->headers.empty()) {
  17. LOG(ERROR) << "Add headers is missing";
  18. return false;
  19. }
  20. return base::ranges::all_of(
  21. add_headers->headers, [](const mojom::UrlHeaderPtr& header) {
  22. if (!net::HttpUtil::IsValidHeaderName(header->name)) {
  23. LOG(ERROR) << "Invalid header name: " << header->name;
  24. return false;
  25. }
  26. if (!net::HttpUtil::IsValidHeaderValue(header->value)) {
  27. LOG(ERROR) << "Invalid header value: " << header->value;
  28. return false;
  29. }
  30. return true;
  31. });
  32. }
  33. bool ValidateRemoveHeader(
  34. const mojom::UrlRequestRewriteRemoveHeaderPtr& remove_header) {
  35. if (!remove_header) {
  36. LOG(ERROR) << "Remove headers is missing";
  37. return false;
  38. }
  39. if (!net::HttpUtil::IsValidHeaderName(remove_header->header_name)) {
  40. LOG(ERROR) << "Invalid header name: " << remove_header->header_name;
  41. return false;
  42. }
  43. return true;
  44. }
  45. bool ValidateSubstituteQueryPattern(
  46. const mojom::UrlRequestRewriteSubstituteQueryPatternPtr&
  47. substitute_query_pattern) {
  48. if (!substitute_query_pattern) {
  49. LOG(ERROR) << "Substitute query pattern is missing";
  50. return false;
  51. }
  52. if (substitute_query_pattern->pattern.empty()) {
  53. LOG(ERROR) << "Substitute query pattern is empty";
  54. return false;
  55. }
  56. return true;
  57. }
  58. bool ValidateReplaceUrl(
  59. const mojom::UrlRequestRewriteReplaceUrlPtr& replace_url) {
  60. if (!replace_url) {
  61. LOG(ERROR) << "Replace url is missing";
  62. return false;
  63. }
  64. if (!GURL("http://site.com/" + replace_url->url_ends_with).is_valid()) {
  65. LOG(ERROR) << "url_ends_with is not valid: " << replace_url->url_ends_with;
  66. return false;
  67. }
  68. if (!replace_url->new_url.is_valid()) {
  69. LOG(ERROR) << "new_url is not valid: " << replace_url->new_url;
  70. return false;
  71. }
  72. return true;
  73. }
  74. bool ValidateAppendToQuery(
  75. const mojom::UrlRequestRewriteAppendToQueryPtr& append_to_query) {
  76. if (!append_to_query) {
  77. LOG(ERROR) << "Append to query is missing";
  78. return false;
  79. }
  80. if (append_to_query->query.empty()) {
  81. LOG(ERROR) << "Append to query is empty";
  82. return false;
  83. }
  84. return true;
  85. }
  86. bool ValidateRewrite(const mojom::UrlRequestActionPtr& action) {
  87. switch (action->which()) {
  88. case mojom::UrlRequestAction::Tag::kAddHeaders:
  89. return ValidateAddHeaders(action->get_add_headers());
  90. case mojom::UrlRequestAction::Tag::kRemoveHeader:
  91. return ValidateRemoveHeader(action->get_remove_header());
  92. case mojom::UrlRequestAction::Tag::kSubstituteQueryPattern:
  93. return ValidateSubstituteQueryPattern(
  94. action->get_substitute_query_pattern());
  95. case mojom::UrlRequestAction::Tag::kReplaceUrl:
  96. return ValidateReplaceUrl(action->get_replace_url());
  97. case mojom::UrlRequestAction::Tag::kAppendToQuery:
  98. return ValidateAppendToQuery(action->get_append_to_query());
  99. case mojom::UrlRequestAction::Tag::kPolicy:
  100. return true;
  101. }
  102. }
  103. } // namespace
  104. bool ValidateRules(const mojom::UrlRequestRewriteRules* rules) {
  105. static constexpr base::StringPiece kWildcard("*.");
  106. if (!rules)
  107. return false;
  108. for (const auto& rule : rules->rules) {
  109. if (!rule)
  110. return false;
  111. if (rule->hosts_filter) {
  112. if (rule->hosts_filter->empty()) {
  113. LOG(ERROR) << "Hosts filter is empty";
  114. return false;
  115. }
  116. for (const base::StringPiece host : *rule->hosts_filter) {
  117. if (base::StartsWith(host, kWildcard, base::CompareCase::SENSITIVE)) {
  118. if (!IsValidUrlHost(host.substr(2))) {
  119. LOG(ERROR) << "Host filter is not valid: " << host;
  120. return false;
  121. }
  122. } else {
  123. if (!IsValidUrlHost(host)) {
  124. LOG(ERROR) << "Host filter is not valid: " << host;
  125. return false;
  126. }
  127. }
  128. }
  129. }
  130. if (rule->schemes_filter && rule->schemes_filter->empty()) {
  131. LOG(ERROR) << "Schemes filter is empty";
  132. return false;
  133. }
  134. if (rule->actions.empty()) {
  135. // No rewrites, no action = no point!
  136. LOG(ERROR) << "Actions are empty";
  137. return false;
  138. }
  139. for (const auto& action : rule->actions) {
  140. if (!action)
  141. return false;
  142. if (action->is_policy() && rule->actions.size() > 1)
  143. // |policy| cannot be combined with other rewrites.
  144. return false;
  145. if (!ValidateRewrite(action))
  146. return false;
  147. }
  148. }
  149. return true;
  150. }
  151. } // namespace url_rewrite