mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-27 04:37:22 +00:00
LibWeb: Enforce Integrity Policy on Fetch requests
This commit is contained in:
parent
ec051bdec2
commit
1228063a85
Notes:
github-actions[bot]
2025-08-14 12:38:53 +00:00
Author: https://github.com/kennethmyhra
Commit: 1228063a85
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5772
Reviewed-by: https://github.com/AtkinsSJ
Reviewed-by: https://github.com/Lubrsi ✅
5 changed files with 88 additions and 4 deletions
|
@ -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 request’s 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 request’s integrity metadata.
|
||||
auto parsed_metadata = MUST(SRI::parse_metadata(request->integrity_metadata()));
|
||||
|
||||
// 3. If parsedMetadata is not the empty set and request’s 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 request’s url is local, return "Allowed".
|
||||
if (Fetch::Infrastructure::is_local_url(request->url()))
|
||||
return Directives::Directive::Result::Allowed;
|
||||
|
||||
// 5. Let policy be policyContainer’s integrity policy.
|
||||
auto const& policy = policy_container->integrity_policy;
|
||||
|
||||
// 6. Let reportPolicy be policyContainer’s 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 request’s client’s 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 policy’s sources contains "inline" and policy’s blocked destinations contains request’s 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 reportPolicy’s sources contains "inline" and reportPolicy’s blocked destinations contains request’s 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)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Web::ContentSecurityPolicy {
|
|||
|
||||
void report_content_security_policy_violations_for_request(JS::Realm&, GC::Ref<Fetch::Infrastructure::Request>);
|
||||
Directives::Directive::Result should_request_be_blocked_by_content_security_policy(JS::Realm&, GC::Ref<Fetch::Infrastructure::Request>);
|
||||
Directives::Directive::Result should_request_be_blocked_by_integrity_policy(GC::Ref<Fetch::Infrastructure::Request>);
|
||||
Directives::Directive::Result should_response_to_request_be_blocked_by_content_security_policy(JS::Realm&, GC::Ref<Fetch::Infrastructure::Response>, GC::Ref<Fetch::Infrastructure::Request>);
|
||||
|
||||
Directives::Directive::Result should_navigation_request_of_type_be_blocked_by_content_security_policy(GC::Ref<Fetch::Infrastructure::Request> navigation_request, Directives::Directive::NavigationType navigation_type);
|
||||
|
|
|
@ -319,11 +319,13 @@ WebIDL::ExceptionOr<GC::Ptr<PendingResponse>> main_fetch(JS::Realm& realm, Infra
|
|||
// 6. Upgrade a mixed content request to a potentially trustworthy URL, if appropriate.
|
||||
MixedContent::upgrade_a_mixed_content_request_to_a_potentially_trustworthy_url_if_appropriate(request);
|
||||
|
||||
// 7. If should request be blocked due to a bad port, should fetching request be blocked as mixed content, or
|
||||
// should request be blocked by Content Security Policy returns blocked, then set response to a network error.
|
||||
// 7. If should request be blocked due to a bad port, should fetching request be blocked as mixed content, should
|
||||
// request be blocked by Content Security Policy, or should request be blocked by Integrity Policy Policy
|
||||
// returns blocked, then set response to a network error.
|
||||
if (Infrastructure::block_bad_port(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
||||
|| MixedContent::should_fetching_request_be_blocked_as_mixed_content(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
||||
|| ContentSecurityPolicy::should_request_be_blocked_by_content_security_policy(realm, request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
|
||||
|| ContentSecurityPolicy::should_request_be_blocked_by_content_security_policy(realm, request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked
|
||||
|| ContentSecurityPolicy::should_request_be_blocked_by_integrity_policy(request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
|
||||
response = Infrastructure::Response::network_error(vm, "Request was blocked"_string);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#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>
|
||||
|
||||
namespace Web::Fetch::Infrastructure {
|
||||
|
||||
|
|
|
@ -10,12 +10,22 @@
|
|||
#include <LibGC/CellAllocator.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibURL/Forward.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/EmbedderPolicy.h>
|
||||
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://w3c.github.io/webappsec-subresource-integrity/#integrity-policy
|
||||
struct IntegrityPolicy {
|
||||
Vector<String> sources;
|
||||
Vector<Fetch::Infrastructure::Request::Destination> blocked_destinations;
|
||||
Vector<String> endpoints;
|
||||
|
||||
bool is_empty() const { return sources.is_empty() && blocked_destinations.is_empty() && endpoints.is_empty(); }
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/origin.html#policy-container
|
||||
// A policy container is a struct containing policies that apply to a Document, a WorkerGlobalScope, or a WorkletGlobalScope. It has the following items:
|
||||
struct PolicyContainer : public GC::Cell {
|
||||
|
@ -37,6 +47,14 @@ public:
|
|||
// A referrer policy, which is a referrer policy. It is initially the default referrer policy.
|
||||
ReferrerPolicy::ReferrerPolicy referrer_policy { ReferrerPolicy::DEFAULT_REFERRER_POLICY };
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#policy-container-integrity-policy
|
||||
// An integrity policy, which is an integrity policy, initially a new integrity policy.
|
||||
IntegrityPolicy integrity_policy {};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#policy-container-report-only-integrity-policy
|
||||
// A report only integrity policy, which is an integrity policy, initially a new integrity policy.
|
||||
IntegrityPolicy report_only_integrity_policy {};
|
||||
|
||||
[[nodiscard]] GC::Ref<PolicyContainer> clone(GC::Heap&) const;
|
||||
[[nodiscard]] SerializedPolicyContainer serialize() const;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue