diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 8e9464fab89..0f4fa083104 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -36,6 +36,7 @@ set(SOURCES Compression/CompressionStream.cpp Compression/DecompressionStream.cpp ContentSecurityPolicy/BlockingAlgorithms.cpp + ContentSecurityPolicy/Directives/ConnectSourceDirective.cpp ContentSecurityPolicy/Directives/Directive.cpp ContentSecurityPolicy/Directives/DirectiveFactory.cpp ContentSecurityPolicy/Directives/DirectiveOperations.cpp diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.cpp new file mode 100644 index 00000000000..61f2de6b85e --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::ContentSecurityPolicy::Directives { + +GC_DEFINE_ALLOCATOR(ConnectSourceDirective); + +ConnectSourceDirective::ConnectSourceDirective(String name, Vector value) + : Directive(move(name), move(value)) +{ +} + +// https://w3c.github.io/webappsec-csp/#connect-src-pre-request +Directive::Result ConnectSourceDirective::pre_request_check(GC::Heap&, GC::Ref request, GC::Ref policy) const +{ + // 1. Let name be the result of executing § 6.8.1 Get the effective directive for request on request. + auto name = get_the_effective_directive_for_request(request); + + // 2. If the result of executing § 6.8.4 Should fetch directive execute on name, connect-src and policy is "No", + // return "Allowed". + if (should_fetch_directive_execute(name, Names::ConnectSrc, policy) == ShouldExecute::No) + return Result::Allowed; + + // 3. If the result of executing § 6.7.2.5 Does request match source list? on request, this directive’s value, + // and policy, is "Does Not Match", return "Blocked". + if (does_request_match_source_list(request, value(), policy) == MatchResult::DoesNotMatch) + return Result::Blocked; + + // 4. Return "Allowed". + return Result::Allowed; +} + +// https://w3c.github.io/webappsec-csp/#connect-src-post-request +Directive::Result ConnectSourceDirective::post_request_check(GC::Heap&, GC::Ref request, GC::Ref response, GC::Ref policy) const +{ + // 1. Let name be the result of executing § 6.8.1 Get the effective directive for request on request. + auto name = get_the_effective_directive_for_request(request); + + // 2. If the result of executing § 6.8.4 Should fetch directive execute on name, connect-src and policy is "No", + // return "Allowed". + if (should_fetch_directive_execute(name, Names::ConnectSrc, policy) == ShouldExecute::No) + return Result::Allowed; + + // 3. If the result of executing § 6.7.2.6 Does response to request match source list? on response, request, this + // directive’s value, and policy, is "Does Not Match", return "Blocked". + if (does_response_match_source_list(response, request, value(), policy) == MatchResult::DoesNotMatch) + return Result::Blocked; + + // 4. Return "Allowed". + return Result::Allowed; +} + +} diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.h b/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.h new file mode 100644 index 00000000000..aee16a9f617 --- /dev/null +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.h @@ -0,0 +1,28 @@ +/* + * 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-connect-src +class ConnectSourceDirective final : public Directive { + GC_CELL(ConnectSourceDirective, Directive) + GC_DECLARE_ALLOCATOR(ConnectSourceDirective); + +public: + virtual ~ConnectSourceDirective() = default; + + [[nodiscard]] virtual Result pre_request_check(GC::Heap&, GC::Ref, GC::Ref) const override; + [[nodiscard]] virtual Result post_request_check(GC::Heap&, GC::Ref, GC::Ref, GC::Ref) const override; + +private: + ConnectSourceDirective(String name, Vector value); +}; + +} diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp index 3d1e10f69d6..46070a3a8dc 100644 --- a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp @@ -5,13 +5,18 @@ */ #include +#include #include #include +#include namespace Web::ContentSecurityPolicy::Directives { GC::Ref create_directive(GC::Heap& heap, String name, Vector value) { + if (name == Names::ConnectSrc) + return heap.allocate(move(name), move(value)); + return heap.allocate(move(name), move(value)); } diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.cpp b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.cpp index 2632a5188aa..eb842350b28 100644 --- a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.cpp +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -579,4 +580,28 @@ MatchResult does_url_match_source_list_in_origin_with_redirect_count(URL::URL co return MatchResult::DoesNotMatch; } +// https://w3c.github.io/webappsec-csp/#match-request-to-source-list +MatchResult does_request_match_source_list(GC::Ref request, Vector const& source_list, GC::Ref policy) +{ + // Given a request request, a source list source list, and a policy policy, this algorithm returns the result of + // executing § 6.7.2.7 Does url match source list in origin with redirect count? on request’s current url, source + // list, policy’s self-origin, and request’s redirect count. + // Spec Note: This is generally used in directives' pre-request check algorithms to verify that a given request is + // reasonable. + return does_url_match_source_list_in_origin_with_redirect_count(request->current_url(), source_list, policy->self_origin(), request->redirect_count()); +} + +// https://w3c.github.io/webappsec-csp/#match-response-to-source-list +MatchResult does_response_match_source_list(GC::Ref response, GC::Ref request, Vector const& source_list, GC::Ref policy) +{ + // Given a request request, and a source list source list, and a policy policy, this algorithm returns the result + // of executing § 6.7.2.7 Does url match source list in origin with redirect count? on response’s url, source list, + // policy’s self-origin, and request’s redirect count. + // Spec Note: This is generally used in directives' post-request check algorithms to verify that a given response + // is reasonable. + // FIXME: File spec issue that it does specify to pass in response here. + VERIFY(response->url().has_value()); + return does_url_match_source_list_in_origin_with_redirect_count(response->url().value(), source_list, policy->self_origin(), request->redirect_count()); +} + } diff --git a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.h b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.h index b8d859a5c87..f9ab4f5c353 100644 --- a/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.h +++ b/Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.h @@ -33,4 +33,7 @@ enum class MatchResult { [[nodiscard]] MatchResult does_url_match_expression_in_origin_with_redirect_count(URL::URL const& url, String const& expression, URL::Origin const& origin, u8 redirect_count); [[nodiscard]] MatchResult does_url_match_source_list_in_origin_with_redirect_count(URL::URL const& url, Vector const& source_list, URL::Origin const& origin, u8 redirect_count); +[[nodiscard]] MatchResult does_request_match_source_list(GC::Ref request, Vector const& source_list, GC::Ref policy); +[[nodiscard]] MatchResult does_response_match_source_list(GC::Ref response, GC::Ref request, Vector const& source_list, GC::Ref policy); + } diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 6025efa8a3e..9cfb1ddc669 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -130,6 +130,7 @@ struct SerializedPolicy; namespace Web::ContentSecurityPolicy::Directives { +class ConnectSourceDirective; class Directive; struct SerializedDirective;