AbstractOperations.cpp 8.3 KB

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