diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index c1d3a2d2ecb..eef59b877b7 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -490,6 +490,7 @@ set(SOURCES HTML/PotentialCORSRequest.cpp HTML/PromiseRejectionEvent.cpp HTML/RadioNodeList.cpp + HTML/SandboxingFlagSet.cpp HTML/Scripting/ClassicScript.cpp HTML/Scripting/Environments.cpp HTML/Scripting/EnvironmentSettingsSnapshot.cpp diff --git a/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Libraries/LibWeb/HTML/BrowsingContext.cpp index baa00dd32fb..d20fa573be5 100644 --- a/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -504,11 +505,28 @@ bool BrowsingContext::is_familiar_with(BrowsingContext const& other) const return false; } -// https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-target-snapshot-params -SandboxingFlagSet determine_the_creation_sandboxing_flags(BrowsingContext const&, GC::Ptr) +// https://html.spec.whatwg.org/multipage/browsers.html#determining-the-creation-sandboxing-flags +SandboxingFlagSet determine_the_creation_sandboxing_flags(BrowsingContext const& browsing_context, GC::Ptr embedder) { - // FIXME: Populate this once we have the proper flag sets on BrowsingContext - return {}; + // To determine the creation sandboxing flags for a browsing context browsing context, given null or an element + // embedder, return the union of the flags that are present in the following sandboxing flag sets: + SandboxingFlagSet sandboxing_flags {}; + + // - If embedder is null, then: the flags set on browsing context's popup sandboxing flag set. + if (!embedder) { + sandboxing_flags |= browsing_context.popup_sandboxing_flag_set(); + } else { + // - If embedder is an element, then: the flags set on embedder's iframe sandboxing flag set. + if (is(embedder.ptr())) { + auto const& iframe_element = static_cast(*embedder); + sandboxing_flags |= iframe_element.iframe_sandboxing_flag_set(); + } + + // - If embedder is an element, then: the flags set on embedder's node document's active sandboxing flag set. + sandboxing_flags |= embedder->document().active_sandboxing_flag_set(); + } + + return sandboxing_flags; } bool BrowsingContext::has_navigable_been_destroyed() const diff --git a/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp b/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp index 022936135c5..cd04e772f1c 100644 --- a/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp @@ -59,6 +59,21 @@ void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional sandbox(); + SandboxingFlagSet iframe_sandboxing_flag_set() const { return m_iframe_sandboxing_flag_set; } + virtual void visit_edges(Cell::Visitor&) override; private: @@ -59,6 +61,9 @@ private: Optional m_pending_resource_start_time = {}; GC::Ptr m_sandbox; + + // https://html.spec.whatwg.org/multipage/browsers.html#iframe-sandboxing-flag-set + SandboxingFlagSet m_iframe_sandboxing_flag_set {}; }; void run_iframe_load_event_steps(HTML::HTMLIFrameElement&); diff --git a/Libraries/LibWeb/HTML/SandboxingFlagSet.cpp b/Libraries/LibWeb/HTML/SandboxingFlagSet.cpp new file mode 100644 index 00000000000..cb73808eab7 --- /dev/null +++ b/Libraries/LibWeb/HTML/SandboxingFlagSet.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/browsers.html#parse-a-sandboxing-directive +SandboxingFlagSet parse_a_sandboxing_directive(String const& 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); + + // 2. Let output be empty. + SandboxingFlagSet output {}; + + // 3. Add the following flags to output: + // - The sandboxed navigation browsing context flag. + output |= SandboxingFlagSet::SandboxedNavigation; + + // - The sandboxed auxiliary navigation browsing context flag, unless tokens contains the allow-popups keyword. + if (!tokens.contains_slow("allow-popups"sv)) + output |= SandboxingFlagSet::SandboxedAuxiliaryNavigation; + + // - The sandboxed top-level navigation without user activation browsing context flag, unless tokens contains the + // allow-top-navigation keyword. + if (!tokens.contains_slow("allow-top-navigation"sv)) + output |= SandboxingFlagSet::SandboxedTopLevelNavigationWithoutUserActivation; + + // - The sandboxed top-level navigation with user activation browsing context flag, unless tokens contains either + // the allow-top-navigation-by-user-activation keyword or the allow-top-navigation keyword. + // Spec Note: This means that if the allow-top-navigation is present, the allow-top-navigation-by-user-activation + // keyword will have no effect. For this reason, specifying both is a document conformance error. + if (!tokens.contains_slow("allow-top-navigation"sv) && !tokens.contains_slow("allow-top-navigation-by-user-activation"sv)) + output |= SandboxingFlagSet::SandboxedTopLevelNavigationWithUserActivation; + + // - The sandboxed origin browsing context flag, unless the tokens contains the allow-same-origin keyword. + // Spec Note: The allow-same-origin keyword is intended for two cases. + // + // First, it can be used to allow content from the same site to be sandboxed to disable scripting, + // while still allowing access to the DOM of the sandboxed content. + // + // Second, it can be used to embed content from a third-party site, sandboxed to prevent that site from + // opening popups, etc, without preventing the embedded page from communicating back to its originating + // site, using the database APIs to store data, etc. + if (!tokens.contains_slow("allow-same-origin"sv)) + output |= SandboxingFlagSet::SandboxedOrigin; + + // - The sandboxed forms browsing context flag, unless tokens contains the allow-forms keyword. + if (!tokens.contains_slow("allow-forms"sv)) + output |= SandboxingFlagSet::SandboxedForms; + + // - The sandboxed pointer lock browsing context flag, unless tokens contains the allow-pointer-lock keyword. + if (!tokens.contains_slow("allow-pointer-lock"sv)) + output |= SandboxingFlagSet::SandboxedPointerLock; + + // - The sandboxed scripts browsing context flag, unless tokens contains the allow-scripts keyword. + // - The sandboxed automatic features browsing context flag, unless tokens contains the allow-scripts keyword + // (defined above). + // Spec Note: This flag is relaxed by the same keyword as scripts, because when scripts are enabled these features + // are trivially possible anyway, and it would be unfortunate to force authors to use script to do them + // when sandboxed rather than allowing them to use the declarative features. + if (!tokens.contains_slow("allow-scripts"sv)) { + output |= SandboxingFlagSet::SandboxedScripts; + output |= SandboxingFlagSet::SandboxedAutomaticFeatures; + } + + // - The sandboxed document.domain browsing context flag. + output |= SandboxingFlagSet::SandboxedDocumentDomain; + + // - The sandbox propagates to auxiliary browsing contexts flag, unless tokens contains the + // allow-popups-to-escape-sandbox keyword. + if (!tokens.contains_slow("allow-popups-to-escape-sandbox"sv)) + output |= SandboxingFlagSet::SandboxPropagatesToAuxiliaryBrowsingContexts; + + // - The sandboxed modals flag, unless tokens contains the allow-modals keyword. + if (!tokens.contains_slow("allow-modals"sv)) + output |= SandboxingFlagSet::SandboxedModals; + + // - The sandboxed orientation lock browsing context flag, unless tokens contains the allow-orientation-lock + // keyword. + if (!tokens.contains_slow("allow-orientation-lock"sv)) + output |= SandboxingFlagSet::SandboxedOrientationLock; + + // - The sandboxed presentation browsing context flag, unless tokens contains the allow-presentation keyword. + if (!tokens.contains_slow("allow-presentation"sv)) + output |= SandboxingFlagSet::SandboxedPresentation; + + // - The sandboxed downloads browsing context flag, unless tokens contains the allow-downloads keyword. + if (!tokens.contains_slow("allow-downloads"sv)) + output |= SandboxingFlagSet::SandboxedDownloads; + + // - The sandboxed custom protocols navigation browsing context flag, unless tokens contains either the + // allow-top-navigation-to-custom-protocols keyword, the allow-popups keyword, or the allow-top-navigation + // keyword. + if (!tokens.contains_slow("allow-top-navigation-to-custom-protocols"sv) && !tokens.contains_slow("allow-popups"sv) && !tokens.contains_slow("allow-top-navigation"sv)) + output |= SandboxingFlagSet::SandboxedCustomProtocols; + + return output; +} + +} diff --git a/Libraries/LibWeb/HTML/SandboxingFlagSet.h b/Libraries/LibWeb/HTML/SandboxingFlagSet.h index 75c7f1a3402..696eec0e27d 100644 --- a/Libraries/LibWeb/HTML/SandboxingFlagSet.h +++ b/Libraries/LibWeb/HTML/SandboxingFlagSet.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include namespace Web::HTML { @@ -35,4 +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); + }