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 chrono::Local;
|
||||
use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
|
||||
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||
use content_security_policy::{CspList, PolicyDisposition};
|
||||
use cookie::Cookie;
|
||||
use cssparser::match_ignore_ascii_case;
|
||||
|
@ -569,6 +570,10 @@ pub(crate) struct Document {
|
|||
/// The global custom element reaction stack for this script thread.
|
||||
#[conditional_malloc_size_of]
|
||||
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)]
|
||||
|
@ -3439,6 +3444,7 @@ impl Document {
|
|||
waiting_on_canvas_image_updates: Cell::new(false),
|
||||
current_canvas_epoch: RefCell::new(Epoch(0)),
|
||||
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> {
|
||||
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)]
|
||||
|
@ -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 {
|
||||
// Step 1.
|
||||
if !self.has_browsing_context {
|
||||
return Err(Error::Security);
|
||||
}
|
||||
|
||||
// TODO: Step 2. "If this Document object's active sandboxing
|
||||
// flag set has its sandboxed document.domain browsing context
|
||||
// flag set, then throw a "SecurityError" DOMException."
|
||||
// Step 2. If this Document object's active sandboxing flag set has its sandboxed
|
||||
// document.domain browsing context 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.
|
||||
let effective_domain = match self.origin.effective_domain() {
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::borrow::ToOwned;
|
|||
use std::cell::Cell;
|
||||
|
||||
use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
|
||||
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||
use dom_struct::dom_struct;
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use headers::{ContentType, HeaderMapExt};
|
||||
|
@ -739,10 +740,18 @@ impl HTMLFormElement {
|
|||
if self.constructing_entry_list.get() {
|
||||
return;
|
||||
}
|
||||
// Step 3
|
||||
// Step 3. Let form document be form's node 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();
|
||||
// TODO: Handle browsing contexts (Step 4, 5)
|
||||
// TODO: Handle browsing contexts (Step 5)
|
||||
// Step 6
|
||||
if submit_method_flag == SubmittedFrom::NotFromForm {
|
||||
// Step 6.1
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::time::{Duration, Instant};
|
|||
use std::{f64, mem};
|
||||
|
||||
use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
|
||||
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
|
||||
use euclid::default::Size2D;
|
||||
|
@ -717,11 +718,8 @@ impl HTMLMediaElement {
|
|||
}
|
||||
|
||||
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.
|
||||
if self.autoplaying.get() && self.Paused() && self.Autoplay() {
|
||||
if self.eligible_for_autoplay() {
|
||||
// Step 1
|
||||
self.paused.set(false);
|
||||
// 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
|
||||
fn resource_fetch_algorithm(&self, resource: 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 base64::Engine as _;
|
||||
use base64::engine::general_purpose;
|
||||
use content_security_policy::sandboxing_directive::SandboxingFlagSet;
|
||||
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
|
@ -940,6 +941,15 @@ impl FetchResponseListener for ParserContext {
|
|||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
// Return the result of loading a media document given navigationParams and type.
|
||||
MediaType::Image | MediaType::AudioVideo => {
|
||||
|
@ -1037,10 +1056,10 @@ impl FetchResponseListener for ParserContext {
|
|||
},
|
||||
Some(_) => {},
|
||||
// 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.
|
||||
MediaType::Xml => parser.document.set_csp_list(csp_list),
|
||||
MediaType::Xml => apply_csp_and_sandboxing_flags(),
|
||||
_ => {
|
||||
// Show warning page for unknown mime types.
|
||||
let page = format!(
|
||||
|
|
22
tests/wpt/meta/MANIFEST.json
vendored
22
tests/wpt/meta/MANIFEST.json
vendored
|
@ -403717,6 +403717,14 @@
|
|||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"autoplay-disabled-by-csp.html.headers": [
|
||||
"32518e57d4584de71845a9260b093c3535fc3074",
|
||||
[]
|
||||
],
|
||||
"form-submission-blocked-by-sandboxing.html.headers": [
|
||||
"1efcf8c226fac074c98d0a5a747856f532e5d84e",
|
||||
[]
|
||||
],
|
||||
"support": {
|
||||
"empty.html": [
|
||||
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
|
||||
|
@ -581697,6 +581705,20 @@
|
|||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"autoplay-disabled-by-csp.html": [
|
||||
"d7bd453a34c0e75c98c837f853c0cf492359625a",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"form-submission-blocked-by-sandboxing.html": [
|
||||
"4c717a18fd8bfa9d5cb4bc5449b0f25498ccb754",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"iframe-inside-csp.sub.html": [
|
||||
"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