From 966e00fd69d1b39b4940645124b28cdf29888a0e Mon Sep 17 00:00:00 2001 From: Tete17 Date: Thu, 31 Jul 2025 10:57:02 +0200 Subject: [PATCH] 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. --- Libraries/LibWeb/CMakeLists.txt | 1 + .../Directives/KeywordTrustedTypes.cpp | 16 +++++ .../Directives/KeywordTrustedTypes.h | 23 +++++++ .../ContentSecurityPolicy/Directives/Names.h | 1 + .../LibWeb/TrustedTypes/TrustedTypePolicy.h | 2 + .../LibWeb/TrustedTypes/TrustedTypePolicy.idl | 2 +- .../TrustedTypes/TrustedTypePolicyFactory.cpp | 68 ++++++++++++++++++- .../TrustedTypes/TrustedTypePolicyFactory.h | 2 + 8 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.cpp create mode 100644 Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 9dc981e42c0..b55d8fa6815 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -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 diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.cpp new file mode 100644 index 00000000000..08d4bd13735 --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025, Miguel Sacristán Izcue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +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 + +} diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h b/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h new file mode 100644 index 00000000000..a07ad3c38c4 --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/KeywordTrustedTypes.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025, Miguel Sacristán Izcue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +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 + +} diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/Names.h b/Libraries/LibWeb/ContentSecurityPolicy/Directives/Names.h index 8022bcfd372..4709d88cb5d 100644 --- a/Libraries/LibWeb/ContentSecurityPolicy/Directives/Names.h +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/Names.h @@ -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") diff --git a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.h b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.h index 3adc2058d80..78a0a18ffee 100644 --- a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.h +++ b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.h @@ -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; diff --git a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.idl b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.idl index b98cb21a107..ed839564a46 100644 --- a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.idl +++ b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicy.idl @@ -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); diff --git a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.cpp b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.cpp index 1e8047143be..c4c325b66c4 100644 --- a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.cpp +++ b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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> TrustedTypePolicyFactory::create_a_trusted_type_policy(String const& policy_name, TrustedTypePolicyOptions const& options, JS::Object&) +WebIDL::ExceptionOr> 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 factory’s 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> 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 const& created_policy_names) +{ + // 1. Let result be "Allowed". + auto result = ContentSecurityPolicy::Directives::Directive::Result::Allowed; + + // 2. For each policy in global’s CSP list: + for (auto const policy : ContentSecurityPolicy::PolicyList::from_object(global)->policies()) { + // 1. Let createViolation be false. + bool create_violation = false; + + // 2. If policy’s 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 policy’s directive set’s directive which name is "trusted-types" + auto const directive = policy->get_directive_by_name(ContentSecurityPolicy::Directives::Names::TrustedTypes); + + // 4. If directive’s 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 directive’s 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 directive’s value does not contain a tt-policy-name, which value is policyName, and directive’s 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 violation’s resource to "trusted-types-policy". + // 10. Set violation’s sample to the substring of policyName, containing its first 40 characters. + // 11. Execute Report a violation on violation. + + // 12. If policy’s 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 get_trusted_type_data_for_attribute(String const& element, String const& attribute, Optional const& attribute_ns) { diff --git a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.h b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.h index 81fb07f6478..618cb9964bb 100644 --- a/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.h +++ b/Libraries/LibWeb/TrustedTypes/TrustedTypePolicyFactory.h @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Web::TrustedTypes { @@ -41,6 +42,7 @@ private: virtual void visit_edges(Visitor&) override; WebIDL::ExceptionOr> 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 const&); // https://w3c.github.io/trusted-types/dist/spec/#trustedtypepolicyfactory-created-policy-names Vector m_created_policy_names;