From 1e1fbb93fbda6cf8c13422aaabae103261700558 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Thu, 13 Oct 2022 19:10:56 +0200 Subject: [PATCH] =?UTF-8?q?LibWeb:=20Implement=20'Determine=20request?= =?UTF-8?q?=E2=80=99s=20Referrer'=20AO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReferrerPolicy/AbstractOperations.cpp | 152 ++++++++++++++++++ .../ReferrerPolicy/AbstractOperations.h | 2 + 2 files changed, 154 insertions(+) diff --git a/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp b/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp index 6fb09c47d3..3523f0fde6 100644 --- a/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.cpp @@ -6,12 +6,164 @@ #include #include +#include #include +#include #include +#include +#include #include namespace Web::ReferrerPolicy { +// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer +Optional 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 { + // 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(environment)->global_object(); + + // 1. If environment’s global object is a Window object, then + if (is(global_object)) { + // 1. Let document be the associated Document of environment’s global object. + auto const& document = static_cast(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 { + // 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 strip_url_for_use_as_referrer(Optional url, OriginOnly origin_only) { // 1. If url is null, return no referrer. diff --git a/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h b/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h index 509277d0bb..5143ae3112 100644 --- a/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/ReferrerPolicy/AbstractOperations.h @@ -7,6 +7,7 @@ #pragma once #include +#include namespace Web::ReferrerPolicy { @@ -15,6 +16,7 @@ enum class OriginOnly { No, }; +Optional determine_requests_referrer(Fetch::Infrastructure::Request const&); Optional strip_url_for_use_as_referrer(Optional, OriginOnly origin_only = OriginOnly::No); }