mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb/CSP: Implement the sandbox directive
This commit is contained in:
parent
40bb50ac60
commit
1d57df6e26
|
|
@ -62,6 +62,7 @@ set(SOURCES
|
|||
ContentSecurityPolicy/Directives/ObjectSourceDirective.cpp
|
||||
ContentSecurityPolicy/Directives/ReportToDirective.cpp
|
||||
ContentSecurityPolicy/Directives/ReportUriDirective.cpp
|
||||
ContentSecurityPolicy/Directives/SandboxDirective.cpp
|
||||
ContentSecurityPolicy/Directives/ScriptSourceAttributeDirective.cpp
|
||||
ContentSecurityPolicy/Directives/ScriptSourceDirective.cpp
|
||||
ContentSecurityPolicy/Directives/ScriptSourceElementDirective.cpp
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <LibWeb/ContentSecurityPolicy/Directives/ObjectSourceDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/ReportToDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/ReportUriDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/SandboxDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/ScriptSourceAttributeDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/ScriptSourceDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/ScriptSourceElementDirective.h>
|
||||
|
|
@ -77,6 +78,9 @@ GC::Ref<Directive> create_directive(GC::Heap& heap, String name, Vector<String>
|
|||
if (name == Names::ReportUri)
|
||||
return heap.allocate<ReportUriDirective>(move(name), move(value));
|
||||
|
||||
if (name == Names::Sandbox)
|
||||
return heap.allocate<SandboxDirective>(move(name), move(value));
|
||||
|
||||
if (name == Names::ScriptSrcAttr)
|
||||
return heap.allocate<ScriptSourceAttributeDirective>(move(name), move(value));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/SandboxDirective.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Policy.h>
|
||||
#include <LibWeb/HTML/SandboxingFlagSet.h>
|
||||
|
||||
namespace Web::ContentSecurityPolicy::Directives {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(SandboxDirective);
|
||||
|
||||
SandboxDirective::SandboxDirective(String name, Vector<String> value)
|
||||
: Directive(move(name), move(value))
|
||||
{
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#sandbox-init
|
||||
Directive::Result SandboxDirective::initialization(Variant<GC::Ref<DOM::Document const>, GC::Ref<HTML::WorkerGlobalScope const>> context, GC::Ref<Policy const> policy) const
|
||||
{
|
||||
// 1. If policy’s disposition is not "enforce", or context is not a WorkerGlobalScope, then abort this algorithm.
|
||||
// FIXME: File spec issue that this step doesn't specify the return value. It must be allowed, because Document
|
||||
// asserts that the result of this algorithm is Allowed.
|
||||
if (policy->disposition() != Policy::Disposition::Enforce || !context.has<GC::Ref<HTML::WorkerGlobalScope const>>())
|
||||
return Result::Allowed;
|
||||
|
||||
// 2. Let sandboxing flag set be a new sandboxing flag set.
|
||||
// 3. Parse a sandboxing directive using this directive’s value as the input, and sandboxing flag set as the output.
|
||||
// FIXME: File spec issue that "parse a sandboxing directive" does not accept a set of tokens.
|
||||
auto sandboxing_flag_set = HTML::parse_a_sandboxing_directive(value());
|
||||
|
||||
// 4. If sandboxing flag set contains either the sandboxed scripts browsing context flag or the sandboxed origin
|
||||
// browsing context flag flags, return "Blocked".
|
||||
// Spec Note: This will need to change if we allow Workers to be sandboxed into unique origins, which seems like a
|
||||
// pretty reasonable thing to do.
|
||||
if (has_flag(sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedScripts) || has_flag(sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedOrigin))
|
||||
return Result::Blocked;
|
||||
|
||||
// 5. Return "Allowed".
|
||||
return Result::Allowed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
|
||||
|
||||
namespace Web::ContentSecurityPolicy::Directives {
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#directive-sandbox
|
||||
class SandboxDirective final : public Directive {
|
||||
GC_CELL(SandboxDirective, Directive)
|
||||
GC_DECLARE_ALLOCATOR(SandboxDirective);
|
||||
|
||||
public:
|
||||
virtual ~SandboxDirective() = default;
|
||||
|
||||
[[nodiscard]] virtual Result initialization(Variant<GC::Ref<DOM::Document const>, GC::Ref<HTML::WorkerGlobalScope const>>, GC::Ref<Policy const>) const override;
|
||||
|
||||
private:
|
||||
SandboxDirective(String name, Vector<String> value);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibGC/RootVector.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/Names.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/SerializedPolicy.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
|
|
@ -74,9 +75,38 @@ bool PolicyList::contains_header_delivered_policy() const
|
|||
return !header_delivered_entry.is_end();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#csp-derived-sandboxing-flags
|
||||
HTML::SandboxingFlagSet PolicyList::csp_derived_sandboxing_flags() const
|
||||
{
|
||||
return HTML::SandboxingFlagSet {};
|
||||
// 1. Let directives be an empty ordered set.
|
||||
// NOTE: Since the algorithm only uses the last entry, we instead use a pointer to the last entry.
|
||||
GC::Ptr<Directives::Directive> sandbox_directive = nullptr;
|
||||
|
||||
// 2. For each policy in cspList:
|
||||
for (auto const policy : m_policies) {
|
||||
// 1. If policy's disposition is not "enforce", then continue.
|
||||
if (policy->disposition() != Policy::Disposition::Enforce)
|
||||
continue;
|
||||
|
||||
// 2. If policy's directive set contains a directive whose name is "sandbox", then append that directive to
|
||||
// directives.
|
||||
auto maybe_sandbox_directive = policy->directives().find_if([](auto const& directive) {
|
||||
return directive->name() == Directives::Names::Sandbox;
|
||||
});
|
||||
|
||||
if (!maybe_sandbox_directive.is_end())
|
||||
sandbox_directive = *maybe_sandbox_directive;
|
||||
}
|
||||
|
||||
// 3. If directives is empty, then return an empty sandboxing flag set.
|
||||
if (!sandbox_directive)
|
||||
return HTML::SandboxingFlagSet {};
|
||||
|
||||
// 4. Let directive be directives[directives's size − 1].
|
||||
// NOTE: Already done.
|
||||
|
||||
// 5. Return the result of parsing the sandboxing directive directive.
|
||||
return HTML::parse_a_sandboxing_directive(sandbox_directive->value());
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#enforced
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ class MediaSourceDirective;
|
|||
class ObjectSourceDirective;
|
||||
class ReportToDirective;
|
||||
class ReportUriDirective;
|
||||
class SandboxDirective;
|
||||
class ScriptSourceAttributeDirective;
|
||||
class ScriptSourceDirective;
|
||||
class ScriptSourceElementDirective;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,24 @@
|
|||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#parse-a-sandboxing-directive
|
||||
SandboxingFlagSet parse_a_sandboxing_directive(String const& input)
|
||||
SandboxingFlagSet parse_a_sandboxing_directive(Variant<String, Vector<String>> input)
|
||||
{
|
||||
// 1. Split input on ASCII whitespace, to obtain tokens.
|
||||
auto lowercase_input = input.to_ascii_lowercase();
|
||||
auto tokens = lowercase_input.bytes_as_string_view().split_view_if(Infra::is_ascii_whitespace);
|
||||
Vector<String> tokens;
|
||||
if (input.has<String>()) {
|
||||
auto lowercase_input = input.get<String>().to_ascii_lowercase();
|
||||
auto token_views = lowercase_input.bytes_as_string_view().split_view_if(Infra::is_ascii_whitespace);
|
||||
tokens.ensure_capacity(token_views.size());
|
||||
for (auto token : token_views) {
|
||||
tokens.unchecked_append(MUST(String::from_utf8(token)));
|
||||
}
|
||||
} else {
|
||||
auto const& pre_parsed_tokens = input.get<Vector<String>>();
|
||||
tokens.ensure_capacity(pre_parsed_tokens.size());
|
||||
for (auto const& token : pre_parsed_tokens) {
|
||||
tokens.unchecked_append(token.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Let output be empty.
|
||||
SandboxingFlagSet output {};
|
||||
|
|
|
|||
|
|
@ -36,6 +36,6 @@ enum class SandboxingFlagSet {
|
|||
AK_ENUM_BITWISE_OPERATORS(SandboxingFlagSet);
|
||||
inline bool is_empty(SandboxingFlagSet s) { return (to_underlying(s) & 0x1FFU) == 0; }
|
||||
|
||||
SandboxingFlagSet parse_a_sandboxing_directive(String const& input);
|
||||
SandboxingFlagSet parse_a_sandboxing_directive(Variant<String, Vector<String>> input);
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user