mirror of
https://github.com/servo/servo.git
synced 2025-09-10 15:08:21 +01:00
script: Implement document's active sandboxing flag set (#39079)
Implements document's active sandboxing flags. These are currently populated only from CSP-derived sandboxing flags for a new document, when defined in the CSP. Testing: 1 new pass, and some new wpt's are added to test points in the spec where these flags influence behaviour. Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
parent
f722419861
commit
989c0d8994
10 changed files with 156 additions and 15 deletions
|
@ -19,6 +19,7 @@ use canvas_traits::canvas::CanvasId;
|
||||||
use canvas_traits::webgl::{WebGLContextId, WebGLMsg};
|
use canvas_traits::webgl::{WebGLContextId, WebGLMsg};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
|
use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
|
||||||
|
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||||
use content_security_policy::{CspList, PolicyDisposition};
|
use content_security_policy::{CspList, PolicyDisposition};
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use cssparser::match_ignore_ascii_case;
|
use cssparser::match_ignore_ascii_case;
|
||||||
|
@ -569,6 +570,10 @@ pub(crate) struct Document {
|
||||||
/// The global custom element reaction stack for this script thread.
|
/// The global custom element reaction stack for this script thread.
|
||||||
#[conditional_malloc_size_of]
|
#[conditional_malloc_size_of]
|
||||||
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
|
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
|
||||||
|
#[no_trace]
|
||||||
|
#[ignore_malloc_size_of = "type from external crate"]
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#active-sandboxing-flag-set>,
|
||||||
|
active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -3439,6 +3444,7 @@ impl Document {
|
||||||
waiting_on_canvas_image_updates: Cell::new(false),
|
waiting_on_canvas_image_updates: Cell::new(false),
|
||||||
current_canvas_epoch: RefCell::new(Epoch(0)),
|
current_canvas_epoch: RefCell::new(Epoch(0)),
|
||||||
custom_element_reaction_stack,
|
custom_element_reaction_stack,
|
||||||
|
active_sandboxing_flag_set: Cell::new(SandboxingFlagSet::empty()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4416,6 +4422,14 @@ impl Document {
|
||||||
pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
|
pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
|
||||||
self.custom_element_reaction_stack.clone()
|
self.custom_element_reaction_stack.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
|
||||||
|
self.active_sandboxing_flag_set.get().contains(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
|
||||||
|
self.active_sandboxing_flag_set.set(flags)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -4537,16 +4551,20 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-document-domain
|
/// <https://html.spec.whatwg.org/multipage/#dom-document-domain>
|
||||||
fn SetDomain(&self, value: DOMString) -> ErrorResult {
|
fn SetDomain(&self, value: DOMString) -> ErrorResult {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
if !self.has_browsing_context {
|
if !self.has_browsing_context {
|
||||||
return Err(Error::Security);
|
return Err(Error::Security);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Step 2. "If this Document object's active sandboxing
|
// Step 2. If this Document object's active sandboxing flag set has its sandboxed
|
||||||
// flag set has its sandboxed document.domain browsing context
|
// document.domain browsing context flag set, then throw a "SecurityError" DOMException.
|
||||||
// flag set, then throw a "SecurityError" DOMException."
|
if self.has_active_sandboxing_flag(
|
||||||
|
SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
|
||||||
|
) {
|
||||||
|
return Err(Error::Security);
|
||||||
|
}
|
||||||
|
|
||||||
// Steps 3-4.
|
// Steps 3-4.
|
||||||
let effective_domain = match self.origin.effective_domain() {
|
let effective_domain = match self.origin.effective_domain() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::borrow::ToOwned;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
|
use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
|
||||||
|
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use headers::{ContentType, HeaderMapExt};
|
use headers::{ContentType, HeaderMapExt};
|
||||||
|
@ -739,10 +740,18 @@ impl HTMLFormElement {
|
||||||
if self.constructing_entry_list.get() {
|
if self.constructing_entry_list.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Step 3
|
// Step 3. Let form document be form's node document.
|
||||||
let doc = self.owner_document();
|
let doc = self.owner_document();
|
||||||
|
|
||||||
|
// Step 4. If form document's active sandboxing flag set has its sandboxed forms browsing
|
||||||
|
// context flag set, then return.
|
||||||
|
if doc.has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_FORMS_BROWSING_CONTEXT_FLAG)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let base = doc.base_url();
|
let base = doc.base_url();
|
||||||
// TODO: Handle browsing contexts (Step 4, 5)
|
// TODO: Handle browsing contexts (Step 5)
|
||||||
// Step 6
|
// Step 6
|
||||||
if submit_method_flag == SubmittedFrom::NotFromForm {
|
if submit_method_flag == SubmittedFrom::NotFromForm {
|
||||||
// Step 6.1
|
// Step 6.1
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::time::{Duration, Instant};
|
||||||
use std::{f64, mem};
|
use std::{f64, mem};
|
||||||
|
|
||||||
use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
|
use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
|
||||||
|
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
|
use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
|
||||||
use euclid::default::Size2D;
|
use euclid::default::Size2D;
|
||||||
|
@ -717,11 +718,8 @@ impl HTMLMediaElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ready_state == ReadyState::HaveEnoughData {
|
if ready_state == ReadyState::HaveEnoughData {
|
||||||
// TODO: Check sandboxed automatic features browsing context flag.
|
|
||||||
// FIXME(nox): I have no idea what this TODO is about.
|
|
||||||
|
|
||||||
// FIXME(nox): Review this block.
|
// FIXME(nox): Review this block.
|
||||||
if self.autoplaying.get() && self.Paused() && self.Autoplay() {
|
if self.eligible_for_autoplay() {
|
||||||
// Step 1
|
// Step 1
|
||||||
self.paused.set(false);
|
self.paused.set(false);
|
||||||
// Step 2
|
// Step 2
|
||||||
|
@ -968,6 +966,31 @@ impl HTMLMediaElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#eligible-for-autoplay>
|
||||||
|
fn eligible_for_autoplay(&self) -> bool {
|
||||||
|
// its can autoplay flag is true;
|
||||||
|
self.autoplaying.get() &&
|
||||||
|
|
||||||
|
// its paused attribute is true;
|
||||||
|
self.Paused() &&
|
||||||
|
|
||||||
|
// it has an autoplay attribute specified;
|
||||||
|
self.Autoplay() &&
|
||||||
|
|
||||||
|
// its node document's active sandboxing flag set does not have the sandboxed automatic
|
||||||
|
// features browsing context flag set; and
|
||||||
|
{
|
||||||
|
let document = self.owner_document();
|
||||||
|
|
||||||
|
!document.has_active_sandboxing_flag(
|
||||||
|
SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// its node document is allowed to use the "autoplay" feature.
|
||||||
|
// TODO: Feature policy: https://html.spec.whatwg.org/iframe-embed-object.html#allowed-to-use
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#concept-media-load-resource
|
// https://html.spec.whatwg.org/multipage/#concept-media-load-resource
|
||||||
fn resource_fetch_algorithm(&self, resource: Resource) {
|
fn resource_fetch_algorithm(&self, resource: Resource) {
|
||||||
if let Err(e) = self.setup_media_player(&resource) {
|
if let Err(e) = self.setup_media_player(&resource) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use base::cross_process_instant::CrossProcessInstant;
|
||||||
use base::id::PipelineId;
|
use base::id::PipelineId;
|
||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
use base64::engine::general_purpose;
|
use base64::engine::general_purpose;
|
||||||
|
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||||
use devtools_traits::ScriptToDevtoolsControlMsg;
|
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use embedder_traits::resources::{self, Resource};
|
use embedder_traits::resources::{self, Resource};
|
||||||
|
@ -940,6 +941,15 @@ impl FetchResponseListener for ParserContext {
|
||||||
|
|
||||||
let _realm = enter_realm(&*parser.document);
|
let _realm = enter_realm(&*parser.document);
|
||||||
|
|
||||||
|
// From Step 23.8.3 of https://html.spec.whatwg.org/multipage/#navigate
|
||||||
|
// Let finalSandboxFlags be the union of targetSnapshotParams's sandboxing flags and
|
||||||
|
// policyContainer's CSP list's CSP-derived sandboxing flags.
|
||||||
|
// TODO: implement targetSnapshotParam's sandboxing flags
|
||||||
|
let csp_derived_sandboxing_flag_set = csp_list
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|csp| csp.get_sandboxing_flag_set_for_document())
|
||||||
|
.unwrap_or(SandboxingFlagSet::empty());
|
||||||
|
|
||||||
if let Some(endpoints) = endpoints_list {
|
if let Some(endpoints) = endpoints_list {
|
||||||
parser.document.window().set_endpoints_list(endpoints);
|
parser.document.window().set_endpoints_list(endpoints);
|
||||||
}
|
}
|
||||||
|
@ -961,6 +971,15 @@ impl FetchResponseListener for ParserContext {
|
||||||
let Some(media_type) = MimeClassifier::get_media_type(&mime_type) else {
|
let Some(media_type) = MimeClassifier::get_media_type(&mime_type) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CSP/Sandboxing is applied conditionally based on the content type
|
||||||
|
let apply_csp_and_sandboxing_flags = || {
|
||||||
|
parser
|
||||||
|
.document
|
||||||
|
.set_active_sandboxing_flag_set(csp_derived_sandboxing_flag_set);
|
||||||
|
parser.document.set_csp_list(csp_list);
|
||||||
|
};
|
||||||
|
|
||||||
match media_type {
|
match media_type {
|
||||||
// Return the result of loading a media document given navigationParams and type.
|
// Return the result of loading a media document given navigationParams and type.
|
||||||
MediaType::Image | MediaType::AudioVideo => {
|
MediaType::Image | MediaType::AudioVideo => {
|
||||||
|
@ -1037,10 +1056,10 @@ impl FetchResponseListener for ParserContext {
|
||||||
},
|
},
|
||||||
Some(_) => {},
|
Some(_) => {},
|
||||||
// Return the result of loading an HTML document, given navigationParams.
|
// Return the result of loading an HTML document, given navigationParams.
|
||||||
None => parser.document.set_csp_list(csp_list),
|
None => apply_csp_and_sandboxing_flags(),
|
||||||
},
|
},
|
||||||
// Return the result of loading an XML document given navigationParams and type.
|
// Return the result of loading an XML document given navigationParams and type.
|
||||||
MediaType::Xml => parser.document.set_csp_list(csp_list),
|
MediaType::Xml => apply_csp_and_sandboxing_flags(),
|
||||||
_ => {
|
_ => {
|
||||||
// Show warning page for unknown mime types.
|
// Show warning page for unknown mime types.
|
||||||
let page = format!(
|
let page = format!(
|
||||||
|
|
22
tests/wpt/meta/MANIFEST.json
vendored
22
tests/wpt/meta/MANIFEST.json
vendored
|
@ -403717,6 +403717,14 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
|
"autoplay-disabled-by-csp.html.headers": [
|
||||||
|
"32518e57d4584de71845a9260b093c3535fc3074",
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
"form-submission-blocked-by-sandboxing.html.headers": [
|
||||||
|
"1efcf8c226fac074c98d0a5a747856f532e5d84e",
|
||||||
|
[]
|
||||||
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"empty.html": [
|
"empty.html": [
|
||||||
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
|
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
|
||||||
|
@ -581697,6 +581705,20 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
|
"autoplay-disabled-by-csp.html": [
|
||||||
|
"d7bd453a34c0e75c98c837f853c0cf492359625a",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"form-submission-blocked-by-sandboxing.html": [
|
||||||
|
"4c717a18fd8bfa9d5cb4bc5449b0f25498ccb754",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"iframe-inside-csp.sub.html": [
|
"iframe-inside-csp.sub.html": [
|
||||||
"cd402bdba0198bf763e1733004c2005614b9a542",
|
"cd402bdba0198bf763e1733004c2005614b9a542",
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[sandboxed-document_domain.html]
|
|
||||||
[Sandboxed document.domain]
|
|
||||||
expected: FAIL
|
|
25
tests/wpt/tests/content-security-policy/sandbox/autoplay-disabled-by-csp.html
vendored
Normal file
25
tests/wpt/tests/content-security-policy/sandbox/autoplay-disabled-by-csp.html
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#eligible-for-autoplay" />
|
||||||
|
<title>Test that autoplay is blocked by a document's active sandboxing flags</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/common/media.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<video id="v" autoplay></video>
|
||||||
|
<script>
|
||||||
|
async_test((t) => {
|
||||||
|
var v = document.getElementById('v')
|
||||||
|
|
||||||
|
v.addEventListener('playing', t.unreached_func(
|
||||||
|
'video should not autoplay due to sandboxing flags'
|
||||||
|
));
|
||||||
|
|
||||||
|
v.src = getVideoURI('/media/movie_5') + '?' + new Date() + Math.random()
|
||||||
|
t.step_timeout(() => t.done(), 500);
|
||||||
|
}, 'csp-derived sandboxing flags prevent autoplay.')
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
tests/wpt/tests/content-security-policy/sandbox/autoplay-disabled-by-csp.html.headers
vendored
Normal file
1
tests/wpt/tests/content-security-policy/sandbox/autoplay-disabled-by-csp.html.headers
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Content-Security-Policy: sandbox allow-forms
|
26
tests/wpt/tests/content-security-policy/sandbox/form-submission-blocked-by-sandboxing.html
vendored
Normal file
26
tests/wpt/tests/content-security-policy/sandbox/form-submission-blocked-by-sandboxing.html
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-form-submit">
|
||||||
|
<title>Test that form submission is blocked by a document's active sandboxing flags</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="f">
|
||||||
|
<input type="hidden" value="test" />
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
async_test((t) => {
|
||||||
|
var f = document.getElementById('f')
|
||||||
|
|
||||||
|
f.addEventListener('submit', t.unreached_func(
|
||||||
|
'form should not be submitted due to sandboxing flags'
|
||||||
|
));
|
||||||
|
|
||||||
|
f.submit();
|
||||||
|
t.step_timeout(() => t.done(), 500);
|
||||||
|
}, 'csp-derived sandboxing flags prevent form submission.')
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Security-Policy: sandbox allow-scripts
|
Loading…
Add table
Add a link
Reference in a new issue