mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-29 21:57:18 +00:00
LibWeb: Parse and propagate the iframe sandbox attribute
This commit is contained in:
parent
a5e2fd2e12
commit
40bb50ac60
Notes:
github-actions[bot]
2025-08-07 17:26:35 +00:00
Author: https://github.com/Lubrsi
Commit: 40bb50ac60
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5765
51 changed files with 1155 additions and 12 deletions
|
@ -555,6 +555,7 @@ set(SOURCES
|
||||||
HTML/PromiseRejectionEvent.cpp
|
HTML/PromiseRejectionEvent.cpp
|
||||||
HTML/RadioNodeList.cpp
|
HTML/RadioNodeList.cpp
|
||||||
HTML/RenderingThread.cpp
|
HTML/RenderingThread.cpp
|
||||||
|
HTML/SandboxingFlagSet.cpp
|
||||||
HTML/Scripting/Agent.cpp
|
HTML/Scripting/Agent.cpp
|
||||||
HTML/Scripting/ClassicScript.cpp
|
HTML/Scripting/ClassicScript.cpp
|
||||||
HTML/Scripting/Environments.cpp
|
HTML/Scripting/Environments.cpp
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <LibWeb/HTML/BrowsingContext.h>
|
#include <LibWeb/HTML/BrowsingContext.h>
|
||||||
#include <LibWeb/HTML/BrowsingContextGroup.h>
|
#include <LibWeb/HTML/BrowsingContextGroup.h>
|
||||||
#include <LibWeb/HTML/HTMLDocument.h>
|
#include <LibWeb/HTML/HTMLDocument.h>
|
||||||
|
#include <LibWeb/HTML/HTMLIFrameElement.h>
|
||||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||||
#include <LibWeb/HTML/SandboxingFlagSet.h>
|
#include <LibWeb/HTML/SandboxingFlagSet.h>
|
||||||
#include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
|
#include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
|
||||||
|
@ -487,11 +488,28 @@ bool BrowsingContext::is_familiar_with(BrowsingContext const& other) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-target-snapshot-params
|
// https://html.spec.whatwg.org/multipage/browsers.html#determining-the-creation-sandboxing-flags
|
||||||
SandboxingFlagSet determine_the_creation_sandboxing_flags(BrowsingContext const&, GC::Ptr<DOM::Element>)
|
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
|
// To determine the creation sandboxing flags for a browsing context browsing context, given null or an element
|
||||||
return {};
|
// 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
|
bool BrowsingContext::has_navigable_been_destroyed() const
|
||||||
|
|
|
@ -53,6 +53,11 @@ void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional<String
|
||||||
{
|
{
|
||||||
Base::attribute_changed(name, old_value, value, namespace_);
|
Base::attribute_changed(name, old_value, value, namespace_);
|
||||||
|
|
||||||
|
if (name == HTML::AttributeNames::sandbox) {
|
||||||
|
if (m_sandbox)
|
||||||
|
m_sandbox->associated_attribute_changed(value.value_or(String {}));
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-2
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-2
|
||||||
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-3
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-3
|
||||||
// Whenever an iframe element with a non-null content navigable has its srcdoc attribute set, changed, or removed,
|
// Whenever an iframe element with a non-null content navigable has its srcdoc attribute set, changed, or removed,
|
||||||
|
@ -62,6 +67,21 @@ void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional<String
|
||||||
if (m_content_navigable) {
|
if (m_content_navigable) {
|
||||||
if (name == AttributeNames::srcdoc || (name == AttributeNames::src && !has_attribute(AttributeNames::srcdoc)))
|
if (name == AttributeNames::srcdoc || (name == AttributeNames::src && !has_attribute(AttributeNames::srcdoc)))
|
||||||
process_the_iframe_attributes();
|
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 = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == HTML::AttributeNames::width || name == HTML::AttributeNames::height) {
|
if (name == HTML::AttributeNames::width || name == HTML::AttributeNames::height) {
|
||||||
|
@ -89,8 +109,13 @@ void HTMLIFrameElement::post_connection()
|
||||||
// The iframe HTML element post-connection steps, given insertedNode, are:
|
// The iframe HTML element post-connection steps, given insertedNode, are:
|
||||||
// 1. Create a new child navigable for insertedNode.
|
// 1. Create a new child navigable for insertedNode.
|
||||||
MUST(create_new_child_navigable(GC::create_function(realm().heap(), [this] {
|
MUST(create_new_child_navigable(GC::create_function(realm().heap(), [this] {
|
||||||
// FIXME: 2. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
|
// 2. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
|
||||||
// value and insertedNode's iframe sandboxing flag set.
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Process the iframe attributes for insertedNode, with initialInsertion set to true.
|
// 3. Process the iframe attributes for insertedNode, with initialInsertion set to true.
|
||||||
process_the_iframe_attributes(InitialInsertion::Yes);
|
process_the_iframe_attributes(InitialInsertion::Yes);
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
|
|
||||||
GC::Ref<DOM::DOMTokenList> sandbox();
|
GC::Ref<DOM::DOMTokenList> sandbox();
|
||||||
|
|
||||||
|
SandboxingFlagSet iframe_sandboxing_flag_set() const { return m_iframe_sandboxing_flag_set; }
|
||||||
|
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -64,6 +66,9 @@ private:
|
||||||
Optional<HighResolutionTime::DOMHighResTimeStamp> m_pending_resource_start_time = {};
|
Optional<HighResolutionTime::DOMHighResTimeStamp> m_pending_resource_start_time = {};
|
||||||
|
|
||||||
GC::Ptr<DOM::DOMTokenList> m_sandbox;
|
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&);
|
void run_iframe_load_event_steps(HTML::HTMLIFrameElement&);
|
||||||
|
|
107
Libraries/LibWeb/HTML/SandboxingFlagSet.cpp
Normal file
107
Libraries/LibWeb/HTML/SandboxingFlagSet.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/EnumBits.h>
|
#include <AK/EnumBits.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
@ -35,4 +36,6 @@ enum class SandboxingFlagSet {
|
||||||
AK_ENUM_BITWISE_OPERATORS(SandboxingFlagSet);
|
AK_ENUM_BITWISE_OPERATORS(SandboxingFlagSet);
|
||||||
inline bool is_empty(SandboxingFlagSet s) { return (to_underlying(s) & 0x1FFU) == 0; }
|
inline bool is_empty(SandboxingFlagSet s) { return (to_underlying(s) & 0x1FFU) == 0; }
|
||||||
|
|
||||||
|
SandboxingFlagSet parse_a_sandboxing_directive(String const& input);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,9 @@ bool is_scripting_enabled(JS::Realm const& realm)
|
||||||
if (!document.page().is_scripting_enabled())
|
if (!document.page().is_scripting_enabled())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// FIXME: Either settings's global object is not a Window object, or settings's global object's associated Document's active sandboxing flag set does not have its sandboxed scripts browsing context flag set.
|
// Either settings's global object is not a Window object, or settings's global object's associated Document's active sandboxing flag set does not have its sandboxed scripts browsing context flag set.
|
||||||
|
if (has_flag(document.active_sandboxing_flag_set(), SandboxingFlagSet::SandboxedScripts))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>noscript parsing when sandbox disables scripting</title>
|
||||||
|
<iframe srcdoc="PASS" sandbox></iframe>
|
||||||
|
<iframe srcdoc="PASS" sandbox></iframe>
|
||||||
|
<iframe srcdoc="P<b>AS</b>S" sandbox></iframe>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<noscript>PASS</noscript>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>noscript parsing when sandbox disables scripting</title>
|
||||||
|
<link rel=match href=../../../../../expected/wpt-import//html/browsers/sandboxing/sandbox-parse-noscript-ref.html>
|
||||||
|
<iframe srcdoc="<noscript>PASS</noscript>" sandbox></iframe>
|
||||||
|
<iframe src="noscript-iframe.html" sandbox></iframe>
|
||||||
|
<iframe srcdoc="<noscript>P<b>AS</b>S</noscript>" sandbox></iframe>
|
|
@ -320,3 +320,16 @@ Text/input/wpt-import/webaudio/the-audio-api/the-periodicwave-interface/periodic
|
||||||
; https://github.com/LadybirdBrowser/ladybird/issues/5333
|
; https://github.com/LadybirdBrowser/ladybird/issues/5333
|
||||||
Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html
|
Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html
|
||||||
Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html
|
Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html
|
||||||
|
|
||||||
|
; Currently always timeout
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/inner-iframe.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-javascript-window-open.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-document-open.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html
|
||||||
|
Text/input/wpt-import/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html
|
||||||
|
|
||||||
|
; Not a ref test, but a subfile of the sandbox-parse-noscript ref test
|
||||||
|
Ref/input/wpt-import/html/browsers/sandboxing/noscript-iframe.html
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Fail
|
||||||
|
Fail CORS with sandboxed iframe
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass DOM access in sandbox='allow-same-origin' iframe is allowed
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass Running script from sandbox='allow-scripts' iframe is allowed
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Fail
|
||||||
|
Fail window.open in sandbox iframe
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass Access to sandbox iframe is disallowed
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass Running script from sandbox iframe is disallowed
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Fail
|
||||||
|
Fail Using document.open() against a document from a different window must not mutate the other window's sandbox flags
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass Inherit sandbox flags from the initiator's frame
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: Timeout
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Timeout
|
||||||
|
Timeout Inherit sandbox flags from the initiator's response
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: Timeout
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Timeout
|
||||||
|
Timeout setting sandbox attribute should not affect current document in iframe
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Pass
|
||||||
|
Pass iframe with sandbox should load with new execution context
|
|
@ -0,0 +1,6 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 1 tests
|
||||||
|
|
||||||
|
1 Fail
|
||||||
|
Fail window.open('about:srcdoc') from sandboxed srcdoc doesn't crash.
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
||||||
|
|
||||||
Found 2 tests
|
Found 2 tests
|
||||||
|
|
||||||
1 Pass
|
2 Pass
|
||||||
1 Fail
|
|
||||||
Pass iframe 'sandbox' ASCII case insensitive, allow-same-orİgin
|
Pass iframe 'sandbox' ASCII case insensitive, allow-same-orİgin
|
||||||
Fail iframe 'sandbox' ASCII case insensitive, allow-ſcripts
|
Pass iframe 'sandbox' ASCII case insensitive, allow-ſcripts
|
|
@ -2,5 +2,5 @@ Harness status: OK
|
||||||
|
|
||||||
Found 1 tests
|
Found 1 tests
|
||||||
|
|
||||||
1 Fail
|
1 Pass
|
||||||
Fail When the scripting flag is disabled, a head start tag in "in head noscript" mode should be ignored
|
Pass When the scripting flag is disabled, a head start tag in "in head noscript" mode should be ignored
|
98
Tests/LibWeb/Text/input/wpt-import/common/utils.js
Normal file
98
Tests/LibWeb/Text/input/wpt-import/common/utils.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* Create an absolute URL from `options` and defaulting unspecified properties to `window.location`.
|
||||||
|
* @param {Object} options - a `Location`-like object
|
||||||
|
* @param {string} options.hostname
|
||||||
|
* @param {string} options.subdomain - prepend subdomain to the hostname
|
||||||
|
* @param {string} options.port
|
||||||
|
* @param {string} options.path
|
||||||
|
* @param {string} options.query
|
||||||
|
* @param {string} options.hash
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function make_absolute_url(options) {
|
||||||
|
var loc = window.location;
|
||||||
|
var protocol = get(options, "protocol", loc.protocol);
|
||||||
|
if (protocol[protocol.length - 1] != ":") {
|
||||||
|
protocol += ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostname = get(options, "hostname", loc.hostname);
|
||||||
|
|
||||||
|
var subdomain = get(options, "subdomain");
|
||||||
|
if (subdomain) {
|
||||||
|
hostname = subdomain + "." + hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
var port = get(options, "port", loc.port)
|
||||||
|
var path = get(options, "path", loc.pathname);
|
||||||
|
var query = get(options, "query", loc.search);
|
||||||
|
var hash = get(options, "hash", loc.hash)
|
||||||
|
|
||||||
|
var url = protocol + "//" + hostname;
|
||||||
|
if (port) {
|
||||||
|
url += ":" + port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path[0] != "/") {
|
||||||
|
url += "/";
|
||||||
|
}
|
||||||
|
url += path;
|
||||||
|
if (query) {
|
||||||
|
if (query[0] != "?") {
|
||||||
|
url += "?";
|
||||||
|
}
|
||||||
|
url += query;
|
||||||
|
}
|
||||||
|
if (hash) {
|
||||||
|
if (hash[0] != "#") {
|
||||||
|
url += "#";
|
||||||
|
}
|
||||||
|
url += hash;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
function get(obj, name, default_val) {
|
||||||
|
if (obj.hasOwnProperty(name)) {
|
||||||
|
return obj[name];
|
||||||
|
}
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new UUID.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function token() {
|
||||||
|
var uuid = [to_hex(rand_int(32), 8),
|
||||||
|
to_hex(rand_int(16), 4),
|
||||||
|
to_hex(0x4000 | rand_int(12), 4),
|
||||||
|
to_hex(0x8000 | rand_int(14), 4),
|
||||||
|
to_hex(rand_int(48), 12)].join("-")
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
function rand_int(bits) {
|
||||||
|
if (bits < 1 || bits > 53) {
|
||||||
|
throw new TypeError();
|
||||||
|
} else {
|
||||||
|
if (bits >= 1 && bits <= 30) {
|
||||||
|
return 0 | ((1 << bits) * Math.random());
|
||||||
|
} else {
|
||||||
|
var high = (0 | ((1 << (bits - 30)) * Math.random())) * (1 << 30);
|
||||||
|
var low = 0 | ((1 << 30) * Math.random());
|
||||||
|
return high + low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
function to_hex(x, length) {
|
||||||
|
var rv = x.toString(16);
|
||||||
|
while (rv.length < length) {
|
||||||
|
rv = "0" + rv;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<iframe sandbox="allow-scripts" src="../resources/sandboxed-iframe.html"></iframe>
|
||||||
|
<script>
|
||||||
|
promise_test(async (t) => {
|
||||||
|
const message = await new Promise((resolve) => {
|
||||||
|
window.addEventListener('message', e => resolve(e.data));
|
||||||
|
});
|
||||||
|
assert_equals(message, 'PASS');
|
||||||
|
}, 'CORS with sandboxed iframe');
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<script>
|
||||||
|
async function no_cors_should_be_rejected() {
|
||||||
|
let thrown = false;
|
||||||
|
try {
|
||||||
|
const resp = await fetch('top.txt');
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
if (!thrown) {
|
||||||
|
throw Error('fetching "top.txt" should be rejected.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function null_origin_should_be_accepted() {
|
||||||
|
const url = 'top.txt?pipe=header(access-control-allow-origin,null)|' +
|
||||||
|
'header(cache-control,no-store)';
|
||||||
|
const resp = await fetch(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
try {
|
||||||
|
await no_cors_should_be_rejected();
|
||||||
|
await null_origin_should_be_accepted();
|
||||||
|
parent.postMessage('PASS', '*');
|
||||||
|
} catch (e) {
|
||||||
|
parent.postMessage(e.message, '*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
top.calledFromIframe();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="inner">foo</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>DOM access in sandbox="allow-same-origin" iframe</title>
|
||||||
|
<link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
|
||||||
|
<link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>DOM access in sandbox="allow-same-origin" iframe</h1>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var t = async_test("DOM access in sandbox='allow-same-origin' iframe is allowed")
|
||||||
|
var called = 0;
|
||||||
|
function calledFromIframe() {
|
||||||
|
called++;
|
||||||
|
}
|
||||||
|
function loaded() {
|
||||||
|
assert_equals(document.getElementById('sandboxedframe').contentWindow.document.getElementById('inner').innerHTML, 'foo');
|
||||||
|
assert_equals(called, 0);
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<iframe src="../../../html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Script execution in sandbox="allow-scripts" iframe</title>
|
||||||
|
<link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
|
||||||
|
<link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Script execution in sandbox="allow-scripts" iframe</h1>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var t = async_test("Running script from sandbox='allow-scripts' iframe is allowed")
|
||||||
|
var called = 0;
|
||||||
|
function calledFromIframe() {
|
||||||
|
called++;
|
||||||
|
}
|
||||||
|
function loaded() {
|
||||||
|
assert_equals(called, 1);
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<iframe src="../../../html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-scripts allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>window.open in sandbox iframe</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../../common/utils.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
setup({single_test: true});
|
||||||
|
// check that the popup's URL is not loaded
|
||||||
|
const uuid = token();
|
||||||
|
async function assert_popup_not_loaded() {
|
||||||
|
const response = await fetch(`/fetch/api/resources/stash-take.py?key=${uuid}`);
|
||||||
|
assert_equals(await response.json(), null); // is "loaded" if it loads
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for message from the iframe
|
||||||
|
window.onmessage = e => {
|
||||||
|
assert_equals(e.data, 'null', 'return value of window.open (stringified)');
|
||||||
|
step_timeout(async () => {
|
||||||
|
await assert_popup_not_loaded();
|
||||||
|
done();
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
iframe.sandbox = 'allow-scripts';
|
||||||
|
iframe.srcdoc = `
|
||||||
|
<script>
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = window.open('/fetch/api/resources/stash-put.py?key=${uuid}&value=loaded', '_blank');
|
||||||
|
} catch(ex) {
|
||||||
|
result = ex;
|
||||||
|
}
|
||||||
|
parent.postMessage(String(result), '*');
|
||||||
|
<\/script>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
</script>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Access to sandbox iframe</title>
|
||||||
|
<link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#sandboxed-origin-browsing-context-flag">
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#integration-with-idl">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Access to sandbox iframe</h1>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var t = async_test("Access to sandbox iframe is disallowed")
|
||||||
|
var called = 0;
|
||||||
|
function calledFromIframe() {
|
||||||
|
called++;
|
||||||
|
}
|
||||||
|
function loaded() {
|
||||||
|
t.step(() => {
|
||||||
|
assert_throws_dom("SecurityError", () => {
|
||||||
|
document.getElementById('sandboxedframe').contentWindow.document;
|
||||||
|
});
|
||||||
|
assert_equals(called, 0);
|
||||||
|
t.done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<iframe src="../../../html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
</html>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
async_test(t => {
|
||||||
|
let i = document.createElement('iframe');
|
||||||
|
i.sandbox = "allow-same-origin allow-popups allow-popups-to-escape-sandbox";
|
||||||
|
i.srcdoc = `<a target='_blank' rel='opener'
|
||||||
|
href="javascript:window.opener.top.postMessage('FAIL', '*');">Click me!</a>
|
||||||
|
<a target='_blank' rel='opener'
|
||||||
|
href="./resources/post-done-to-opener.html">Click me next!</a>`;
|
||||||
|
|
||||||
|
i.onload = _ => {
|
||||||
|
// Since the frame is sandboxed, but allow-same-origin, we can reach into it to grab the
|
||||||
|
// anchor element to click. We'll click the `javascript:` URL first, then pop up a new
|
||||||
|
// window that posts `DONE`.
|
||||||
|
//
|
||||||
|
// TODO(mkwst): This feels like a race, but it's one that we consistently win when I'm
|
||||||
|
// running the test locally 10,000 times. Good enough!™
|
||||||
|
i.contentDocument.body.querySelectorAll('a')[0].click();
|
||||||
|
i.contentDocument.body.querySelectorAll('a')[1].click();
|
||||||
|
};
|
||||||
|
document.body.appendChild(i);
|
||||||
|
|
||||||
|
window.addEventListener('message', t.step_func(e => {
|
||||||
|
assert_not_equals(e.data, "FAIL");
|
||||||
|
if (e.data == "DONE")
|
||||||
|
t.done();
|
||||||
|
}));
|
||||||
|
}, "Sandboxed => unsandboxed popup");
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Script execution in sandbox iframe</title>
|
||||||
|
<link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
|
||||||
|
<link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Script execution in sandbox iframe</h1>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var t = async_test("Running script from sandbox iframe is disallowed")
|
||||||
|
var called = 0;
|
||||||
|
function calledFromIframe() {
|
||||||
|
called++;
|
||||||
|
}
|
||||||
|
function loaded() {
|
||||||
|
assert_equals(called, 0);
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<iframe src="../../../html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id=log></div>
|
||||||
|
<script src="../../../html/browsers/sandboxing/sandbox-document-open-mutation.window.js"></script>
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Return whether the current context is sandboxed or not. The implementation do
|
||||||
|
// not matter much, but might have to change over time depending on what side
|
||||||
|
// effect sandbox flag have. Feel free to update as needed.
|
||||||
|
const is_sandboxed = () => {
|
||||||
|
try {
|
||||||
|
document.domain = document.domain;
|
||||||
|
return "not sandboxed";
|
||||||
|
} catch (error) {
|
||||||
|
return "sandboxed";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async test => {
|
||||||
|
const message = new Promise(r => window.addEventListener("message", r));
|
||||||
|
|
||||||
|
const iframe_unsandboxed = document.createElement("iframe");
|
||||||
|
document.body.appendChild(iframe_unsandboxed);
|
||||||
|
|
||||||
|
const iframe_sandboxed = document.createElement("iframe");
|
||||||
|
iframe_sandboxed.sandbox = "allow-same-origin allow-scripts";
|
||||||
|
document.body.appendChild(iframe_sandboxed);
|
||||||
|
|
||||||
|
iframe_sandboxed.srcdoc = `
|
||||||
|
<script>
|
||||||
|
parent.frames[0].document.write(\`
|
||||||
|
<script>
|
||||||
|
const is_sandboxed = ${is_sandboxed};
|
||||||
|
window.parent.postMessage(is_sandboxed(), '*');
|
||||||
|
</scr\`+\`ipt>
|
||||||
|
\`);
|
||||||
|
parent.frames[0].document.close();
|
||||||
|
</scr`+`ipt>
|
||||||
|
`;
|
||||||
|
assert_equals((await message).data, "not sandboxed");
|
||||||
|
|
||||||
|
}, "Using document.open() against a document from a different window must not" +
|
||||||
|
" mutate the other window's sandbox flags");
|
|
@ -0,0 +1,50 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>
|
||||||
|
Check sandbox-flags aren't lost after using document.open().
|
||||||
|
</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
promise_test(async test => {
|
||||||
|
let message = new Promise(resolve =>
|
||||||
|
window.addEventListener("message", event => resolve(event.data))
|
||||||
|
);
|
||||||
|
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
|
||||||
|
iframe.setAttribute("src", "./resources/document-open.html")
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
assert_equals(await message, "document-domain-is-disallowed");
|
||||||
|
}, "document.open()");
|
||||||
|
|
||||||
|
promise_test(async test => {
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
|
||||||
|
iframe.setAttribute("src", "/common/blank.html");
|
||||||
|
let loaded = new Promise(resolve => iframe.onload = resolve);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
await loaded;
|
||||||
|
|
||||||
|
let message = new Promise(resolve =>
|
||||||
|
window.addEventListener("message", event => resolve(event.data))
|
||||||
|
);
|
||||||
|
|
||||||
|
iframe.contentDocument.write(`
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
document.domain = document.domain;
|
||||||
|
parent.postMessage('document-domain-is-allowed', '*');
|
||||||
|
} catch (error) {
|
||||||
|
parent.postMessage('document-domain-is-disallowed', '*');
|
||||||
|
}
|
||||||
|
</sc`+`ript>
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert_equals(await message, "document-domain-is-disallowed");
|
||||||
|
}, "other_document.open()");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Inherit sandbox flags from the initiator's frame</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Check sandbox flags are properly inherited when a document initiate a
|
||||||
|
// navigation inside another frame that it doesn't own directly.
|
||||||
|
|
||||||
|
// This check the sandbox flags defined by the frame. See also the other test
|
||||||
|
// about sandbox flags defined by the response (e.g. CSP sandbox):
|
||||||
|
// => sandbox-inherited-from-initiators-response.html
|
||||||
|
|
||||||
|
// Return a promise, resolving when |element| triggers |event_name| event.
|
||||||
|
let future = (element, event_name) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
element.addEventListener(event_name, event => resolve(event))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async test => {
|
||||||
|
const iframe_1 = document.createElement("iframe");
|
||||||
|
const iframe_2 = document.createElement("iframe");
|
||||||
|
|
||||||
|
iframe_1.id = "iframe_1";
|
||||||
|
iframe_2.id = "iframe_2";
|
||||||
|
|
||||||
|
const iframe_1_script = encodeURI(`
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
document.domain = document.domain;
|
||||||
|
parent.postMessage("not sandboxed", "*");
|
||||||
|
} catch (exception) {
|
||||||
|
parent.postMessage("sandboxed", "*");
|
||||||
|
}
|
||||||
|
</scr`+`ipt>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const iframe_2_script = `
|
||||||
|
<script>
|
||||||
|
const iframe_1 = parent.document.querySelector("#iframe_1");
|
||||||
|
iframe_1.src = "data:text/html,${iframe_1_script}";
|
||||||
|
</scr`+`ipt>
|
||||||
|
`;
|
||||||
|
|
||||||
|
iframe_2.sandbox = "allow-scripts allow-same-origin";
|
||||||
|
iframe_2.srcdoc = iframe_2_script;
|
||||||
|
|
||||||
|
// Insert |iframe_1|. It will load the initial empty document, with no sandbox
|
||||||
|
// flags.
|
||||||
|
const iframe_1_load_1 = future(iframe_1, "load");
|
||||||
|
document.body.appendChild(iframe_1);
|
||||||
|
await iframe_1_load_1;
|
||||||
|
|
||||||
|
// Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1|
|
||||||
|
// to navigate toward a data-url, which should inherit the sandbox flags.
|
||||||
|
const iframe_1_reply = future(window, "message");
|
||||||
|
document.body.appendChild(iframe_2);
|
||||||
|
const result = await iframe_1_reply;
|
||||||
|
|
||||||
|
assert_equals("sandboxed", result.data);
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Inherit sandbox flags from the initiator's response</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Check sandbox flags are properly inherited when a document initiate a
|
||||||
|
// navigation inside another frame that it doesn't own directly.
|
||||||
|
|
||||||
|
// This check the sandbox flags defined by the response (e.g. CSP sandbox). See
|
||||||
|
// also the other test about sandbox flags inherited from the frame.
|
||||||
|
// => sandbox-inherited-from-initiators-frame.html
|
||||||
|
|
||||||
|
// Return a promise, resolving when |element| triggers |event_name| event.
|
||||||
|
let future = (element, event_name) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
element.addEventListener(event_name, event => resolve(event))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async test => {
|
||||||
|
const iframe_1 = document.createElement("iframe");
|
||||||
|
const iframe_2 = document.createElement("iframe");
|
||||||
|
|
||||||
|
iframe_1.id = "iframe_1";
|
||||||
|
iframe_2.id = "iframe_2";
|
||||||
|
|
||||||
|
iframe_2.src =
|
||||||
|
"./resources/sandbox-inherited-from-initiator-response-helper.html";
|
||||||
|
|
||||||
|
// Insert |iframe_1|. It will load the initial empty document, with no sandbox
|
||||||
|
// flags.
|
||||||
|
const iframe_1_load_1 = future(iframe_1, "load");
|
||||||
|
document.body.appendChild(iframe_1);
|
||||||
|
await iframe_1_load_1;
|
||||||
|
|
||||||
|
// Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1|
|
||||||
|
// to navigate toward a data-url, which should inherit the sandbox flags.
|
||||||
|
const iframe_1_reply = future(window, "message");
|
||||||
|
document.body.appendChild(iframe_2);
|
||||||
|
const result = await iframe_1_reply;
|
||||||
|
|
||||||
|
assert_equals("sandboxed", result.data);
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -0,0 +1,154 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Inherit sandbox from CSP embedded enforcement</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../../common/get-host-info.sub.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Check sandbox flags are properly defined when its parent requires them and
|
||||||
|
// the child allows it.
|
||||||
|
|
||||||
|
const same_origin = get_host_info().HTTP_ORIGIN;
|
||||||
|
const cross_origin = get_host_info().HTTP_REMOTE_ORIGIN;
|
||||||
|
const check_sandbox_url =
|
||||||
|
"/html/browsers/sandboxing/resources/check-sandbox-flags.html?pipe=";
|
||||||
|
const allow_csp_from_star = "|header(Allow-CSP-From,*)";
|
||||||
|
|
||||||
|
// Return a promise, resolving when |element| triggers |event_name| event.
|
||||||
|
const future = (element, event_name, source) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
element.addEventListener(event_name, event => {
|
||||||
|
if (!source || source.contentWindow == event.source)
|
||||||
|
resolve(event)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const check_sandbox_script = `
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
document.domain = document.domain;
|
||||||
|
parent.postMessage("document-domain-is-allowed", "*");
|
||||||
|
} catch (exception) {
|
||||||
|
parent.postMessage("document-domain-is-disallowed", "*");
|
||||||
|
}
|
||||||
|
</scr`+`ipt>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const sandbox_policy = "sandbox allow-scripts allow-same-origin";
|
||||||
|
|
||||||
|
// Test using the modern async/await primitives are easier to read/write.
|
||||||
|
// However they run sequentially, contrary to async_test. This is the parallel
|
||||||
|
// version, to avoid timing out.
|
||||||
|
let promise_test_parallel = (promise, description) => {
|
||||||
|
async_test(test => {
|
||||||
|
promise(test)
|
||||||
|
.then(() => {test.done();})
|
||||||
|
.catch(test.step_func(error => { throw error; }));
|
||||||
|
}, description);
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
|
||||||
|
// The <iframe> immediately hosts the initial empty document after being
|
||||||
|
// appended into the DOM. It will, as long as its 'src' isn't loaded. That's
|
||||||
|
// why a page do not load is being used.
|
||||||
|
iframe.src = "/fetch/api/resources/infinite-slow-response.py";
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
iframe.contentDocument.write(check_sandbox_script);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
iframe.remove();
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "initial empty document");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.src = "data:text/html,dummy";
|
||||||
|
|
||||||
|
const iframe_load_1 = future(iframe, "load");
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
await iframe_load_1;
|
||||||
|
|
||||||
|
const iframe_load_2 = future(iframe, "load");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
iframe.src = "about:blank";
|
||||||
|
await iframe_load_2;
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
iframe.contentDocument.write(check_sandbox_script);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "about:blank");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
iframe.src =
|
||||||
|
`data:text/html,${encodeURI(check_sandbox_script)}`;
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "data-url");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
iframe.srcdoc = check_sandbox_script;
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "srcdoc");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
|
||||||
|
const blob = new Blob([check_sandbox_script], { type: "text/html" });
|
||||||
|
|
||||||
|
iframe.src = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "blob URL");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
iframe.src = same_origin + check_sandbox_url + allow_csp_from_star;
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "same-origin");
|
||||||
|
|
||||||
|
promise_test_parallel(async test => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.csp = sandbox_policy;
|
||||||
|
iframe.src = cross_origin + check_sandbox_url + allow_csp_from_star;
|
||||||
|
|
||||||
|
const iframe_reply = future(window, "message", iframe);
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
const result = await iframe_reply;
|
||||||
|
|
||||||
|
assert_equals(result.data, "document-domain-is-disallowed");
|
||||||
|
}, "cross-origin");
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>
|
||||||
|
Check sandbox-flags inheritance in case of javascript window reuse.
|
||||||
|
</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
promise_test(async test => {
|
||||||
|
let message = new Promise(resolve =>
|
||||||
|
window.addEventListener("message", event => resolve(event.data))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an initial empty document in the iframe, sandboxed. It will attempt
|
||||||
|
// to load a slow page, but won't have time.
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
|
||||||
|
iframe.src = "/fetch/api/resources/infinite-slow-response.py";
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
// Remove sandbox flags. This should apply to documents committed from
|
||||||
|
// navigations started after this instruction.
|
||||||
|
iframe.removeAttribute("sandbox");
|
||||||
|
iframe.src = "./resources/check-sandbox-flags.html";
|
||||||
|
|
||||||
|
// The window is reused, but the new sandbox flags should be used.
|
||||||
|
assert_equals(await message, "document-domain-is-allowed");
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>window.open in sandbox iframe</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../../common/utils.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
promise_test(async test => {
|
||||||
|
let message = new Promise(resolve => {
|
||||||
|
window.addEventListener("message", event => resolve(event.data));
|
||||||
|
});
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.sandbox = "allow-scripts allow-popups allow-same-origin";
|
||||||
|
iframe.src = "./resources/sandbox-javascript-window-open.html";
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
assert_equals(await message, "disallow-document-domain");
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Sandbox Navigation Timing</title>
|
||||||
|
<script src=../../../resources/testharness.js></script>
|
||||||
|
<script src=../../../resources/testharnessreport.js></script>
|
||||||
|
<html></html>
|
||||||
|
<script>
|
||||||
|
const sandboxUrl = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1) + 'sandbox-navigation-timing-iframe.tentative.html';
|
||||||
|
async_test(t => {
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
iframe.src = sandboxUrl;
|
||||||
|
document.body.appendChild(iframe); // Navigation starts; value of sandbox flags locked on.
|
||||||
|
// This should not affect the sandbox value used for both about:blank document
|
||||||
|
// and the final document in iframe.
|
||||||
|
iframe.sandbox = 'allow-scripts';
|
||||||
|
const iframeAboutBlankDocument = iframe.contentDocument;
|
||||||
|
|
||||||
|
iframe.onload = t.step_func(() => {
|
||||||
|
const iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body');
|
||||||
|
assert_equals(iframeAboutBlankContents[0].tagName, "BODY",
|
||||||
|
"about:blank document's contents should still be accessible");
|
||||||
|
|
||||||
|
iframe.contentWindow.postMessage("is iframe sandboxed?", "*");
|
||||||
|
});
|
||||||
|
window.onmessage = t.step_func_done(e => {
|
||||||
|
assert_equals(e.data.result, 'iframe not sandboxed');
|
||||||
|
});
|
||||||
|
}, 'setting sandbox attribute should not affect current document in iframe');
|
||||||
|
</script>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Reuse of iframe about:blank document execution context</title>
|
||||||
|
<link rel="author" title="Dan Clark" href="mailto:daniec@microsoft.com">
|
||||||
|
<link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Reuse of iframe about:blank document execution context in sandbox="allow-scripts" iframe</h1>
|
||||||
|
<script type="text/javascript">
|
||||||
|
async_test(t => {
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
let iframeAboutBlankDocument = iframe.contentDocument;
|
||||||
|
assert_equals(iframeAboutBlankDocument.URL, "about:blank");
|
||||||
|
|
||||||
|
iframe.sandbox = "allow-scripts";
|
||||||
|
iframe.src = './sandbox-new-execution-context-iframe.html';
|
||||||
|
|
||||||
|
iframe.onload = t.step_func_done(() => {
|
||||||
|
assert_equals(iframe.contentDocument, null,
|
||||||
|
"New document in sandboxed iframe should have opaque origin");
|
||||||
|
|
||||||
|
assert_equals(Object.getPrototypeOf(iframeAboutBlankDocument).changeFromSandboxedIframe, undefined,
|
||||||
|
"Sandboxed iframe contents should not have been able to mess with type system of about:blank document");
|
||||||
|
|
||||||
|
let iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body');
|
||||||
|
assert_equals(iframeAboutBlankContents[0].tagName, "BODY",
|
||||||
|
"about:blank document's contents should still be accessible");
|
||||||
|
});
|
||||||
|
},"iframe with sandbox should load with new execution context");
|
||||||
|
</script>
|
||||||
|
<div id="log"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>window.open("about:srcdoc") from a sandboxed iframe</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Check what happens when executing window.open("about:srcdoc") from a
|
||||||
|
// sandboxed iframe. Srcdoc can't be loaded in the main frame. It should
|
||||||
|
// result in an error page. The error page should be cross-origin with the
|
||||||
|
// opener.
|
||||||
|
//
|
||||||
|
// This test covers an interesting edge case. A main frame should inherit
|
||||||
|
// sandbox flags. However the document loaded is an internal error page. This
|
||||||
|
// might trigger some assertions, especially if the implementation wrongly
|
||||||
|
// applies the sandbox flags of the opener to the internal error page document.
|
||||||
|
//
|
||||||
|
// This test is mainly a coverage test. It passes if it doesn't crash.
|
||||||
|
async_test(test => {
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.sandbox = "allow-scripts allow-popups allow-same-origin";
|
||||||
|
iframe.srcdoc = `
|
||||||
|
<script>
|
||||||
|
let w = window.open();
|
||||||
|
onunload = () => w.close();
|
||||||
|
|
||||||
|
let notify = () => {
|
||||||
|
try {
|
||||||
|
w.origin; // Will fail after navigating to about:srcdoc.
|
||||||
|
parent.postMessage("pending", "*");
|
||||||
|
} catch (e) {
|
||||||
|
parent.postMessage("done", "*");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener("message", notify);
|
||||||
|
notify();
|
||||||
|
|
||||||
|
w.location = "about:srcdoc"; // Error page.
|
||||||
|
</scr`+`ipt>
|
||||||
|
`;
|
||||||
|
|
||||||
|
let closed = false;
|
||||||
|
addEventListener("message", event => {
|
||||||
|
closed = (event.data === "done");
|
||||||
|
iframe.contentWindow.postMessage("ping","*");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
test.step_wait_func_done(()=>closed);
|
||||||
|
}, "window.open('about:srcdoc') from sandboxed srcdoc doesn't crash.");
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue