From fe2964ce52dee40ee4b2d47557e757424325638e Mon Sep 17 00:00:00 2001 From: ldm0 Date: Thu, 16 Jul 2026 23:06:14 +0800 Subject: [PATCH] fix(trusted-types): gate DOMParser source input --- .../tests/browser_api/trusted_types.rs | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) 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 ae924678d0..164506aa39 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 @@ -580,6 +580,87 @@ fn document_parse_html_unsafe_gates_converted_union_source() { ); } +#[test] +fn dom_parser_gates_converted_union_source_after_webidl_argument_conversion() { + let mut vm = new_storage_test_vm("https://dom-parser-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 parser = new DOMParser(); + const custom = trustedTypes.createPolicy("dom-parser-custom", { + createHTML: value => value + }); + const blocked = [ + errorName(() => parser.parseFromString("

blocked

", "text/html")), + errorName(() => parser.parseFromString(null, "text/html")), + errorName(() => parser.parseFromString("", "application/xml")) + ]; + const accepted = [ + parser.parseFromString( + custom.createHTML("
trusted
"), + "text/html" + ).body.innerText, + parser.parseFromString( + custom.createHTML(""), + "application/xml" + ).documentElement.tagName + ]; + + let sourceConversions = 0; + const defaultCalls = []; + trustedTypes.createPolicy("default", { + createHTML: (value, type, sink) => { + defaultCalls.push([value, type, sink]); + return value === "source" ? "

default

" : value; + } + }); + const source = { + toString() { + sourceConversions += 1; + return "source"; + } + }; + const defaultValues = [ + parser.parseFromString(source, "text/html").body.innerText, + parser.parseFromString(null, "text/html").body.innerText, + parser.parseFromString("", "application/xml").documentElement.tagName + ]; + const callsBeforeInvalidType = defaultCalls.length; + const invalidType = errorName(() => parser.parseFromString(source, "TEXT/html")); + const invalidTypeSkippedPolicy = defaultCalls.length === callsBeforeInvalidType; + + return JSON.stringify({ + blocked, + accepted, + defaultValues, + sourceConversions, + invalidType, + invalidTypeSkippedPolicy, + symbolSource: errorName(() => parser.parseFromString(Symbol(), "text/html")), + defaultCalls + }); +})() +"#, + ) + .expect("DOMParser TrustedHTML union probe should evaluate"); + + assert_eq!( + result, + r#"{"blocked":["TypeError","TypeError","TypeError"],"accepted":["trusted","root"],"defaultValues":["default","null","root"],"sourceConversions":2,"invalidType":"TypeError","invalidTypeSkippedPolicy":true,"symbolSource":"TypeError","defaultCalls":[["source","TrustedHTML","DOMParser parseFromString"],["null","TrustedHTML","DOMParser parseFromString"],["","TrustedHTML","DOMParser parseFromString"]]}"# + ); +} + #[test] fn document_write_gates_concatenated_union_values_before_writeln_newline() { let mut vm = new_storage_test_vm("https://document-write-trusted-types.test/"); @@ -600,7 +681,7 @@ fn document_write_gates_concatenated_union_values_before_writeln_newline() { const custom = trustedTypes.createPolicy("document-write-custom", { createHTML: value => `(${value})` }); - const doc = new DOMParser().parseFromString("", "text/html"); + const doc = new DOMParser().parseFromString(trustedTypes.emptyHTML, "text/html"); const replacementDoc = new DOMParser().parseFromString( custom.createHTML("seed"), "text/html"