mirror of
https://github.com/lexmount/moli.git
synced 2026-10-07 00:01:00 +00:00
fix(timers): preserve the initiating script nonce for string handlers
This commit is contained in:
@@ -2972,8 +2972,6 @@ html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-worker
|
||||
html/semantics/scripting-1/the-script-element/module/crossorigin.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-base-url.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html
|
||||
html/semantics/scripting-1/the-script-element/module/inline-async-execorder.html
|
||||
html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-1.html
|
||||
html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html
|
||||
|
||||
@@ -6508,6 +6508,8 @@ html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compi
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-other-document.html
|
||||
html/semantics/scripting-1/the-script-element/module/dynamic-import/v8-code-cache.html
|
||||
|
||||
@@ -18,7 +18,8 @@ use crate::{
|
||||
page_task_queue::RendererPageTimerSelection,
|
||||
script_provenance::CompiledStringProvenance,
|
||||
util::{
|
||||
context_host_ptr_from_global_bridge, create_script_origin_with_base_url, get_private_value,
|
||||
context_host_ptr_from_global_bridge, create_script_origin_with_base_url_and_nonce,
|
||||
get_private_value, script_nonce_from_host_defined_options,
|
||||
},
|
||||
};
|
||||
use moli_time::{
|
||||
@@ -44,6 +45,8 @@ struct ScheduledTimerSource {
|
||||
use_target_context: bool,
|
||||
source: String,
|
||||
provenance: CompiledStringProvenance,
|
||||
// A snapshot of the initiating script, not the eventual timer caller/realm.
|
||||
script_nonce: Option<String>,
|
||||
}
|
||||
|
||||
struct ScheduledTimerFunction {
|
||||
@@ -493,6 +496,9 @@ impl HostTimeoutScheduler {
|
||||
owner: HostTimerOwner,
|
||||
extra_args: Vec<v8::Global<v8::Value>>,
|
||||
) -> u32 {
|
||||
let script_nonce = scope
|
||||
.get_current_host_defined_options()
|
||||
.and_then(|options| script_nonce_from_host_defined_options(scope, options));
|
||||
let Some(owner) = scheduled_timer_owner_for_target(scope, owner, Some(receiver), context)
|
||||
else {
|
||||
return 0;
|
||||
@@ -519,6 +525,7 @@ impl HostTimeoutScheduler {
|
||||
use_target_context,
|
||||
source,
|
||||
provenance,
|
||||
script_nonce,
|
||||
}),
|
||||
owner,
|
||||
is_interval: false,
|
||||
@@ -541,6 +548,9 @@ impl HostTimeoutScheduler {
|
||||
owner: HostTimerOwner,
|
||||
extra_args: Vec<v8::Global<v8::Value>>,
|
||||
) -> u32 {
|
||||
let script_nonce = scope
|
||||
.get_current_host_defined_options()
|
||||
.and_then(|options| script_nonce_from_host_defined_options(scope, options));
|
||||
let Some(owner) = scheduled_timer_owner_for_target(scope, owner, Some(receiver), context)
|
||||
else {
|
||||
return 0;
|
||||
@@ -567,6 +577,7 @@ impl HostTimeoutScheduler {
|
||||
use_target_context,
|
||||
source,
|
||||
provenance,
|
||||
script_nonce,
|
||||
}),
|
||||
owner,
|
||||
is_interval: true,
|
||||
@@ -1268,11 +1279,12 @@ fn run_window_timer_source(
|
||||
exception: None,
|
||||
}));
|
||||
};
|
||||
let origin = create_script_origin_with_base_url(
|
||||
let origin = create_script_origin_with_base_url_and_nonce(
|
||||
&mut scope,
|
||||
source.provenance.source_url().as_str(),
|
||||
0,
|
||||
Some(source.provenance.module_base_url()),
|
||||
source.script_nonce.as_deref(),
|
||||
);
|
||||
let Some(script) = v8::Script::compile(&scope, source_value, Some(&origin)) else {
|
||||
let exception = scope.exception();
|
||||
|
||||
@@ -15622,6 +15622,7 @@ mod queue_microtask;
|
||||
mod rendering_update;
|
||||
mod script_terminal_completion;
|
||||
mod streams;
|
||||
mod string_timers;
|
||||
mod url_components;
|
||||
mod webidl_collections;
|
||||
mod webidl_fetch;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
use super::*;
|
||||
use crate::script_provenance::CompiledStringProvenance;
|
||||
|
||||
fn execute_nonce_script(vm: &mut ScriptVm, source: &str, url: &Url, nonce: Option<&str>) {
|
||||
vm.exec_in_enclosing_script_turn_with_provenance(
|
||||
source,
|
||||
&CompiledStringProvenance::at_url(url.clone()),
|
||||
0,
|
||||
nonce,
|
||||
true,
|
||||
)
|
||||
.map_err(|error| error.into_anyhow())
|
||||
.expect("the initiating script should execute");
|
||||
}
|
||||
|
||||
fn consume_string_timer(vm: &mut ScriptVm) {
|
||||
assert!(matches!(
|
||||
vm.run_next_timeout_for_test()
|
||||
.expect("string timer should execute"),
|
||||
crate::host::HostTimeoutRunResult::Consumed
|
||||
));
|
||||
}
|
||||
|
||||
fn assert_import_nonce(vm: &mut ScriptVm, base_url: &Url, nonce: Option<&str>) {
|
||||
let request = vm
|
||||
.document_runtime
|
||||
.take_next_native_dynamic_module_import()
|
||||
.expect("the timer should queue a dynamic import")
|
||||
.into_dynamic_import_request();
|
||||
assert_eq!(request.specifier(), "./dependency.mjs");
|
||||
assert_eq!(request.base_url(), base_url);
|
||||
assert_eq!(request.fetch_metadata().nonce.as_deref(), nonce);
|
||||
assert!(!request.fetch_metadata().parser_inserted);
|
||||
assert!(request.fetch_metadata().integrity.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_timers_preserve_nonce_for_timeout_interval_and_nested_source() {
|
||||
for (source, timer_turns) in [
|
||||
(r#"setTimeout("import('./dependency.mjs')", 0);"#, 1),
|
||||
(
|
||||
r#"globalThis.__nonceInterval = setInterval("clearInterval(__nonceInterval); import('./dependency.mjs')", 0);"#,
|
||||
1,
|
||||
),
|
||||
(
|
||||
r#"setTimeout("setTimeout(\"import('./dependency.mjs')\", 0)", 0);"#,
|
||||
2,
|
||||
),
|
||||
] {
|
||||
let mut vm = new_storage_test_vm("https://timer-nonce.test/page.html");
|
||||
let script_url = Url::parse("https://timer-nonce.test/scripts/initiator.js").unwrap();
|
||||
execute_nonce_script(&mut vm, source, &script_url, Some("initiating-nonce"));
|
||||
for _ in 0..timer_turns {
|
||||
consume_string_timer(&mut vm);
|
||||
}
|
||||
assert_import_nonce(&mut vm, &script_url, Some("initiating-nonce"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_timers_keep_nonce_snapshots_separate_for_identical_source_and_url() {
|
||||
let mut vm = new_storage_test_vm("https://timer-nonce.test/page.html");
|
||||
let script_url = Url::parse("https://timer-nonce.test/scripts/same.js").unwrap();
|
||||
let nonces = [Some("first-nonce"), None, Some("last-nonce")];
|
||||
for nonce in nonces {
|
||||
execute_nonce_script(
|
||||
&mut vm,
|
||||
r#"setTimeout("import('./dependency.mjs')", 0);"#,
|
||||
&script_url,
|
||||
nonce,
|
||||
);
|
||||
}
|
||||
for nonce in nonces {
|
||||
consume_string_timer(&mut vm);
|
||||
assert_import_nonce(&mut vm, &script_url, nonce);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_timers_use_the_active_function_nonce_without_borrowing_the_callers_nonce() {
|
||||
for initiating_nonce in [Some("function-nonce"), None] {
|
||||
let mut vm = new_storage_test_vm("https://timer-nonce.test/page.html");
|
||||
let script_url = Url::parse("https://timer-nonce.test/scripts/function.js").unwrap();
|
||||
execute_nonce_script(
|
||||
&mut vm,
|
||||
r#"globalThis.scheduleFromInitiator = () => setTimeout("import('./dependency.mjs')", 0);"#,
|
||||
&script_url,
|
||||
initiating_nonce,
|
||||
);
|
||||
execute_nonce_script(
|
||||
&mut vm,
|
||||
"scheduleFromInitiator();",
|
||||
&Url::parse("https://timer-nonce.test/other/caller.js").unwrap(),
|
||||
Some("caller-nonce"),
|
||||
);
|
||||
consume_string_timer(&mut vm);
|
||||
assert_import_nonce(&mut vm, &script_url, initiating_nonce);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user