LibWeb: Parse and propagate the iframe sandbox attribute

This commit is contained in:
Luke Wilde 2024-12-06 14:53:03 +00:00
parent a39c84de5f
commit e390f870b3
6 changed files with 160 additions and 6 deletions

View file

@ -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

View file

@ -18,6 +18,7 @@
#include <LibWeb/HTML/DocumentState.h>
#include <LibWeb/HTML/HTMLAnchorElement.h>
#include <LibWeb/HTML/HTMLDocument.h>
#include <LibWeb/HTML/HTMLIFrameElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/NavigableContainer.h>
#include <LibWeb/HTML/SandboxingFlagSet.h>
@ -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<DOM::Element>)
// 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<DOM::Element> 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<HTMLIFrameElement>(embedder.ptr())) {
auto const& iframe_element = static_cast<HTMLIFrameElement const&>(*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

View file

@ -59,6 +59,21 @@ void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional<String
if (m_content_navigable) {
if (name == AttributeNames::srcdoc || (name == AttributeNames::src && !has_attribute(AttributeNames::srcdoc)))
process_the_iframe_attributes();
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:iframe-sandboxing-flag-set-2
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:iframe-sandboxing-flag-set-3
// When an iframe element's sandbox attribute is set or changed while it has a non-null content navigable, the
// user agent must parse the sandboxing directive given the attribute's value and the iframe element's iframe
// sandboxing flag set.
// When an iframe element's sandbox attribute is removed while it has a non-null content navigable, the user
// agent must empty the iframe element's iframe sandboxing flag set.
if (name == AttributeNames::sandbox) {
if (value.has_value()) {
m_iframe_sandboxing_flag_set = parse_a_sandboxing_directive(value.value());
} else {
m_iframe_sandboxing_flag_set = {};
}
}
}
}
@ -80,8 +95,13 @@ void HTMLIFrameElement::inserted()
// 2. Create a new child navigable for insertedNode.
MUST(create_new_child_navigable(GC::create_function(realm().heap(), [this] {
// FIXME: 3. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
// value and insertedNode's iframe sandboxing flag set.
// 3. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
// value and insertedNode's iframe sandboxing flag set.
if (has_attribute(AttributeNames::sandbox)) {
auto sandbox_attribute = attribute(AttributeNames::sandbox);
VERIFY(sandbox_attribute.has_value());
m_iframe_sandboxing_flag_set = parse_a_sandboxing_directive(sandbox_attribute.value());
}
// 4. Process the iframe attributes for insertedNode, with initialInsertion set to true.
process_the_iframe_attributes(true);

View file

@ -33,6 +33,8 @@ public:
GC::Ref<DOM::DOMTokenList> 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<HighResolutionTime::DOMHighResTimeStamp> m_pending_resource_start_time = {};
GC::Ptr<DOM::DOMTokenList> 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&);

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/SandboxingFlagSet.h>
#include <LibWeb/Infra/CharacterTypes.h>
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;
}
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/EnumBits.h>
#include <AK/String.h>
#include <AK/Types.h>
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);
}