diff --git a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index e6ca116c3c18f..20f1811f092cb 100644 --- a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -545,9 +545,8 @@ WebIDL::ExceptionOr> 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 internalResponse’s has-cross-origin-redirects to true. - if (request->has_redirect_tainted_origin()) - internal_response->set_has_cross_origin_redirects(true); + // 17. Set internalResponse’s redirect taint to request’s redirect-taint. + internal_response->set_redirect_taint(request->redirect_taint()); // 18. If request’s timing allow failed flag is unset, then set internalResponse’s 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 fetchParams’s request’s mode is not "navigate" or response’s has-cross-origin-redirects is false: - if (fetch_params.request()->mode() != Infrastructure::Request::Mode::Navigate || !response.has_cross_origin_redirects()) { + // 7. If fetchParams’s request’s mode is not "navigate" or response’s redirect taint is "same-origin": + if (fetch_params.request()->mode() != Infrastructure::Request::Mode::Navigate || response.redirect_taint() == Infrastructure::RedirectTaint::SameOrigin) { // 1. Set responseStatus to response’s status. response_status = response.status(); diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP.h index e2cfc37d6b476..9a2af944b5192 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP.h +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP.h @@ -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); } diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp index d4ba72541f5ef..5611aa504c7bc 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp @@ -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: request’s origin is not "client". + if (auto const* origin = m_origin.get_pointer()) + VERIFY(*origin != Origin::Client); - // 1. Let lastURL be null. + // 2. Let lastURL be null. Optional last_url; - // 2. For each url of request’s URL list: + // 3. Let taint be "same-origin". + auto taint = RedirectTaint::SameOrigin; + + // 4. For each url of request’s 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 url’s origin is not same origin with lastURL’s origin and request’s origin is not same origin with lastURL’s origin, then return true. + // 2. If url’s origin is not same site with lastURL’s origin and request’s origin is not same site with + // lastURL’s origin, then return "cross-site". auto const* request_origin = m_origin.get_pointer(); + if (!url.origin().is_same_site(last_url->origin()) + && (request_origin == nullptr || !request_origin->is_same_site(last_url->origin()))) { + return RedirectTaint::CrossSite; + } + + // 3. If url’s origin is not same origin with lastURL’s origin and request’s origin is not same origin with + // lastURL’s 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()))) { - return true; + taint = RedirectTaint::SameSite; } - // 3. Set lastURL to url. + // 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: request’s origin is not "client". + if (auto const* origin = m_origin.get_pointer()) + VERIFY(*origin != Origin::Client); + + // 2. If request’s redirect-taint is not "same-origin", then return "null". + if (redirect_taint() != RedirectTaint::SameOrigin) return "null"_string; - // 2. Return request’s origin, serialized. + // 3. Return request’s origin, serialized. return m_origin.get().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 request’s mode is not "no-cors", then return true. + // 1. Assert: request’s origin is not "client". + if (auto const* origin = m_origin.get_pointer()) + VERIFY(*origin != Origin::Client); + + // 2. If request’s mode is not "no-cors", then return true. if (m_mode != Mode::NoCORS) return true; - // 2. If request’s client is null, then return true. + // 3. If request’s client is null, then return true. if (m_client == nullptr) return true; - // 3. If request’s client’s policy container’s embedder policy’s value is not "credentialless", then return true. + // 4. If request’s client’s policy container’s embedder policy’s value is not "credentialless", then return true. if (m_policy_container.has>() && m_policy_container.get>()->embedder_policy.value != HTML::EmbedderPolicyValue::Credentialless) return true; - // 4. If request’s origin is same origin with request’s current URL’s origin and request does not have a redirect-tainted origin, then return true. - // 5. Return false. + // 5. If request’s origin is same origin with request’s current URL’s origin and request’s redirect-taint is not + // "same-origin", then return true. + // 6. Return false. auto const* request_origin = m_origin.get_pointer(); 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) diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.h index c0261d685d416..21806761287e9 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.h +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -228,6 +229,9 @@ class Request final : public JS::Cell { [[nodiscard]] OriginType const& origin() const { return m_origin; } void set_origin(OriginType origin) { m_origin = move(origin); } + [[nodiscard]] Optional const& top_level_navigation_initiator_origin() const { return m_top_level_navigation_initiator_origin; } + void set_top_level_navigation_initiator_origin(Optional top_level_navigation_initiator_origin) { m_top_level_navigation_initiator_origin = move(top_level_navigation_initiator_origin); } + [[nodiscard]] PolicyContainerType const& policy_container() const { return m_policy_container; } void set_policy_container(PolicyContainerType policy_container) { m_policy_container = move(policy_container); } @@ -307,7 +311,7 @@ class Request final : public JS::Cell { [[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; @@ -417,6 +421,10 @@ class Request final : public JS::Cell { // A request has an associated origin, which is "client" or an origin. Unless stated otherwise it is "client". OriginType m_origin { Origin::Client }; + // https://fetch.spec.whatwg.org/#request-top-level-navigation-initiator-origin + // A request has an associated top-level navigation initiator origin, which is an origin or null. Unless stated otherwise it is null. + Optional m_top_level_navigation_initiator_origin; + // https://fetch.spec.whatwg.org/#concept-request-policy-container // A request has an associated policy container, which is "client" or a policy container. Unless stated otherwise // it is "client". diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.h index 49ae6025e9f63..cc818f7d359a1 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.h +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -103,8 +104,8 @@ class Response : public JS::Cell { [[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 @@ class Response : public JS::Cell { // 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; diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index 511df6241ecd4..1062aa373e46b 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -866,7 +866,10 @@ static WebIDL::ExceptionOr create_navigation request->set_referrer(entry->document_state()->request_referrer()); request->set_policy_container(source_snapshot_params.source_policy_container); - // FIXME: 4. If navigable is a top-level traversable, then set request's top-level navigation initiator origin to entry's document state's initiator origin. + // 4. If navigable is a top-level traversable, then set request's top-level navigation initiator origin to entry's + // document state's initiator origin. + if (navigable->top_level_traversable()->parent() == nullptr) + request->set_top_level_navigation_initiator_origin(entry->document_state()->origin()); // 5. If request's client is null: if (request->client() == nullptr) {