mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-03 08:52:54 +00:00
LibWeb: Enforce Content Security Policy on Fetch requests
This commit is contained in:
parent
86170f4bfd
commit
6f771f45e2
Notes:
github-actions[bot]
2025-03-18 23:56:19 +00:00
Author: https://github.com/Lubrsi
Commit: 6f771f45e2
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3972
5 changed files with 122 additions and 2 deletions
|
@ -41,6 +41,7 @@ set(SOURCES
|
||||||
ContentSecurityPolicy/Directives/DirectiveOperations.cpp
|
ContentSecurityPolicy/Directives/DirectiveOperations.cpp
|
||||||
ContentSecurityPolicy/Directives/Names.cpp
|
ContentSecurityPolicy/Directives/Names.cpp
|
||||||
ContentSecurityPolicy/Directives/SerializedDirective.cpp
|
ContentSecurityPolicy/Directives/SerializedDirective.cpp
|
||||||
|
ContentSecurityPolicy/BlockingAlgorithms.cpp
|
||||||
ContentSecurityPolicy/Policy.cpp
|
ContentSecurityPolicy/Policy.cpp
|
||||||
ContentSecurityPolicy/PolicyList.cpp
|
ContentSecurityPolicy/PolicyList.cpp
|
||||||
ContentSecurityPolicy/SecurityPolicyViolationEvent.cpp
|
ContentSecurityPolicy/SecurityPolicyViolationEvent.cpp
|
||||||
|
|
101
Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.cpp
Normal file
101
Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/Directives/Names.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/Violation.h>
|
||||||
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||||
|
|
||||||
|
namespace Web::ContentSecurityPolicy {
|
||||||
|
|
||||||
|
// https://w3c.github.io/webappsec-csp/#does-resource-hint-violate-policy
|
||||||
|
[[nodiscard]] static GC::Ptr<Directives::Directive> does_resource_hint_request_violate_policy(JS::Realm& realm, GC::Ref<Fetch::Infrastructure::Request const> request, GC::Ref<Policy const> policy)
|
||||||
|
{
|
||||||
|
// 1. Let defaultDirective be policy’s first directive whose name is "default-src".
|
||||||
|
auto default_directive_iterator = policy->directives().find_if([](auto const& directive) {
|
||||||
|
return directive->name() == Directives::Names::DefaultSrc;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. If defaultDirective does not exist, return "Does Not Violate".
|
||||||
|
if (default_directive_iterator.is_end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 3. For each directive of policy:
|
||||||
|
for (auto directive : policy->directives()) {
|
||||||
|
// 1. Let result be the result of executing directive’s pre-request check on request and policy.
|
||||||
|
auto result = directive->pre_request_check(realm, request, policy);
|
||||||
|
|
||||||
|
// 2. If result is "Allowed", then return "Does Not Violate".
|
||||||
|
if (result == Directives::Directive::Result::Allowed) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return defaultDirective.
|
||||||
|
return *default_directive_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webappsec-csp/#does-request-violate-policy
|
||||||
|
[[nodiscard]] static GC::Ptr<Directives::Directive> does_request_violate_policy(JS::Realm& realm, GC::Ref<Fetch::Infrastructure::Request const> request, GC::Ref<Policy const> policy)
|
||||||
|
{
|
||||||
|
// 1. If request’s initiator is "prefetch", then return the result of executing § 6.7.2.2 Does resource hint
|
||||||
|
// request violate policy? on request and policy.
|
||||||
|
if (request->initiator() == Fetch::Infrastructure::Request::Initiator::Prefetch)
|
||||||
|
return does_resource_hint_request_violate_policy(realm, request, policy);
|
||||||
|
|
||||||
|
// 2. Let violates be "Does Not Violate".
|
||||||
|
GC::Ptr<Directives::Directive> violates;
|
||||||
|
|
||||||
|
// 3. For each directive of policy:
|
||||||
|
for (auto directive : policy->directives()) {
|
||||||
|
// 1. Let result be the result of executing directive’s pre-request check on request and policy.
|
||||||
|
auto result = directive->pre_request_check(realm, request, policy);
|
||||||
|
|
||||||
|
// 2. If result is "Blocked", then let violates be directive.
|
||||||
|
if (result == Directives::Directive::Result::Blocked) {
|
||||||
|
violates = directive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return violates.
|
||||||
|
return violates;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directives::Directive::Result should_request_be_blocked_by_content_security_policy(JS::Realm& realm, GC::Ref<Fetch::Infrastructure::Request> request)
|
||||||
|
{
|
||||||
|
// 1. Let CSP list be request’s policy container's CSP list.
|
||||||
|
auto csp_list = request->policy_container().get<GC::Ref<HTML::PolicyContainer>>()->csp_list;
|
||||||
|
|
||||||
|
// 2. Let result be "Allowed".
|
||||||
|
auto result = Directives::Directive::Result::Allowed;
|
||||||
|
|
||||||
|
// 3. For each policy of CSP list:
|
||||||
|
for (auto policy : csp_list->policies()) {
|
||||||
|
// 1. If policy’s disposition is "report", then skip to the next policy.
|
||||||
|
if (policy->disposition() == Policy::Disposition::Report)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 2. Let violates be the result of executing § 6.7.2.1 Does request violate policy? on request and policy.
|
||||||
|
auto violates = does_request_violate_policy(realm, request, policy);
|
||||||
|
|
||||||
|
// 3. If violates is not "Does Not Violate", then:
|
||||||
|
if (violates) {
|
||||||
|
// 1. Execute § 5.5 Report a violation on the result of executing § 2.4.2 Create a violation object for
|
||||||
|
// request, and policy. on request, and policy.
|
||||||
|
auto violation = Violation::create_a_violation_object_for_request_and_policy(realm, request, policy);
|
||||||
|
violation->report_a_violation(realm);
|
||||||
|
|
||||||
|
// 2. Set result to "Blocked".
|
||||||
|
result = Directives::Directive::Result::Blocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return result.
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h
Normal file
15
Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
|
||||||
|
|
||||||
|
namespace Web::ContentSecurityPolicy {
|
||||||
|
|
||||||
|
[[nodiscard]] Directives::Directive::Result should_request_be_blocked_by_content_security_policy(JS::Realm&, GC::Ref<Fetch::Infrastructure::Request>);
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibRequests/RequestTimingInfo.h>
|
#include <LibRequests/RequestTimingInfo.h>
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||||
#include <LibWeb/Bindings/PrincipalHostDefined.h>
|
#include <LibWeb/Bindings/PrincipalHostDefined.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h>
|
||||||
#include <LibWeb/Cookie/Cookie.h>
|
#include <LibWeb/Cookie/Cookie.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOMURL/DOMURL.h>
|
#include <LibWeb/DOMURL/DOMURL.h>
|
||||||
|
@ -295,8 +296,7 @@ WebIDL::ExceptionOr<GC::Ptr<PendingResponse>> main_fetch(JS::Realm& realm, Infra
|
||||||
// should request be blocked by Content Security Policy returns blocked, then set response to a network error.
|
// should request be blocked by Content Security Policy returns blocked, then set response to a network error.
|
||||||
if (Infrastructure::block_bad_port(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
if (Infrastructure::block_bad_port(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
||||||
|| MixedContent::should_fetching_request_be_blocked_as_mixed_content(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
|| MixedContent::should_fetching_request_be_blocked_as_mixed_content(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|
||||||
|| false // FIXME: "should request be blocked by Content Security Policy returns blocked"
|
|| ContentSecurityPolicy::should_request_be_blocked_by_content_security_policy(realm, request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
|
||||||
) {
|
|
||||||
response = Infrastructure::Response::network_error(vm, "Request was blocked"sv);
|
response = Infrastructure::Response::network_error(vm, "Request was blocked"sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include <AK/Array.h>
|
#include <AK/Array.h>
|
||||||
#include <LibGC/Heap.h>
|
#include <LibGC/Heap.h>
|
||||||
#include <LibJS/Runtime/Realm.h>
|
#include <LibJS/Runtime/Realm.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/Directives/Names.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
|
||||||
|
#include <LibWeb/ContentSecurityPolicy/Violation.h>
|
||||||
#include <LibWeb/DOMURL/DOMURL.h>
|
#include <LibWeb/DOMURL/DOMURL.h>
|
||||||
#include <LibWeb/Fetch/Fetching/PendingResponse.h>
|
#include <LibWeb/Fetch/Fetching/PendingResponse.h>
|
||||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue