Skip to content

Commit 4aa3556

Browse files
Lubrsikalenikaliaksandr
authored andcommitted
LibWeb/CSP: Implement the base-uri directive
1 parent febe4fd commit 4aa3556

File tree

8 files changed

+111
-5
lines changed

8 files changed

+111
-5
lines changed

Libraries/LibWeb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ set(SOURCES
4343
Compression/CompressionStream.cpp
4444
Compression/DecompressionStream.cpp
4545
ContentSecurityPolicy/BlockingAlgorithms.cpp
46+
ContentSecurityPolicy/Directives/BaseUriDirective.cpp
4647
ContentSecurityPolicy/Directives/ChildSourceDirective.cpp
4748
ContentSecurityPolicy/Directives/ConnectSourceDirective.cpp
4849
ContentSecurityPolicy/Directives/DefaultSourceDirective.cpp

Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <LibWeb/DOM/Element.h>
1515
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
1616
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
17+
#include <LibWeb/HTML/Window.h>
1718
#include <LibWeb/Infra/Strings.h>
1819
#include <LibWeb/WebAssembly/WebAssembly.h>
1920

@@ -556,4 +557,53 @@ JS::ThrowCompletionOr<void> ensure_csp_does_not_block_wasm_byte_compilation(JS::
556557
return {};
557558
}
558559

560+
// https://w3c.github.io/webappsec-csp/#allow-base-for-document
561+
Directives::Directive::Result is_base_allowed_for_document(JS::Realm& realm, URL::URL const& base, GC::Ref<DOM::Document const> document)
562+
{
563+
// 1. For each policy of document’s global object’s csp list:
564+
VERIFY(document->window());
565+
auto csp_list = PolicyList::from_object(*document->window());
566+
VERIFY(csp_list);
567+
for (auto const policy : csp_list->policies()) {
568+
// 1. Let source list be null.
569+
// NOTE: Not necessary.
570+
571+
// 2. If a directive whose name is "base-uri" is present in policy’s directive set, set source list to that
572+
// directive’s value.
573+
auto maybe_base_uri = policy->directives().find_if([](auto const& directive) {
574+
return directive->name() == Directives::Names::BaseUri;
575+
});
576+
577+
// 3. If source list is null, skip to the next policy.
578+
if (maybe_base_uri.is_end())
579+
continue;
580+
581+
auto const& source_list = (*maybe_base_uri)->value();
582+
583+
// 4. If the result of executing § 6.7.2.7 Does url match source list in origin with redirect count? on base,
584+
// source list, policy’s self-origin, and 0 is "Does Not Match":
585+
// Spec Note: We compare against the fallback base URL in order to deal correctly with things like an iframe
586+
// srcdoc Document which has been sandboxed into an opaque origin.
587+
if (Directives::does_url_match_source_list_in_origin_with_redirect_count(base, source_list, policy->self_origin(), 0) == Directives::MatchResult::DoesNotMatch) {
588+
// 1. Let violation be the result of executing § 2.4.1 Create a violation object for global, policy, and
589+
// directive on document’s global object, policy, and "base-uri".
590+
auto base_uri_string = Directives::Names::BaseUri.to_string();
591+
auto violation = Violation::create_a_violation_object_for_global_policy_and_directive(realm, document->window(), policy, base_uri_string);
592+
593+
// 2. Set violation’s resource to "inline".
594+
violation->set_resource(Violation::Resource::Inline);
595+
596+
// 3. Execute § 5.5 Report a violation on violation.
597+
violation->report_a_violation(realm);
598+
599+
// 4. If policy’s disposition is "enforce", return "Blocked".
600+
if (policy->disposition() == Policy::Disposition::Enforce)
601+
return Directives::Directive::Result::Blocked;
602+
}
603+
}
604+
605+
// 2. Return "Allowed".
606+
return Directives::Directive::Result::Allowed;
607+
}
608+
559609
}

Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#pragma once
88

99
#include <LibJS/Runtime/VM.h>
10+
#include <LibURL/Forward.h>
1011
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
1112

