LibWeb: Introduce Content Security Policy policies and directives

These form the basis of Content Security Policy. A policy is a
collection of directives that are parsed from either the
Content-Security-Policy(-Report-Only) HTTP header, or the `<meta>`
element.

The directives are what restrict the operations can be performed in the
current global execution context. For example, "frame-ancestors: none"
tells us to prevent the page from being loaded in an embedded context,
such as `<iframe>`.

You can see it a bit like OpenBSD's pledge() functionality, but for the
web platform: https://man.openbsd.org/pledge.2
This commit is contained in:
Luke Wilde 2024-11-25 16:17:17 +00:00 committed by Andreas Kling
commit e34a6c86b9
Notes: github-actions[bot] 2025-03-04 13:28:21 +00:00
20 changed files with 846 additions and 3 deletions

View file

@ -7,6 +7,8 @@
#include <LibJS/Runtime/Realm.h>
#include <LibURL/URL.h>
#include <LibWeb/ContentSecurityPolicy/Policy.h>
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
#include <LibWeb/Fetch/Infrastructure/URL.h>
#include <LibWeb/HTML/PolicyContainers.h>
#include <LibWeb/HTML/SerializedPolicyContainer.h>
@ -15,7 +17,8 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(PolicyContainer);
PolicyContainer::PolicyContainer(JS::Realm&)
PolicyContainer::PolicyContainer(JS::Realm& realm)
: csp_list(realm.create<ContentSecurityPolicy::PolicyList>())
{
}
@ -34,6 +37,7 @@ bool url_requires_storing_the_policy_container_in_history(URL::URL const& url)
GC::Ref<PolicyContainer> create_a_policy_container_from_serialized_policy_container(JS::Realm& realm, SerializedPolicyContainer const& serialized_policy_container)
{
GC::Ref<PolicyContainer> result = realm.create<PolicyContainer>(realm);
result->csp_list = ContentSecurityPolicy::PolicyList::create(realm, serialized_policy_container.csp_list);
result->embedder_policy = serialized_policy_container.embedder_policy;
result->referrer_policy = serialized_policy_container.referrer_policy;
return result;
@ -45,7 +49,8 @@ GC::Ref<PolicyContainer> PolicyContainer::clone(JS::Realm& realm) const
// 1. Let clone be a new policy container.
auto clone = realm.create<PolicyContainer>(realm);
// FIXME: 2. For each policy in policyContainer's CSP list, append a copy of policy into clone's CSP list.
// 2. For each policy in policyContainer's CSP list, append a copy of policy into clone's CSP list.
clone->csp_list = csp_list->clone(realm);
// 3. Set clone's embedder policy to a copy of policyContainer's embedder policy.
// NOTE: This is a C++ copy.
@ -61,9 +66,16 @@ GC::Ref<PolicyContainer> PolicyContainer::clone(JS::Realm& realm) const
SerializedPolicyContainer PolicyContainer::serialize() const
{
return SerializedPolicyContainer {
.csp_list = csp_list->serialize(),
.embedder_policy = embedder_policy,
.referrer_policy = referrer_policy,
};
}
void PolicyContainer::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(csp_list);
}
}

View file

@ -26,7 +26,8 @@ public:
virtual ~PolicyContainer() = default;
// https://html.spec.whatwg.org/multipage/origin.html#policy-container-csp-list
// FIXME: A CSP list, which is a CSP list. It is initially empty.
// A CSP list, which is a CSP list. It is initially empty.
GC::Ref<ContentSecurityPolicy::PolicyList> csp_list;
// https://html.spec.whatwg.org/multipage/origin.html#policy-container-embedder-policy
// An embedder policy, which is an embedder policy. It is initially a new embedder policy.
@ -39,6 +40,9 @@ public:
[[nodiscard]] GC::Ref<PolicyContainer> clone(JS::Realm&) const;
[[nodiscard]] SerializedPolicyContainer serialize() const;
protected:
virtual void visit_edges(Cell::Visitor&) override;
private:
PolicyContainer(JS::Realm&);
};

View file

@ -13,6 +13,7 @@ namespace IPC {
template<>
ErrorOr<void> encode(Encoder& encoder, Web::HTML::SerializedPolicyContainer const& serialized_policy_container)
{
TRY(encoder.encode(serialized_policy_container.csp_list));
TRY(encoder.encode(serialized_policy_container.embedder_policy));
TRY(encoder.encode(serialized_policy_container.referrer_policy));
@ -24,6 +25,7 @@ ErrorOr<Web::HTML::SerializedPolicyContainer> decode(Decoder& decoder)
{
Web::HTML::SerializedPolicyContainer serialized_policy_container {};
serialized_policy_container.csp_list = TRY(decoder.decode<Vector<Web::ContentSecurityPolicy::SerializedPolicy>>());
serialized_policy_container.embedder_policy = TRY(decoder.decode<Web::HTML::EmbedderPolicy>());
serialized_policy_container.referrer_policy = TRY(decoder.decode<Web::ReferrerPolicy::ReferrerPolicy>());

View file

@ -6,12 +6,14 @@
#pragma once
#include <LibWeb/ContentSecurityPolicy/SerializedPolicy.h>
#include <LibWeb/HTML/EmbedderPolicy.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
namespace Web::HTML {
struct SerializedPolicyContainer {
Vector<ContentSecurityPolicy::SerializedPolicy> csp_list;
EmbedderPolicy embedder_policy;
ReferrerPolicy::ReferrerPolicy referrer_policy;
};