From febe4fdb46f92a2de06ed08064695da09489e507 Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Wed, 4 Dec 2024 12:36:18 +0000 Subject: [PATCH] LibWeb/CSP: Implement the frame-ancestors directive --- Libraries/LibWeb/CMakeLists.txt | 1 + .../Directives/DirectiveFactory.cpp | 4 + .../Directives/FrameAncestorsDirective.cpp | 77 +++++++++++++++++++ .../Directives/FrameAncestorsDirective.h | 27 +++++++ Libraries/LibWeb/Forward.h | 1 + 5 files changed, 110 insertions(+) create mode 100644 Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.cpp create mode 100644 Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 174cb9a12b3..aa46dd7f296 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -51,6 +51,7 @@ set(SOURCES ContentSecurityPolicy/Directives/DirectiveOperations.cpp ContentSecurityPolicy/Directives/FontSourceDirective.cpp ContentSecurityPolicy/Directives/FormActionDirective.cpp + ContentSecurityPolicy/Directives/FrameAncestorsDirective.cpp ContentSecurityPolicy/Directives/FrameSourceDirective.cpp ContentSecurityPolicy/Directives/ImageSourceDirective.cpp ContentSecurityPolicy/Directives/KeywordSources.cpp diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp index 3501f79a5c6..40b095af41f 100644 --- a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,9 @@ GC::Ref create_directive(GC::Heap& heap, String name, Vector if (name == Names::FormAction) return heap.allocate(move(name), move(value)); + if (name == Names::FrameAncestors) + return heap.allocate(move(name), move(value)); + if (name == Names::FrameSrc) return heap.allocate(move(name), move(value)); diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.cpp new file mode 100644 index 00000000000..79288e13dbb --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::ContentSecurityPolicy::Directives { + +GC_DEFINE_ALLOCATOR(FrameAncestorsDirective); + +FrameAncestorsDirective::FrameAncestorsDirective(String name, Vector value) + : Directive(move(name), move(value)) +{ +} + +// https://w3c.github.io/webappsec-csp/#frame-ancestors-navigation-response +Directive::Result FrameAncestorsDirective::navigation_response_check(GC::Ref, NavigationType, GC::Ref navigation_response, GC::Ref target, CheckType check_type, GC::Ref policy) const +{ + // 1. If navigation response’s URL is local, return "Allowed". + VERIFY(navigation_response->url().has_value()); + if (Fetch::Infrastructure::is_local_url(navigation_response->url().value())) + return Result::Allowed; + + // 2. Assert: request, navigation response, and navigation type, are unused from this point forward in this + // algorithm, as frame-ancestors is concerned only with navigation response’s frame-ancestors directive. + + // 3. If check type is "source", return "Allowed". + // Spec Note: The 'frame-ancestors' directive is relevant only to the target navigable and it has no impact on the + // request’s context. + if (check_type == CheckType::Source) + return Result::Allowed; + + // 4. If target is not a child navigable, return "Allowed". + if (!target->parent()) + return Result::Allowed; + + // 5. Let current be target. + auto current = target; + + // 6. While current is a child navigable: + while (current->parent()) { + // 1. Let document be current’s container document. + auto document = current->container_document(); + VERIFY(document); + + // 2. Let origin be the result of executing the URL parser on the ASCII serialization of document’s origin. + auto origin = DOMURL::parse(document->origin().serialize()); + + // FIXME: What do we do if origin is invalid here? + VERIFY(origin.has_value()); + + // 3. If § 6.7.2.7 Does url match source list in origin with redirect count? returns Does Not Match when + // executed upon origin, this directive’s value, policy’s self-origin, and 0, return "Blocked". + if (does_url_match_source_list_in_origin_with_redirect_count(origin.value(), value(), policy->self_origin(), 0) == MatchResult::DoesNotMatch) + return Result::Blocked; + + // 4. Set current to document’s node navigable. + VERIFY(document->navigable()); + current = *document->navigable(); + } + + // 7. Return "Allowed". + return Result::Allowed; +} + +} diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.h b/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.h new file mode 100644 index 00000000000..12ead5a1509 --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/FrameAncestorsDirective.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::ContentSecurityPolicy::Directives { + +// https://w3c.github.io/webappsec-csp/#directive-frame-ancestors +class FrameAncestorsDirective final : public Directive { + GC_CELL(FrameAncestorsDirective, Directive) + GC_DECLARE_ALLOCATOR(FrameAncestorsDirective); + +public: + virtual ~FrameAncestorsDirective() = default; + + virtual Result navigation_response_check(GC::Ref, NavigationType, GC::Ref, GC::Ref, CheckType, GC::Ref) const override; + +private: + FrameAncestorsDirective(String name, Vector value); +}; + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 6b95693ef9f..d1c12817b65 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -142,6 +142,7 @@ class DefaultSourceDirective; class Directive; class FontSourceDirective; class FormActionDirective; +class FrameAncestorsDirective; class FrameSourceDirective; class ImageSourceDirective; class ManifestSourceDirective;