LibWeb: Finish algorithm to block trusted type policy creation with CSP

This is the mechanism that should pages to determine what kind of
policies can be created on their domains mostly based around the HTTP
headers the server responds with.
This commit is contained in:
Tete17 2025-07-31 10:57:02 +02:00 committed by Luke Wilde
commit 966e00fd69
Notes: github-actions[bot] 2025-08-11 11:23:27 +00:00
8 changed files with 111 additions and 4 deletions

View file

@ -56,6 +56,7 @@ set(SOURCES
ContentSecurityPolicy/Directives/FrameSourceDirective.cpp
ContentSecurityPolicy/Directives/ImageSourceDirective.cpp
ContentSecurityPolicy/Directives/KeywordSources.cpp
ContentSecurityPolicy/Directives/KeywordTrustedTypes.cpp
ContentSecurityPolicy/Directives/ManifestSourceDirective.cpp
ContentSecurityPolicy/Directives/MediaSourceDirective.cpp
ContentSecurityPolicy/Directives/Names.cpp

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2025, Miguel Sacristán Izcue <miguel_tete17@hotmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h>
namespace Web::ContentSecurityPolicy::Directives::KeywordTrustedTypes {
#define __ENUMERATE_KEYWORD_TRUSTED_TYPE(name, value) \
FlyString name = value##_fly_string;
ENUMERATE_KEYWORD_TRUSTED_TYPES
#undef __ENUMERATE_KEYWORD_TRUSTED_TYPE
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2025, Miguel Sacristán Izcue <miguel_tete17@hotmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
namespace Web::ContentSecurityPolicy::Directives::KeywordTrustedTypes {
// https://www.w3.org/TR/trusted-types/#tt-keyword
#define ENUMERATE_KEYWORD_TRUSTED_TYPES \
__ENUMERATE_KEYWORD_TRUSTED_TYPE(AllowDuplicates, "'allow-duplicates'") \
__ENUMERATE_KEYWORD_TRUSTED_TYPE(None, "'none'") \
__ENUMERATE_KEYWORD_TRUSTED_TYPE(WildCard, "*")
#define __ENUMERATE_KEYWORD_TRUSTED_TYPE(name, value) extern FlyString name;
ENUMERATE_KEYWORD_TRUSTED_TYPES
#undef __ENUMERATE_KEYWORD_TRUSTED_TYPE
}

View file

@ -32,6 +32,7 @@ namespace Web::ContentSecurityPolicy::Directives::Names {
__ENUMERATE_DIRECTIVE_NAME(StyleSrc, "style-src") \
__ENUMERATE_DIRECTIVE_NAME(StyleSrcElem, "style-src-elem") \
__ENUMERATE_DIRECTIVE_NAME(StyleSrcAttr, "style-src-attr") \
__ENUMERATE_DIRECTIVE_NAME(TrustedTypes, "trusted-types") \
__ENUMERATE_DIRECTIVE_NAME(WebRTC, "webrtc") \
__ENUMERATE_DIRECTIVE_NAME(WorkerSrc, "worker-src")

View file

@ -25,6 +25,8 @@ class TrustedTypePolicy final : public Bindings::PlatformObject {
public:
virtual ~TrustedTypePolicy() override = default;
String const& name() const { return m_name; }
private:
explicit TrustedTypePolicy(JS::Realm&, String const&, TrustedTypePolicyOptions const&);
virtual void initialize(JS::Realm&) override;

View file

@ -1,7 +1,7 @@
// https://w3c.github.io/trusted-types/dist/spec/#trusted-type-policy
[Exposed=(Window,Worker)]
interface TrustedTypePolicy {
[FIXME] readonly attribute DOMString name;
readonly attribute DOMString name;
[FIXME] TrustedHTML createHTML(DOMString input, any... arguments);
[FIXME] TrustedScript createScript(DOMString input, any... arguments);
[FIXME] TrustedScriptURL createScriptURL(DOMString input, any... arguments);

View file

@ -10,6 +10,7 @@
#include <LibJS/Runtime/Realm.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
#include <LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h>
#include <LibWeb/ContentSecurityPolicy/Directives/Names.h>
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
#include <LibWeb/HTML/AttributeNames.h>
@ -164,13 +165,12 @@ bool TrustedTypePolicyFactory::is_html(JS::Value value)
}
// https://w3c.github.io/trusted-types/dist/spec/#create-trusted-type-policy-algorithm
WebIDL::ExceptionOr<GC::Ref<TrustedTypePolicy>> TrustedTypePolicyFactory::create_a_trusted_type_policy(String const& policy_name, TrustedTypePolicyOptions const& options, JS::Object&)
WebIDL::ExceptionOr<GC::Ref<TrustedTypePolicy>> TrustedTypePolicyFactory::create_a_trusted_type_policy(String const& policy_name, TrustedTypePolicyOptions const& options, JS::Object& global)
{
auto& realm = this->realm();
// TODO
// 1. Let allowedByCSP be the result of executing Should Trusted Type policy creation be blocked by Content Security Policy? algorithm with global, policyName and factorys created policy names value.
auto const allowed_by_csp = ContentSecurityPolicy::Directives::Directive::Result::Blocked;
auto const allowed_by_csp = should_trusted_type_policy_be_blocked_by_content_security_policy(global, policy_name, m_created_policy_names);
// 2. If allowedByCSP is "Blocked", throw a TypeError and abort further steps.
if (allowed_by_csp == ContentSecurityPolicy::Directives::Directive::Result::Blocked)
@ -196,6 +196,68 @@ WebIDL::ExceptionOr<GC::Ref<TrustedTypePolicy>> TrustedTypePolicyFactory::create
return policy;
}
// https://www.w3.org/TR/trusted-types/#should-block-create-policy
ContentSecurityPolicy::Directives::Directive::Result TrustedTypePolicyFactory::should_trusted_type_policy_be_blocked_by_content_security_policy(JS::Object& global, String const& policy_name, Vector<String> const& created_policy_names)
{
// 1. Let result be "Allowed".
auto result = ContentSecurityPolicy::Directives::Directive::Result::Allowed;
// 2. For each policy in globals CSP list:
for (auto const policy : ContentSecurityPolicy::PolicyList::from_object(global)->policies()) {
// 1. Let createViolation be false.
bool create_violation = false;
// 2. If policys directive set does not contain a directive which name is "trusted-types", skip to the next policy.
if (!policy->contains_directive_with_name(ContentSecurityPolicy::Directives::Names::TrustedTypes))
continue;
// 3. Let directive be the policys directive sets directive which name is "trusted-types"
auto const directive = policy->get_directive_by_name(ContentSecurityPolicy::Directives::Names::TrustedTypes);
// 4. If directives value only contains a tt-keyword which is a match for a value 'none', set createViolation to true.
if (directive->value().size() == 1 && directive->value().first().equals_ignoring_ascii_case(ContentSecurityPolicy::Directives::KeywordTrustedTypes::None))
create_violation = true;
// 5. If createdPolicyNames contains policyName and directives value does not contain a tt-keyword which is a match for a value 'allow-duplicates', set createViolation to true.
auto created_policy_names_iterator = created_policy_names.find(policy_name);
if (!created_policy_names_iterator.is_end()) {
auto maybe_allow_duplicates = directive->value().find_if([](auto const& directive_value) {
return directive_value.equals_ignoring_ascii_case(ContentSecurityPolicy::Directives::KeywordTrustedTypes::AllowDuplicates);
});
if (maybe_allow_duplicates.is_end())
create_violation = true;
}
// 6. If directives value does not contain a tt-policy-name, which value is policyName, and directives value does not contain a tt-wildcard, set createViolation to true.
auto directive_value_iterator = directive->value().find(policy_name);
if (directive_value_iterator.is_end()) {
auto maybe_wild_card = directive->value().find_if([](auto const& directive_value) {
return directive_value.equals_ignoring_ascii_case(ContentSecurityPolicy::Directives::KeywordTrustedTypes::WildCard);
});
if (maybe_wild_card.is_end())
create_violation = true;
}
// 7. If createViolation is false, skip to the next policy.
if (!create_violation)
continue;
// FIXME
// 8. Let violation be the result of executing Create a violation object for global, policy, and directive on global, policy and "trusted-types"
// 9. Set violations resource to "trusted-types-policy".
// 10. Set violations sample to the substring of policyName, containing its first 40 characters.
// 11. Execute Report a violation on violation.
// 12. If policys disposition is "enforce", then set result to "Blocked".
if (policy->disposition() == ContentSecurityPolicy::Policy::Disposition::Enforce)
result = ContentSecurityPolicy::Directives::Directive::Result::Blocked;
}
// 3. Return result.
return result;
}
// https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-get-trusted-type-data-for-attribute
Optional<TrustedTypeData> get_trusted_type_data_for_attribute(String const& element, String const& attribute, Optional<String> const& attribute_ns)
{

View file

@ -9,6 +9,7 @@
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Bindings/TrustedTypePolicyFactoryPrototype.h>
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
#include <LibWeb/TrustedTypes/TrustedTypePolicy.h>
namespace Web::TrustedTypes {
@ -41,6 +42,7 @@ private:
virtual void visit_edges(Visitor&) override;
WebIDL::ExceptionOr<GC::Ref<TrustedTypePolicy>> create_a_trusted_type_policy(String const&, TrustedTypePolicyOptions const&, JS::Object&);
ContentSecurityPolicy::Directives::Directive::Result should_trusted_type_policy_be_blocked_by_content_security_policy(JS::Object&, String const&, Vector<String> const&);
// https://w3c.github.io/trusted-types/dist/spec/#trustedtypepolicyfactory-created-policy-names
Vector<String> m_created_policy_names;