LibWeb: Enforce Content Security Policy on Fetch requests

This commit is contained in:
Luke Wilde 2024-11-25 17:29:27 +00:00 committed by Alexander Kalenik
parent 86170f4bfd
commit 6f771f45e2
Notes: github-actions[bot] 2025-03-18 23:56:19 +00:00
5 changed files with 122 additions and 2 deletions

View 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 policys 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 directives 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 requests 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 directives 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 requests 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 policys 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;
}
}