LibWeb: Enforce Integrity Policy on Fetch requests

This commit is contained in:
Kenneth Myhra 2025-08-07 22:41:54 +02:00 committed by Luke Wilde
commit 1228063a85
Notes: github-actions[bot] 2025-08-14 12:38:53 +00:00
5 changed files with 88 additions and 4 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2025, Kenneth Myhra <kennethmyhra@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -14,8 +15,11 @@
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
#include <LibWeb/Fetch/Infrastructure/URL.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/SRI/SRI.h>
#include <LibWeb/WebAssembly/WebAssembly.h>
namespace Web::ContentSecurityPolicy {
@ -131,6 +135,66 @@ Directives::Directive::Result should_request_be_blocked_by_content_security_poli
return result;
}
// https://w3c.github.io/webappsec-subresource-integrity/#should-request-be-blocked-by-integrity-policy
Directives::Directive::Result should_request_be_blocked_by_integrity_policy(GC::Ref<Fetch::Infrastructure::Request> request)
{
VERIFY(request->policy_container().has<GC::Ref<HTML::PolicyContainer>>());
// 1. Let policyContainer be requests policy container.
auto const& policy_container = request->policy_container().get<GC::Ref<HTML::PolicyContainer>>();
// 2. Let parsedMetadata be the result of calling parse metadata with requests integrity metadata.
auto parsed_metadata = MUST(SRI::parse_metadata(request->integrity_metadata()));
// 3. If parsedMetadata is not the empty set and requests mode is either "cors" or "same-origin", return "Allowed".
if (!parsed_metadata.is_empty() && (request->mode() == Fetch::Infrastructure::Request::Mode::CORS || request->mode() == Fetch::Infrastructure::Request::Mode::SameOrigin))
return Directives::Directive::Result::Allowed;
// 4. If requests url is local, return "Allowed".
if (Fetch::Infrastructure::is_local_url(request->url()))
return Directives::Directive::Result::Allowed;
// 5. Let policy be policyContainers integrity policy.
auto const& policy = policy_container->integrity_policy;
// 6. Let reportPolicy be policyContainers report only integrity policy.
auto const& report_policy = policy_container->report_only_integrity_policy;
// 7. If both policy and reportPolicy are empty integrity policys, return "Allowed".
if (policy.is_empty() && report_policy.is_empty())
return Directives::Directive::Result::Allowed;
// 8. Let global be requests clients global object.
auto& global = request->client()->global_object();
// 9. If global is not a Window nor a WorkerGlobalScope, return "Allowed".
if (!is<HTML::Window>(global) && !is<HTML::WorkerGlobalScope>(global))
return Directives::Directive::Result::Allowed;
// 10. Let block be a boolean, initially false.
bool block = false;
// FIXME: 11. Let reportBlock be a boolean, initially false.
[[maybe_unused]] auto report_block = false;
// 12. If policys sources contains "inline" and policys blocked destinations contains requests destination, set block to true.
if (policy.sources.contains_slow("inline"sv)
&& request->destination().has_value()
&& policy.blocked_destinations.contains_slow(request->destination().value()))
block = true;
// 13. If reportPolicys sources contains "inline" and reportPolicys blocked destinations contains requests destination, set reportBlock to true.
if (report_policy.sources.contains_slow("inline"sv)
&& request->destination().has_value()
&& report_policy.blocked_destinations.contains_slow(request->destination().value()))
report_block = true;
// FIXME: 14. If block is true or reportBlock is true, then report violation with request, block, reportBlock, policy and reportPolicy.
// 15. If block is true, then return "Blocked"; otherwise "Allowed".
return block ? Directives::Directive::Result::Blocked : Directives::Directive::Result::Allowed;
}
// https://w3c.github.io/webappsec-csp/#should-block-response
Directives::Directive::Result should_response_to_request_be_blocked_by_content_security_policy(JS::Realm& realm, GC::Ref<Fetch::Infrastructure::Response> response, GC::Ref<Fetch::Infrastructure::Request> request)
{