diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 3c87454ab..98e4c4f37 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -3973,7 +3973,6 @@ trusted-types/trusted-types-reporting-for-Element-setAttribute.html trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html trusted-types/trusted-types-reporting-for-ServiceWorker-ServiceWorkerContainer-register.https.html trusted-types/trusted-types-reporting-for-SharedWorker-ServiceWorkerContainer-register.https.html -trusted-types/trusted-types-reporting-for-Window-function-constructor.html trusted-types/trusted-types-reporting.html trusted-types/trusted-types-secondary-document.html trusted-types/trusted-types-svg-script-set-href.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index b3c90fd45..4db3544ef 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -7628,6 +7628,7 @@ trusted-types/trusted-types-reporting-for-SharedWorker-setTimeout-setInterval.ht trusted-types/trusted-types-reporting-for-Window-DedicatedWorker-constructor.html trusted-types/trusted-types-reporting-for-Window-SharedWorker-constructor.html trusted-types/trusted-types-reporting-for-Window-eval.html +trusted-types/trusted-types-reporting-for-Window-function-constructor.html trusted-types/trusted-types-reporting-for-Window-setTimeout-setInterval.html trusted-types/trusted-types-sandbox-allow-scripts.html trusted-types/trusted-types-sandbox-no-allow-scripts.html diff --git a/moli-renderer-v8/src/context_bootstrap/trusted_types.rs b/moli-renderer-v8/src/context_bootstrap/trusted_types.rs index 95cb1cf86..0f2e11707 100644 --- a/moli-renderer-v8/src/context_bootstrap/trusted_types.rs +++ b/moli-renderer-v8/src/context_bootstrap/trusted_types.rs @@ -326,27 +326,35 @@ fn trusted_script_string_for_code_generation( scope: &mut v8::PinScope<'_, '_>, original: &str, ) -> Option { - let Some(callback_input) = function_constructor_callback_input(original) else { + let Some(function_source) = function_constructor_code_generation_source(original) else { return trusted_script_string_for_eval_source(scope, original); }; if let Some(default_value) = apply_default_trusted_type_policy( scope, - callback_input, + function_source.default_policy_input, TrustedTypeKind::Script, "Function", TrustedTypeErrorKind::Eval, ) { - if default_value == callback_input { + if default_value == function_source.default_policy_input { return Some(original.to_owned()); } - dispatch_trusted_types_sink_violation_event(scope, "Function", callback_input); + dispatch_trusted_types_sink_violation_event( + scope, + "Function", + function_source.violation_sample, + ); throw_eval_error( scope, "Trusted Types default policy must not transform strings passed to Function.", ); return None; } - dispatch_trusted_types_sink_violation_event(scope, "Function", callback_input); + dispatch_trusted_types_sink_violation_event( + scope, + "Function", + function_source.violation_sample, + ); throw_trusted_type_error( scope, TrustedTypeErrorKind::Eval, @@ -357,19 +365,29 @@ fn trusted_script_string_for_code_generation( None } -fn function_constructor_callback_input(source: &str) -> Option<&str> { - let source = source +struct FunctionConstructorCodeGenerationSource<'a> { + default_policy_input: &'a str, + violation_sample: &'a str, +} + +fn function_constructor_code_generation_source( + source: &str, +) -> Option> { + let default_policy_input = source .strip_prefix('(') .and_then(|source| source.strip_suffix(')'))?; - [ - "function anonymous", - "async function anonymous", - "function* anonymous", - "async function* anonymous", + let generated_prefix = [ + "(function anonymous", + "(async function anonymous", + "(function* anonymous", + "(async function* anonymous", ] .into_iter() - .any(|prefix| source.starts_with(prefix)) - .then_some(source) + .find(|prefix| source.starts_with(prefix))?; + Some(FunctionConstructorCodeGenerationSource { + default_policy_input, + violation_sample: &source[generated_prefix.len()..], + }) } fn trusted_script_string_for_eval_source( diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs index ca72c3df3..b2a5d6753 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs @@ -785,6 +785,58 @@ fn rejected_default_policy_reports_both_dispositions_and_enforces_once() { ); } +#[test] +fn function_constructor_violations_sample_only_parameters_and_body() { + let mut vm = new_storage_test_vm("https://function-constructor-violation.test/"); + vm.set_response_content_security_policies(&["require-trusted-types-for 'script'".to_owned()]); + + let result = vm + .eval( + r#" +(() => { + const samples = []; + document.addEventListener("securitypolicyviolation", event => { + if (event.blockedURI === "trusted-types-sink") { + samples.push(event.sample); + } + }); + globalThis.__functionConstructorViolationSamples = samples; + + const constructors = [ + Function, + async function() {}.constructor, + function*() {}.constructor, + async function*() {}.constructor + ]; + const errors = constructors.map(Constructor => { + try { + new Constructor(`return${";".repeat(100)}`); + return "none"; + } catch (error) { + return `${error.name}:${error instanceof EvalError}`; + } + }); + return JSON.stringify({ errors, samples }); +})() +"#, + ) + .expect("Function constructor violation probe should evaluate"); + + assert_eq!( + result, + r#"{"errors":["EvalError:true","EvalError:true","EvalError:true","EvalError:true"],"samples":[]}"# + ); + assert_eq!( + drain_pre_domcontentloaded_non_script_page_tasks_for_test(&mut vm), + 4 + ); + assert_eq!( + vm.eval("JSON.stringify(globalThis.__functionConstructorViolationSamples)") + .expect("queued Function constructor violations should be observable"), + r#"["Function|(\n) {\nreturn;;;;;;;;;;;;;;;;;;;;;;;;;;;;","Function|(\n) {\nreturn;;;;;;;;;;;;;;;;;;;;;;;;;;;;","Function|(\n) {\nreturn;;;;;;;;;;;;;;;;;;;;;;;;;;;;","Function|(\n) {\nreturn;;;;;;;;;;;;;;;;;;;;;;;;;;;;"]"# + ); +} + #[test] fn script_execution_violation_outside_javascript_stack_avoids_v8_frame_probe() { let mut vm = new_storage_test_vm("https://script-execution-violation.test/");