Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion moli-benchmark/wpt-cross-current/failed-cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions moli-benchmark/wpt-cross-current/passed-cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 32 additions & 14 deletions moli-renderer-v8/src/context_bootstrap/trusted_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,27 +326,35 @@ fn trusted_script_string_for_code_generation(
scope: &mut v8::PinScope<'_, '_>,
original: &str,
) -> Option<String> {
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,
Expand All @@ -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<FunctionConstructorCodeGenerationSource<'_>> {
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(
Expand Down
52 changes: 52 additions & 0 deletions moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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/");
Expand Down
Loading