LibWeb: Update Fetch's compute the redirect-taint concept
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

This commit is contained in:
Kenneth Myhra 2025-08-10 19:41:30 +02:00 committed by Tim Flynn
commit 0dc2fb3781
Notes: github-actions[bot] 2025-08-12 11:09:46 +00:00
5 changed files with 60 additions and 31 deletions

View file

@ -545,9 +545,8 @@ WebIDL::ExceptionOr<GC::Ptr<PendingResponse>> main_fetch(JS::Realm& realm, Infra
if (internal_response->url_list().is_empty())
internal_response->set_url_list(request->url_list());
// 17. If request has a redirect-tainted origin, then set internalResponses has-cross-origin-redirects to true.
if (request->has_redirect_tainted_origin())
internal_response->set_has_cross_origin_redirects(true);
// 17. Set internalResponses redirect taint to requests redirect-taint.
internal_response->set_redirect_taint(request->redirect_taint());
// 18. If requests timing allow failed flag is unset, then set internalResponses timing allow passed flag.
if (!request->timing_allow_failed())
@ -706,8 +705,8 @@ void fetch_response_handover(JS::Realm& realm, Infrastructure::FetchParams const
// 6. Let responseStatus be 0.
auto response_status = 0;
// 7. If fetchParamss requests mode is not "navigate" or responses has-cross-origin-redirects is false:
if (fetch_params.request()->mode() != Infrastructure::Request::Mode::Navigate || !response.has_cross_origin_redirects()) {
// 7. If fetchParamss requests mode is not "navigate" or responses redirect taint is "same-origin":
if (fetch_params.request()->mode() != Infrastructure::Request::Mode::Navigate || response.redirect_taint() == Infrastructure::RedirectTaint::SameOrigin) {
// 1. Set responseStatus to responses status.
response_status = response.status();

View file

@ -47,6 +47,12 @@ enum class HttpQuotedStringExtractValue {
Yes,
};
enum class RedirectTaint {
SameOrigin,
SameSite,
CrossSite,
};
[[nodiscard]] String collect_an_http_quoted_string(GenericLexer& lexer, HttpQuotedStringExtractValue extract_value = HttpQuotedStringExtractValue::No);
}

View file

@ -164,14 +164,19 @@ bool Request::is_navigation_request() const
}
// https://fetch.spec.whatwg.org/#concept-request-tainted-origin
bool Request::has_redirect_tainted_origin() const
RedirectTaint Request::redirect_taint() const
{
// A request request has a redirect-tainted origin if these steps return true:
// 1. Assert: requests origin is not "client".
if (auto const* origin = m_origin.get_pointer<Origin>())
VERIFY(*origin != Origin::Client);
// 1. Let lastURL be null.
// 2. Let lastURL be null.
Optional<URL::URL const&> last_url;
// 2. For each url of requests URL list:
// 3. Let taint be "same-origin".
auto taint = RedirectTaint::SameOrigin;
// 4. For each url of requests URL list:
for (auto const& url : m_url_list) {
// 1. If lastURL is null, then set lastURL to url and continue.
if (!last_url.has_value()) {
@ -179,29 +184,41 @@ bool Request::has_redirect_tainted_origin() const
continue;
}
// 2. If urls origin is not same origin with lastURLs origin and requests origin is not same origin with lastURLs origin, then return true.
// 2. If urls origin is not same site with lastURLs origin and requests origin is not same site with
// lastURLs origin, then return "cross-site".
auto const* request_origin = m_origin.get_pointer<URL::Origin>();
if (!url.origin().is_same_origin(last_url->origin())
&& (request_origin == nullptr || !request_origin->is_same_origin(last_url->origin()))) {
return true;
if (!url.origin().is_same_site(last_url->origin())
&& (request_origin == nullptr || !request_origin->is_same_site(last_url->origin()))) {
return RedirectTaint::CrossSite;
}
// 3. Set lastURL to url.
// 3. If urls origin is not same origin with lastURLs origin and requests origin is not same origin with
// lastURLs origin, then set taint to "same-site".
if (!url.origin().is_same_origin(last_url->origin())
&& (request_origin == nullptr || !request_origin->is_same_origin(last_url->origin()))) {
taint = RedirectTaint::SameSite;
}
// 4. Set lastURL to url.
last_url = url;
}
// 3. Return false.
return false;
// 5. Return taint.
return taint;
}
// https://fetch.spec.whatwg.org/#serializing-a-request-origin
String Request::serialize_origin() const
{
// 1. If request has a redirect-tainted origin, then return "null".
if (has_redirect_tainted_origin())
// 1. Assert: requests origin is not "client".
if (auto const* origin = m_origin.get_pointer<Origin>())
VERIFY(*origin != Origin::Client);
// 2. If requests redirect-taint is not "same-origin", then return "null".
if (redirect_taint() != RedirectTaint::SameOrigin)
return "null"_string;
// 2. Return requests origin, serialized.
// 3. Return requests origin, serialized.
return m_origin.get<URL::Origin>().serialize();
}
@ -358,25 +375,30 @@ void Request::add_origin_header()
// https://fetch.spec.whatwg.org/#cross-origin-embedder-policy-allows-credentials
bool Request::cross_origin_embedder_policy_allows_credentials() const
{
// 1. If requests mode is not "no-cors", then return true.
// 1. Assert: requests origin is not "client".
if (auto const* origin = m_origin.get_pointer<Origin>())
VERIFY(*origin != Origin::Client);
// 2. If requests mode is not "no-cors", then return true.
if (m_mode != Mode::NoCORS)
return true;
// 2. If requests client is null, then return true.
// 3. If requests client is null, then return true.
if (m_client == nullptr)
return true;
// 3. If requests clients policy containers embedder policys value is not "credentialless", then return true.
// 4. If requests clients policy containers embedder policys value is not "credentialless", then return true.
if (m_policy_container.has<GC::Ref<HTML::PolicyContainer>>() && m_policy_container.get<GC::Ref<HTML::PolicyContainer>>()->embedder_policy.value != HTML::EmbedderPolicyValue::Credentialless)
return true;
// 4. If requests origin is same origin with requests current URLs origin and request does not have a redirect-tainted origin, then return true.
// 5. Return false.
// 5. If requests origin is same origin with requests current URLs origin and requests redirect-taint is not
// "same-origin", then return true.
// 6. Return false.
auto const* request_origin = m_origin.get_pointer<URL::Origin>();
if (request_origin == nullptr)
return false;
return request_origin->is_same_origin(current_url().origin()) && !has_redirect_tainted_origin();
return request_origin->is_same_origin(current_url().origin()) && redirect_taint() != RedirectTaint::SameOrigin;
}
StringView request_destination_to_string(Request::Destination destination)

View file

@ -19,6 +19,7 @@
#include <LibJS/Heap/Cell.h>
#include <LibURL/Origin.h>
#include <LibURL/URL.h>
#include <LibWeb/Fetch/Infrastructure/HTTP.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/HTML/PolicyContainers.h>
@ -310,7 +311,7 @@ public:
[[nodiscard]] bool is_non_subresource_request() const;
[[nodiscard]] bool is_navigation_request() const;
[[nodiscard]] bool has_redirect_tainted_origin() const;
[[nodiscard]] RedirectTaint redirect_taint() const;
[[nodiscard]] String serialize_origin() const;
[[nodiscard]] ByteBuffer byte_serialize_origin() const;

View file

@ -16,6 +16,7 @@
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibURL/URL.h>
#include <LibWeb/Fetch/Infrastructure/HTTP.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Statuses.h>
@ -103,8 +104,8 @@ public:
[[nodiscard]] virtual BodyInfo const& body_info() const { return m_body_info; }
virtual void set_body_info(BodyInfo body_info) { m_body_info = body_info; }
[[nodiscard]] bool has_cross_origin_redirects() const { return m_has_cross_origin_redirects; }
void set_has_cross_origin_redirects(bool has_cross_origin_redirects) { m_has_cross_origin_redirects = has_cross_origin_redirects; }
[[nodiscard]] RedirectTaint redirect_taint() const { return m_redirect_taint; }
void set_redirect_taint(RedirectTaint redirect_taint) { m_redirect_taint = move(redirect_taint); }
[[nodiscard]] bool is_aborted_network_error() const;
[[nodiscard]] bool is_network_error() const;
@ -188,9 +189,9 @@ private:
// https://fetch.spec.whatwg.org/#response-service-worker-timing-info
// FIXME: A response has an associated service worker timing info (null or a service worker timing info), which is initially null.
// https://fetch.spec.whatwg.org/#response-has-cross-origin-redirects
// A response has an associated has-cross-origin-redirects (a boolean), which is initially false.
bool m_has_cross_origin_redirects { false };
// https://fetch.spec.whatwg.org/#response-redirect-taint
// A response has an associated redirect taint ("same-origin", "same-site", or "cross-site"), which is initially "same-origin".
RedirectTaint m_redirect_taint { RedirectTaint::SameOrigin };
// FIXME is the type correct?
u64 current_age() const;