Преглед на файлове

LibWeb: Implement 'Determine request’s Referrer' AO

Linus Groh преди 2 години
родител
ревизия
1e1fbb93fb
променени са 2 файла, в които са добавени 154 реда и са изтрити 0 реда
  1. 152 0
      Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp
  2. 2 0
      Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h

+ 152 - 0
Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp

@@ -6,12 +6,164 @@
 
 #include <AK/URL.h>
 #include <LibWeb/DOM/Document.h>
+#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
 #include <LibWeb/Fetch/Infrastructure/URL.h>
+#include <LibWeb/HTML/Window.h>
 #include <LibWeb/ReferrerPolicy/AbstractOperations.h>
+#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
+#include <LibWeb/SecureContexts/AbstractOperations.h>
 #include <LibWeb/URL/URL.h>
 
 namespace Web::ReferrerPolicy {
 
+// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
+Optional<AK::URL> determine_requests_referrer(Fetch::Infrastructure::Request const& request)
+{
+    // 1. Let policy be request’s referrer policy.
+    auto const& policy = request.referrer_policy();
+
+    // 2. Let environment be request’s client.
+    auto* environment = request.client();
+
+    // 3. Switch on request’s referrer:
+    auto referrer_source = request.referrer().visit(
+        // "client"
+        [&](Fetch::Infrastructure::Request::Referrer referrer) -> Optional<AK::URL> {
+            // Note: If request’s referrer is "no-referrer", Fetch will not call into this algorithm.
+            VERIFY(referrer == Fetch::Infrastructure::Request::Referrer::Client);
+
+            // FIXME: Add a const global_object() getter to ESO
+            auto& global_object = const_cast<HTML::EnvironmentSettingsObject*>(environment)->global_object();
+
+            // 1. If environment’s global object is a Window object, then
+            if (is<HTML::Window>(global_object)) {
+                // 1. Let document be the associated Document of environment’s global object.
+                auto const& document = static_cast<HTML::Window const&>(global_object).associated_document();
+
+                // 2. If document’s origin is an opaque origin, return no referrer.
+                if (document.origin().is_opaque())
+                    return {};
+
+                // FIXME: 3. While document is an iframe srcdoc document, let document be document’s browsing context’s
+                //           browsing context container’s node document.
+
+                // 4. Let referrerSource be document’s URL.
+                return document.url();
+            }
+            // 2. Otherwise, let referrerSource be environment’s creation URL.
+            else {
+                return environment->creation_url;
+            }
+        },
+        // a URL
+        [&](AK::URL const& url) -> Optional<AK::URL> {
+            // Let referrerSource be request’s referrer.
+            return url;
+        });
+    // NOTE: This only happens in step 1.2. of the "client" case above.
+    if (!referrer_source.has_value())
+        return {};
+
+    // 4. Let request’s referrerURL be the result of stripping referrerSource for use as a referrer.
+    auto referrer_url = strip_url_for_use_as_referrer(referrer_source);
+
+    // 5. Let referrerOrigin be the result of stripping referrerSource for use as a referrer, with the origin-only flag
+    //    set to true.
+    auto referrer_origin = strip_url_for_use_as_referrer(referrer_source, OriginOnly::Yes);
+
+    // 6. If the result of serializing referrerURL is a string whose length is greater than 4096, set referrerURL to
+    //    referrerOrigin.
+    if (referrer_url.has_value() && referrer_url.value().serialize().length() > 4096)
+        referrer_url = referrer_origin;
+
+    // 7. The user agent MAY alter referrerURL or referrerOrigin at this point to enforce arbitrary policy
+    //    considerations in the interests of minimizing data leakage. For example, the user agent could strip the URL
+    //    down to an origin, modify its host, replace it with an empty string, etc.
+
+    // 8. Execute the statements corresponding to the value of policy:
+    // Note: If request’s referrer policy is the empty string, Fetch will not call into this algorithm.
+    VERIFY(policy.has_value());
+    switch (*policy) {
+    // "no-referrer"
+    case ReferrerPolicy::NoReferrer:
+        // Return no referrer
+        return {};
+    // "origin"
+    case ReferrerPolicy::Origin:
+        // Return referrerOrigin
+        return referrer_origin;
+    // "unsafe-url"
+    case ReferrerPolicy::UnsafeURL:
+        // Return referrerURL.
+        return referrer_url;
+    // "strict-origin"
+    case ReferrerPolicy::StrictOrigin:
+        // 1. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
+        //    trustworthy URL, then return no referrer.
+        if (referrer_url.has_value()
+            && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
+            && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
+            return {};
+        }
+
+        // 2. Return referrerOrigin.
+        return referrer_origin;
+    // "strict-origin-when-cross-origin"
+    case ReferrerPolicy::StrictOriginWhenCrossOrigin:
+        // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
+        //    referrerURL.
+        if (referrer_url.has_value() && URL::url_origin(*referrer_url).is_same_origin(URL::url_origin(request.current_url())))
+            return referrer_url;
+
+        // 2. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
+        //    trustworthy URL, then return no referrer.
+        if (referrer_url.has_value()
+            && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
+            && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
+            return {};
+        }
+
+        // 3. Return referrerOrigin.
+        return referrer_origin;
+    // "same-origin"
+    case ReferrerPolicy::SameOrigin:
+        // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
+        //    referrerURL.
+        if (referrer_url.has_value()
+            && URL::url_origin(*referrer_url).is_same_origin(URL::url_origin(request.current_url()))) {
+            return referrer_url;
+        }
+
+        // 2. Return no referrer.
+        return {};
+    // "origin-when-cross-origin"
+    case ReferrerPolicy::OriginWhenCrossOrigin:
+        // 1. If the origin of referrerURL and the origin of request’s current URL are the same, then return
+        //    referrerURL.
+        if (referrer_url.has_value()
+            && URL::url_origin(*referrer_url).is_same_origin(URL::url_origin(request.current_url()))) {
+            return referrer_url;
+        }
+
+        // 2. Return referrerOrigin.
+        return referrer_origin;
+    // "no-referrer-when-downgrade"
+    case ReferrerPolicy::NoReferrerWhenDowngrade:
+        // 1. If referrerURL is a potentially trustworthy URL and request’s current URL is not a potentially
+        //    trustworthy URL, then return no referrer.
+        if (referrer_url.has_value()
+            && SecureContexts::is_url_potentially_trustworthy(*referrer_url) == SecureContexts::Trustworthiness::PotentiallyTrustworthy
+            && SecureContexts::is_url_potentially_trustworthy(request.current_url()) == SecureContexts::Trustworthiness::NotTrustworthy) {
+            return {};
+        }
+
+        // 2. Return referrerURL.
+        return referrer_url;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
 Optional<AK::URL> strip_url_for_use_as_referrer(Optional<AK::URL> url, OriginOnly origin_only)
 {
     // 1. If url is null, return no referrer.

+ 2 - 0
Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h

@@ -7,6 +7,7 @@
 #pragma once
 
 #include <AK/Forward.h>
+#include <LibWeb/Forward.h>
 
 namespace Web::ReferrerPolicy {
 
@@ -15,6 +16,7 @@ enum class OriginOnly {
     No,
 };
 
+Optional<AK::URL> determine_requests_referrer(Fetch::Infrastructure::Request const&);
 Optional<AK::URL> strip_url_for_use_as_referrer(Optional<AK::URL>, OriginOnly origin_only = OriginOnly::No);
 
 }