LibWeb: Implement integrity-metadata part of fetch algorithm

Specifically, this makes `<link>` elements with an `integrity` attribute
actually work. Previously, we would load their resource, and then drop
it on the floor without actually using it.

The Subresource Integrity code is in `LibWeb/SRI`, since SRI is the name
of the recommendation spec: https://www.w3.org/TR/SRI/

However, the Fetch spec links to the editor's draft, which varies
significantly from the recommendation, and so that is what the code is
based on and what the spec comments link to:
https://w3c.github.io/webappsec-subresource-integrity/

Fixes #18408
This commit is contained in:
Sam Atkins 2023-04-20 16:52:01 +01:00 committed by Linus Groh
parent 6d93e03211
commit 22e0603bf7
Notes: sideshowbarker 2024-07-17 08:55:54 +09:00
4 changed files with 244 additions and 8 deletions

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -38,6 +39,7 @@
#include <LibWeb/Loader/ResourceLoader.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/ReferrerPolicy/AbstractOperations.h>
#include <LibWeb/SRI/SRI.h>
#include <LibWeb/URL/URL.h>
#include <LibWeb/WebIDL/DOMException.h>
@ -479,22 +481,39 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> main_fetch(JS::
if (!request->integrity_metadata().is_empty()) {
// 1. Let processBodyError be this step: run fetch response handover given fetchParams and a network
// error.
auto process_body_error = [&]() -> WebIDL::ExceptionOr<void> {
return fetch_response_handover(realm, fetch_params, Infrastructure::Response::network_error(vm, "Response body could not be processed"sv));
// FIXME: The spec disagrees on whether fully_read()'s process_body_error should take an argument or not.
// See https://github.com/whatwg/fetch/issues/1636
// For now, define two versions of processBodyError
auto process_body_error_no_argument = [&realm, &vm, &fetch_params]() {
TRY_OR_IGNORE(fetch_response_handover(realm, fetch_params, Infrastructure::Response::network_error(vm, "Response body could not be processed"sv)));
};
Infrastructure::Body::ProcessBodyErrorCallback process_body_error = [&realm, &vm, &fetch_params](auto&) {
TRY_OR_IGNORE(fetch_response_handover(realm, fetch_params, Infrastructure::Response::network_error(vm, "Response body could not be processed"sv)));
};
// 2. If responses body is null, then run processBodyError and abort these steps.
if (!response->body().has_value()) {
TRY_OR_IGNORE(process_body_error());
process_body_error_no_argument();
return;
}
// FIXME: 3. Let processBody given bytes be these steps:
// 1. If bytes do not match requests integrity metadata, then run processBodyError and abort these steps.
// 2. Set responses body to bytes as a body.
// 3. Run fetch response handover given fetchParams and response.
// 3. Let processBody given bytes be these steps:
Infrastructure::Body::ProcessBodyCallback process_body = [&realm, &request, &response, &fetch_params, process_body_error = move(process_body_error_no_argument)](ByteBuffer bytes) {
// 1. If bytes do not match requests integrity metadata, then run processBodyError and abort these steps.
if (!TRY_OR_IGNORE(SRI::do_bytes_match_metadata_list(bytes, request->integrity_metadata()))) {
process_body_error();
return;
}
// FIXME: 4. Fully read responses body given processBody and processBodyError.
// 2. Set responses body to bytes as a body.
response->set_body(TRY_OR_IGNORE(Infrastructure::byte_sequence_as_body(realm, bytes)));
// 3. Run fetch response handover given fetchParams and response.
TRY_OR_IGNORE(fetch_response_handover(realm, fetch_params, *response));
};
// 4. Fully read responses body given processBody and processBodyError.
TRY_OR_IGNORE(response->body()->fully_read(realm, move(process_body), move(process_body_error), fetch_params.task_destination()));
}
// 23. Otherwise, run fetch response handover given fetchParams and response.
else {