diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index d3cf31f671..a6cb21c9af 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -3617,7 +3617,6 @@ trusted-types/ServiceWorkerContainer-register-from-SharedWorker.https.html trusted-types/TrustedType-AttributeNodes.html trusted-types/TrustedTypePolicyFactory-getAttributeType.html trusted-types/Window-TrustedTypes.html -trusted-types/block-Document-execCommand.html trusted-types/block-string-assignment-to-Document-write.html trusted-types/block-string-assignment-to-Element-outerHTML.html trusted-types/block-string-assignment-to-Element-setAttribute.html @@ -3650,7 +3649,6 @@ trusted-types/trusted-types-event-handlers.html trusted-types/trusted-types-report-only.html trusted-types/trusted-types-reporting-check-report-DedicatedWorker-sink-mismatch.html trusted-types/trusted-types-reporting-for-DedicatedWorker-ServiceWorkerContainer-register.https.html -trusted-types/trusted-types-reporting-for-Document-execCommand.html trusted-types/trusted-types-reporting-for-Document-write.html trusted-types/trusted-types-reporting-for-Element-setAttribute.html trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 70cac0ff9b..23f7eef13d 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -8597,6 +8597,7 @@ trusted-types/TrustedTypePolicyFactory-getAttributeType-svg.html trusted-types/TrustedTypePolicyFactory-isXXX.html trusted-types/Window-block-eval-function-constructor.html trusted-types/Window-setTimeout-setInterval.html +trusted-types/block-Document-execCommand.html trusted-types/block-string-assignment-to-DOMParser-parseFromString.html trusted-types/block-string-assignment-to-DedicatedWorker-setTimeout-setInterval.html trusted-types/block-string-assignment-to-Document-parseHTMLUnsafe.html @@ -8666,6 +8667,7 @@ trusted-types/trusted-types-reporting-for-DedicatedWorker-eval.html trusted-types/trusted-types-reporting-for-DedicatedWorker-function-constructor.html trusted-types/trusted-types-reporting-for-DedicatedWorker-importScripts.html trusted-types/trusted-types-reporting-for-DedicatedWorker-setTimeout-setInterval.html +trusted-types/trusted-types-reporting-for-Document-execCommand.html trusted-types/trusted-types-reporting-for-Document-parseHTMLUnsafe.html trusted-types/trusted-types-reporting-for-Element-innerHTML.html trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html diff --git a/moli-renderer-v8/src/native_bridge/document/lifecycle.rs b/moli-renderer-v8/src/native_bridge/document/lifecycle.rs index 1a00f46938..19380ab09a 100644 --- a/moli-renderer-v8/src/native_bridge/document/lifecycle.rs +++ b/moli-renderer-v8/src/native_bridge/document/lifecycle.rs @@ -537,7 +537,7 @@ pub(in crate::native_bridge) fn node_document_exec_command_callback<'s>( return; } EditingCommand::InsertHtml => { - let Some(value) = editing_command_value(scope, &args) else { + let Some(value) = editing_command_insert_html_value(scope, runtime_ptr, &args) else { return; }; let inserted = exec_command_insert_html(scope, runtime_ptr, &value); @@ -759,6 +759,24 @@ fn editing_command_value<'s>( .map(|value| value.to_rust_string_lossy(scope)) } +fn editing_command_insert_html_value<'s>( + scope: &mut v8::PinScope<'s, '_>, + runtime_ptr: *mut JsContextHost, + args: &v8::FunctionCallbackArguments<'s>, +) -> Option { + if args.length() < 3 || args.get(2).is_undefined() { + return Some(String::new()); + } + let requirements = unsafe { &*runtime_ptr }.trusted_types_for_script_requirements(scope); + crate::context_bootstrap::trusted_html_string_or_throw( + scope, + args.get(2), + requirements, + "Document execCommand", + "execCommand", + ) +} + fn exec_command_insert_html( scope: &mut v8::PinScope<'_, '_>, runtime_ptr: *mut JsContextHost, 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 ba51d380ae..3dca474998 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 @@ -661,6 +661,85 @@ fn dom_parser_gates_converted_union_source_after_webidl_argument_conversion() { ); } +#[test] +fn document_exec_command_gates_only_insert_html_values() { + let mut vm = new_storage_test_vm("https://exec-command-trusted-types.test/"); + vm.set_response_content_security_policies(&["require-trusted-types-for 'script'".to_owned()]); + + let result = vm + .eval( + r#" +(() => { + const errorName = callback => { + try { + callback(); + return "none"; + } catch (error) { + return error && error.name; + } + }; + const root = document.documentElement || document.appendChild(document.createElement("html")); + const body = document.body || root.appendChild(document.createElement("body")); + const input = document.createElement("input"); + body.append(input); + input.focus(); + + const blocked = [ + errorName(() => document.execCommand("insertHTML", false, "blocked")), + errorName(() => document.execCommand("InSeRtHtMl", false, null)) + ]; + const unprotected = [ + errorName(() => document.execCommand("insertHTML")), + errorName(() => document.execCommand("insertHTML", false, undefined)), + errorName(() => document.execCommand("paste", false, "plain")), + errorName(() => document.execCommand("insertText", false, "plain")) + ]; + + input.value = ""; + const custom = trustedTypes.createPolicy("exec-command-custom", { + createHTML: value => value + }); + const trustedReturned = document.execCommand( + "insertHTML", + false, + custom.createHTML("trusted") + ); + const trustedValue = input.value; + + let sourceConversions = 0; + const defaultCalls = []; + trustedTypes.createPolicy("default", { + createHTML: (value, type, sink) => { + defaultCalls.push([value, type, sink]); + return "default"; + } + }); + input.value = ""; + const defaultReturned = document.execCommand("insertHTML", false, { + toString() { + sourceConversions += 1; + return "original"; + } + }); + + return JSON.stringify({ + blocked, + unprotected, + trusted: [trustedReturned, trustedValue], + defaulted: [defaultReturned, input.value, sourceConversions], + defaultCalls + }); +})() +"#, + ) + .expect("Document.execCommand TrustedHTML sink probe should evaluate"); + + assert_eq!( + result, + r#"{"blocked":["TypeError","TypeError"],"unprotected":["none","none","none","none"],"trusted":[true,"trusted"],"defaulted":[true,"default",1],"defaultCalls":[["original","TrustedHTML","Document execCommand"]]}"# + ); +} + #[test] fn document_write_gates_concatenated_union_values_before_writeln_newline() { let mut vm = new_storage_test_vm("https://document-write-trusted-types.test/");