1213
namespace Web::ContentSecurityPolicy {
@@ -27,4 +28,6 @@ Directives::Directive::Result should_elements_inline_type_behavior_be_blocked_by
2728
JS::ThrowCompletionOr<void> ensure_csp_does_not_block_string_compilation(JS::Realm& realm, ReadonlySpan<String> parameter_strings, StringView body_string, StringView code_string, JS::CompilationType compilation_type, ReadonlySpan<JS::Value> parameter_args, JS::Value body_arg);
2829
JS::ThrowCompletionOr<void> ensure_csp_does_not_block_wasm_byte_compilation(JS::Realm&);
2930

31+
[[nodiscard]] Directives::Directive::Result is_base_allowed_for_document(JS::Realm&, URL::URL const& base, GC::Ref<DOM::Document const> document);
32+
3033
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <LibWeb/ContentSecurityPolicy/Directives/BaseUriDirective.h>
8+
9+
namespace Web::ContentSecurityPolicy::Directives {
10+
11+
GC_DEFINE_ALLOCATOR(BaseUriDirective);
12+
13+
BaseUriDirective::BaseUriDirective(String name, Vector<String> value)
14+
: Directive(move(name), move(value))
15+
{
16+
}
17+
18+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
10+
11+
namespace Web::ContentSecurityPolicy::Directives {
12+
13+
// https://w3c.github.io/webappsec-csp/#directive-base-uri
14+
class BaseUriDirective final : public Directive {
15+
GC_CELL(BaseUriDirective, Directive)
16+
GC_DECLARE_ALLOCATOR(BaseUriDirective);
17+
18+
public:
19+
virtual ~BaseUriDirective() = default;
20+
21+
private:
22+
BaseUriDirective(String name, Vector<String> value);
23+
};
24+
25+
}

Libraries/LibWeb/ContentSecurityPolicy/Directives/DirectiveFactory.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <LibJS/Runtime/Realm.h>
8+
#include <LibWeb/ContentSecurityPolicy/Directives/BaseUriDirective.h>
89
#include <LibWeb/ContentSecurityPolicy/Directives/ChildSourceDirective.h>
910
#include <LibWeb/ContentSecurityPolicy/Directives/ConnectSourceDirective.h>
1011
#include <LibWeb/ContentSecurityPolicy/Directives/DefaultSourceDirective.h>
@@ -31,6 +32,9 @@ namespace Web::ContentSecurityPolicy::Directives {
3132

3233
GC::Ref<Directive> create_directive(GC::Heap& heap, String name, Vector<String> value)
3334
{
35+
if (name == Names::BaseUri)
36+
return heap.allocate<BaseUriDirective>(move(name), move(value));
37+
3438
if (name == Names::ChildSrc)
3539
return heap.allocate<ChildSourceDirective>(move(name), move(value));
3640

Libraries/LibWeb/Forward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ struct SerializedPolicy;
136136

137137
namespace Web::ContentSecurityPolicy::Directives {
138138

139+
class BaseUriDirective;
139140
class ChildSourceDirective;
140141
class ConnectSourceDirective;
141142
class DefaultSourceDirective;

Libraries/LibWeb/HTML/HTMLBaseElement.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <LibWeb/Bindings/HTMLBaseElementPrototype.h>
8+
#include <LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h>
89
#include <LibWeb/DOM/Document.h>
910
#include <LibWeb/HTML/HTMLBaseElement.h>
1011

@@ -80,11 +81,14 @@ void HTMLBaseElement::set_the_frozen_base_url()
8081
auto url_record = document.fallback_base_url().complete_url(href);
8182

8283
// 3. If any of the following are true:
83-
// - urlRecord is failure;
84-
// - urlRecord's scheme is "data" or "javascript"; or
85-
// FIXME: - running Is base allowed for Document? on urlRecord and document returns "Blocked",
86-
// then set element's frozen base URL to document's fallback base URL and return.
87-
if (!url_record.has_value() || url_record->scheme() == "data" || url_record->scheme() == "javascript") {
84+
// - urlRecord is failure;
85+
// - urlRecord's scheme is "data" or "javascript"; or
86+
// - running Is base allowed for Document? on urlRecord and document returns "Blocked",
87+
if (!url_record.has_value()
88+
|| url_record->scheme() == "data"
89+
|| url_record->scheme() == "javascript"
90+
|| ContentSecurityPolicy::is_base_allowed_for_document(realm(), url_record.value(), document) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
91+
// then set element's frozen base URL to document's fallback base URL and return.
8892
m_frozen_base_url = document.fallback_base_url();
8993
return;
9094
}

0 commit comments

Comments
 (0)