AbstractOperations.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibURL/URL.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/DOMURL/DOMURL.h>
  10. #include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
  11. #include <LibWeb/Fetch/Infrastructure/URL.h>
  12. #include <LibWeb/HTML/Window.h>
  13. #include <LibWeb/ReferrerPolicy/AbstractOperations.h>
  14. #include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
  15. #include <LibWeb/SecureContexts/AbstractOperations.h>
  16. namespace Web::ReferrerPolicy {
  17. // https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
  18. ReferrerPolicy parse_a_referrer_policy_from_a_referrer_policy_header(Fetch::Infrastructure::Response const& response)
  19. {
  20. // 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list.
  21. auto policy_tokens_or_failure = Fetch::Infrastructure::extract_header_list_values("Referrer-Policy"sv.bytes(), response.header_list());
  22. auto policy_tokens = policy_tokens_or_failure.has<Vector<ByteBuffer>>() ? policy_tokens_or_failure.get<Vector<ByteBuffer>>() : Vector<ByteBuffer> {};
  23. // 2. Let policy be the empty string.
  24. auto policy = ReferrerPolicy::EmptyString;
  25. // 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty string, then set policy to token.
  26. for (auto token : policy_tokens) {
  27. auto referrer_policy = from_string(token);
  28. if (referrer_policy.has_value() && referrer_policy.value() != ReferrerPolicy::EmptyString)
  29. policy = referrer_policy.release_value();
  30. }
  31. // 4. Return policy.
  32. return policy;
  33. }
  34. // https://w3c.github.io/webappsec-referrer-policy/#set-requests-referrer-policy-on-redirect
  35. void set_request_referrer_policy_on_redirect(Fetch::Infrastructure::Request& request, Fetch::Infrastructure::Response const& response)
  36. {
  37. // 1. Let policy be the result of executing § 8.1 Parse a referrer policy from a Referrer-Policy header on
  38. // actualResponse.
  39. auto policy = parse_a_referrer_policy_from_a_referrer_policy_header(response);
  40. // 2. If policy is not the empty string, then set request’s referrer policy to policy.
  41. if (policy != ReferrerPolicy::EmptyString)
  42. request.set_referrer_policy(policy);
  43. }
  44. // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
  45. Optional<URL::URL> determine_requests_referrer(Fetch::Infrastructure::Request const& request)
  46. {
  47. // 1. Let policy be request’s referrer policy.
  48. auto const& policy = request.referrer_policy();
  49. // 2. Let environment be request’s client.
  50. auto environment = request.client();
  51. // 3. Switch on request’s referrer:
  52. auto referrer_source = request.referrer().visit(
  53. // "client"
  54. [&](Fetch::Infrastructure::Request::Referrer referrer) -> Optional<URL::URL> {
  55. // Note: If request’s referrer is "no-referrer", Fetch will not call into this algorithm.
  56. VERIFY(referrer == Fetch::Infrastructure::Request::Referrer::Client);
  57. // FIXME: Add a const global_object() getter to ESO
  58. auto& global_object = const_cast<HTML::EnvironmentSettingsObject&>(*environment).global_object();
  59. // 1. If environment’s global object is a Window object, then
  60. if (is<HTML::Window>(global_object)) {
  61. // 1. Let document be the associated Document of environment’s global object.
  62. auto const& document = static_cast<HTML::Window const&>(global_object).associated_document();
  63. // 2. If document’s origin is an opaque origin, return no referrer.
  64. if (document.origin().is_opaque())
  65. return {};
  66. // FIXME: 3. While document is an iframe srcdoc document, let document be document’s browsing context’s
  67. // browsing context container’s node document.
  68. // 4. Let referrerSource be document’s URL.
  69. return document.url();
  70. }
  71. // 2. Otherwise, let referrerSource be environment’s creation URL.
  72. else {
  73. return environment->creation_url;
  74. }
  75. },
  76. // a URL
  77. [&](URL::URL const& url) -> Optional<URL::URL> {
  78. // Let referrerSource be request’s referrer.
  79. return url;
  80. });
  81. // NOTE: This only happens in step 1.2. of the "client" case above.
  82. if (!referrer_source.has_value())
  83. return {};
  84. // 4. Let request’s referrerURL be the result of stripping referrerSource for use as a referrer.
  85. auto referrer_url = strip_url_for_use_as_referrer(referrer_source);
  86. // 5. Let referrerOrigin be the result of stripping referrerSource for use as a referrer, with the origin-only flag
  87. // set to true.
  88. auto referrer_origin = strip_url_for_use_as_referrer(referrer_source, OriginOnly::Yes);
  89. // 6. If the result of serializing referrerURL is a string whose length is greater than 4096, set referrerURL to
  90. // referrerOrigin.
  91. if (referrer_url.has_value() && referrer_url.value().serialize().length() > 4096)
  92. referrer_url = referrer_origin;
  93. // 7. The user agent MAY alter referrerURL or referrerOrigin at this point to enforce arbitrary policy
  94. // considerations in the interests of minimizing data leakage. For example, the user agent could strip the URL
  95. // down to an origin, modify its host, replace it with an empty string, etc.
  96. // 8. Execute the statements corresponding to the value of policy:
  97. // Note: If request’s referrer policy is the empty string, Fetch will not call into this algorithm.
  98. VERIFY(policy != ReferrerPolicy::EmptyString);
  99. switch (policy) {
  100. // "no-referrer"
  101. case ReferrerPolicy::NoReferrer:
  102. // Return no referrer
  103. return {};
  104. // "origin"
  105. case ReferrerPolicy::Origin:
  106. // Return referrerOrigin
  107. return referrer_origin;
  108. // "unsafe-url"
  109. case ReferrerPolicy::UnsafeURL:
  110. // Return referrerURL.
  111. return referrer_url;
  112. // "strict-origin"
  113. case ReferrerPolicy::StrictOrigin:
  114. // 1. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
  115. // trustworthy URL, then return no referrer.
  116. if (referrer_url.has_value()
  117. && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
  118. && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
  119. return {};
  120. }
  121. // 2. Return referrerOrigin.
  122. return referrer_origin;
  123. // "strict-origin-when-cross-origin"
  124. case ReferrerPolicy::StrictOriginWhenCrossOrigin:
  125. // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
  126. // referrerURL.
  127. if (referrer_url.has_value() && referrer_url->origin().is_same_origin(request.current_url().origin()))
  128. return referrer_url;
  129. // 2. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
  130. // trustworthy URL, then return no referrer.
  131. if (referrer_url.has_value()
  132. && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
  133. && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
  134. return {};
  135. }
  136. // 3. Return referrerOrigin.
  137. return referrer_origin;
  138. // "same-origin"
  139. case ReferrerPolicy::SameOrigin:
  140. // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
  141. // referrerURL.
  142. if (referrer_url.has_value()
  143. && referrer_url->origin().is_same_origin(request.current_url().origin())) {
  144. return referrer_url;
  145. }
  146. // 2. Return no referrer.
  147. return {};
  148. // "origin-when-cross-origin"
  149. case ReferrerPolicy::OriginWhenCrossOrigin:
  150. // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
  151. // referrerURL.
  152. if (referrer_url.has_value()
  153. && referrer_url->origin().is_same_origin(request.current_url().origin())) {
  154. return referrer_url;
  155. }
  156. // 2. Return referrerOrigin.
  157. return referrer_origin;
  158. // "no-referrer-when-downgrade"
  159. case ReferrerPolicy::NoReferrerWhenDowngrade:
  160. // 1. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
  161. // trustworthy URL, then return no referrer.
  162. if (referrer_url.has_value()
  163. && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
  164. && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
  165. return {};
  166. }
  167. // 2. Return referrerURL.
  168. return referrer_url;
  169. default:
  170. VERIFY_NOT_REACHED();
  171. }
  172. }
  173. // https://w3c.github.io/webappsec-referrer-policy/#strip-url
  174. Optional<URL::URL> strip_url_for_use_as_referrer(Optional<URL::URL> url, OriginOnly origin_only)
  175. {
  176. // 1. If url is null, return no referrer.
  177. if (!url.has_value())
  178. return {};
  179. // 2. If url’s scheme is a local scheme, then return no referrer.
  180. if (Fetch::Infrastructure::LOCAL_SCHEMES.span().contains_slow(url->scheme()))
  181. return {};
  182. // 3. Set url’s username to the empty string.
  183. url->set_username(""sv);
  184. // 4. Set url’s password to the empty string.
  185. url->set_password(""sv);
  186. // 5. Set url’s fragment to null.
  187. url->set_fragment({});
  188. // 6. If the origin-only flag is true, then:
  189. if (origin_only == OriginOnly::Yes) {
  190. // 1. Set url’s path to « the empty string ».
  191. url->set_paths({ ""sv });
  192. // 2. Set url’s query to null.
  193. url->set_query({});
  194. }
  195. // 7. Return url.
  196. return url;
  197. }
  198. }