diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 64fbf587a6..5c7e4bae2f 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -3702,7 +3702,6 @@ trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html trusted-types/block-string-assignment-to-attribute-via-attribute-node.html trusted-types/eval-function-constructor-untrusted-arguments-and-applying-default-policy.html trusted-types/inheriting-csp-for-local-schemes.html -trusted-types/legacy-trusted-script-urls.html trusted-types/modify-attributes-in-callback.html trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html trusted-types/script-enforcement-006.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 021836c28e..db0b58cfc7 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -7851,6 +7851,7 @@ trusted-types/eval-no-csp-no-tt.html trusted-types/eval-with-non-trusted-script-object.html trusted-types/eval-with-permissive-csp.html trusted-types/get-trusted-types-compliant-attribute-value.html +trusted-types/legacy-trusted-script-urls.html trusted-types/legacy-trusted-scripts.html trusted-types/navigate-to-javascript-url-001.html trusted-types/navigate-to-javascript-url-002.html diff --git a/moli-renderer-v8/src/context_bootstrap/trusted_types.rs b/moli-renderer-v8/src/context_bootstrap/trusted_types.rs index 3a9e30d355..615a2eb323 100644 --- a/moli-renderer-v8/src/context_bootstrap/trusted_types.rs +++ b/moli-renderer-v8/src/context_bootstrap/trusted_types.rs @@ -95,6 +95,13 @@ struct TrustedTypePolicyFactoryInterfaceDeclaration { enumerable )] get_attribute_type: (), + #[webapi( + method = "getPropertyType", + callback = trusted_types_get_property_type_callback, + length = 2, + enumerable + )] + get_property_type: (), #[webapi( accessor_property = "defaultPolicy", getter = trusted_types_default_policy_getter_callback, @@ -127,6 +134,17 @@ struct TrustedTypesGetAttributeTypeArgs { attribute_namespace: Option, } +#[derive(webidl::WebIdlArgs)] +#[webidl(prefix = "TrustedTypePolicyFactory.getPropertyType")] +struct TrustedTypesGetPropertyTypeArgs { + #[webidl(required)] + tag_name: String, + #[webidl(required)] + property: String, + #[webidl(nullable)] + element_namespace: Option, +} + #[derive(WebApiObject)] #[webapi(interface = "Object")] struct TrustedTypeObjectDeclaration<'scope> { @@ -1152,6 +1170,36 @@ fn trusted_types_get_attribute_type_callback<'s>( rv.set(type_name.into()); } +fn trusted_types_get_property_type_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + if !trusted_types_factory_receiver_is_valid(scope, args.this()) { + return; + } + let Some(parsed) = webidl::parse_args::(scope, &args) else { + return; + }; + let element_namespace = parsed + .element_namespace + .filter(|namespace| !namespace.is_empty()) + .unwrap_or_else(|| crate::native_bridge::document::XHTML_NS.to_owned()); + let tag_name = parsed.tag_name.to_ascii_lowercase(); + let Some(type_name) = crate::native_bridge::element::trusted_property_type_name_for_names( + &element_namespace, + &tag_name, + &parsed.property, + ) else { + rv.set_null(); + return; + }; + let Some(type_name) = v8_string(scope, type_name) else { + return; + }; + rv.set(type_name.into()); +} + #[derive(Clone, Copy)] enum FunctionConstructorKind { Function, diff --git a/moli-renderer-v8/src/context_bootstrap/trusted_types/realm_state.rs b/moli-renderer-v8/src/context_bootstrap/trusted_types/realm_state.rs index 176e25503d..f134d05eec 100644 --- a/moli-renderer-v8/src/context_bootstrap/trusted_types/realm_state.rs +++ b/moli-renderer-v8/src/context_bootstrap/trusted_types/realm_state.rs @@ -83,6 +83,7 @@ pub(super) fn install_lazy_trusted_types_runtime_state<'s>( empty_html: (), empty_script: (), get_attribute_type: (), + get_property_type: (), default_policy: (), } .bind(scope, global) diff --git a/moli-renderer-v8/src/native_bridge/element.rs b/moli-renderer-v8/src/native_bridge/element.rs index 532228d0c2..d79b317b73 100644 --- a/moli-renderer-v8/src/native_bridge/element.rs +++ b/moli-renderer-v8/src/native_bridge/element.rs @@ -62,6 +62,7 @@ use trusted_types::{ }; pub(crate) use trusted_types::{ set_svg_animated_string_base_value, trusted_attribute_type_name_for_names, + trusted_property_type_name_for_names, }; pub(crate) use forms::{ diff --git a/moli-renderer-v8/src/native_bridge/element/trusted_types.rs b/moli-renderer-v8/src/native_bridge/element/trusted_types.rs index 882e406c47..9e5eae2ce6 100644 --- a/moli-renderer-v8/src/native_bridge/element/trusted_types.rs +++ b/moli-renderer-v8/src/native_bridge/element/trusted_types.rs @@ -81,15 +81,6 @@ fn trusted_attribute_sink_for_names( ("http://www.w3.org/1999/xhtml", "script", None, "src") => { Some(TrustedAttributeSink::ScriptUrl("HTMLScriptElement src")) } - ("http://www.w3.org/1999/xhtml", "embed", None, "src") => { - Some(TrustedAttributeSink::ScriptUrl("HTMLEmbedElement src")) - } - ("http://www.w3.org/1999/xhtml", "object", None, "data") => { - Some(TrustedAttributeSink::ScriptUrl("HTMLObjectElement data")) - } - ("http://www.w3.org/1999/xhtml", "object", None, "codebase") => Some( - TrustedAttributeSink::ScriptUrl("HTMLObjectElement codebase"), - ), ( "http://www.w3.org/2000/svg", "script", @@ -115,6 +106,24 @@ pub(crate) fn trusted_attribute_type_name_for_names( .map(|sink| sink.type_name()) } +pub(crate) fn trusted_property_type_name_for_names( + element_namespace: &str, + element_local_name: &str, + property_name: &str, +) -> Option<&'static str> { + if matches!(property_name, "innerHTML" | "outerHTML") { + return Some("TrustedHTML"); + } + match (element_namespace, element_local_name, property_name) { + ("http://www.w3.org/1999/xhtml", "iframe", "srcdoc") => Some("TrustedHTML"), + ("http://www.w3.org/1999/xhtml", "script", "innerText" | "text" | "textContent") => { + Some("TrustedScript") + } + ("http://www.w3.org/1999/xhtml", "script", "src") => Some("TrustedScriptURL"), + _ => None, + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(in crate::native_bridge::element) enum TrustedHtmlSink { ElementInnerHtml, 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 152c72df86..323d59b068 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 @@ -159,7 +159,7 @@ fn trusted_type_factory_interface_exposes_stable_branded_empty_values() { } #[test] -fn trusted_type_factory_get_attribute_type_reuses_attribute_sink_classification() { +fn trusted_type_factory_introspection_reuses_current_sink_classification() { let mut vm = new_storage_test_vm("https://trusted-type-attribute-types.test/"); let result = vm @@ -172,6 +172,7 @@ fn trusted_type_factory_get_attribute_type_reuses_attribute_sink_classification( const XLINK = "http://www.w3.org/1999/xlink"; const OTHER = "https://example.test/namespace"; const type = (...args) => trustedTypes.getAttributeType(...args); + const propertyType = (...args) => trustedTypes.getPropertyType(...args); const errorName = callback => { try { callback(); @@ -180,19 +181,28 @@ fn trusted_type_factory_get_attribute_type_reuses_attribute_sink_classification( return error.constructor.name; } }; - const descriptor = Object.getOwnPropertyDescriptor( + const attributeDescriptor = Object.getOwnPropertyDescriptor( TrustedTypePolicyFactory.prototype, "getAttributeType" ); + const propertyDescriptor = Object.getOwnPropertyDescriptor( + TrustedTypePolicyFactory.prototype, + "getPropertyType" + ); + const describe = descriptor => [ + typeof descriptor.value, + descriptor.value.name, + descriptor.value.length, + descriptor.enumerable, + descriptor.configurable + ]; return JSON.stringify({ interface: [ - typeof descriptor.value, - descriptor.value.name, - descriptor.value.length, - descriptor.enumerable, - descriptor.configurable, - Object.hasOwn(trustedTypes, "getAttributeType") + describe(attributeDescriptor), + describe(propertyDescriptor), + Object.hasOwn(trustedTypes, "getAttributeType"), + Object.hasOwn(trustedTypes, "getPropertyType") ], htmlDefaults: [ type("script", "src"), @@ -213,6 +223,17 @@ fn trusted_type_factory_get_attribute_type_reuses_attribute_sink_classification( type("script", "href", SVG, OTHER), type("script", "href", SVG.toUpperCase()) ], + properties: [ + propertyType("script", "text"), + propertyType("SCRIPT", "src"), + propertyType("script", "sRc"), + propertyType("div", "innerHTML"), + propertyType("foo", "outerHTML", OTHER), + propertyType("script", "src", SVG), + propertyType("embed", "src"), + propertyType("object", "data"), + propertyType("object", "codeBase") + ], handlers: [ type("div", "onclick"), type("g", "ondblclick", SVG), @@ -224,17 +245,20 @@ fn trusted_type_factory_get_attribute_type_reuses_attribute_sink_classification( errors: [ errorName(() => type()), errorName(() => type("script")), - errorName(() => descriptor.value.call({}, "script", "src")) + errorName(() => attributeDescriptor.value.call({}, "script", "src")), + errorName(() => propertyType()), + errorName(() => propertyType("script")), + errorName(() => propertyDescriptor.value.call({}, "script", "src")) ] }); })() "#, ) - .expect("TrustedTypePolicyFactory getAttributeType probe should evaluate"); + .expect("TrustedTypePolicyFactory introspection probe should evaluate"); assert_eq!( result, - r#"{"interface":["function","getAttributeType",2,true,true,false],"htmlDefaults":["TrustedScriptURL","TrustedScriptURL","TrustedScriptURL","TrustedScriptURL","TrustedScriptURL",null,null],"urls":["TrustedScriptURL","TrustedScriptURL","TrustedScriptURL",null,"TrustedScriptURL","TrustedScriptURL",null,null],"handlers":["TrustedScript","TrustedScript","TrustedScript",null,null,null],"errors":["TypeError","TypeError","TypeError"]}"# + r#"{"interface":[["function","getAttributeType",2,true,true],["function","getPropertyType",2,true,true],false,false],"htmlDefaults":["TrustedScriptURL","TrustedScriptURL","TrustedScriptURL","TrustedScriptURL","TrustedScriptURL",null,null],"urls":[null,null,null,null,"TrustedScriptURL","TrustedScriptURL",null,null],"properties":["TrustedScript","TrustedScriptURL",null,"TrustedHTML","TrustedHTML",null,null,null,null],"handlers":["TrustedScript","TrustedScript","TrustedScript",null,null,null],"errors":["TypeError","TypeError","TypeError","TypeError","TypeError","TypeError"]}"# ); } @@ -1298,7 +1322,7 @@ fn event_handler_attribute_writes_enforce_trusted_script_at_the_attribute_bounda } #[test] -fn url_attribute_writes_enforce_the_non_iframe_trusted_script_url_sink_table() { +fn url_attribute_writes_enforce_only_the_current_non_iframe_script_url_sinks() { let mut vm = new_storage_test_vm("https://url-attribute-trusted-types.test/"); vm.set_response_content_security_policies(&["require-trusted-types-for 'script'".to_owned()]); @@ -1333,10 +1357,7 @@ fn url_attribute_writes_enforce_the_non_iframe_trusted_script_url_sink_table() { [document.createElement("script"), element => element.setAttribute("src", "plain")], [document.createElement("script"), element => element.setAttributeNS(null, "src", null)], [document.createElementNS(svg, "script"), element => element.setAttribute("href", "plain")], - [document.createElementNS(svg, "script"), element => element.setAttributeNS(xlink, "xlink:href", policy.createScript("wrong"))], - [document.createElement("embed"), element => element.setAttribute("src", "plain")], - [document.createElement("object"), element => element.setAttribute("data", "plain")], - [document.createElement("object"), element => element.setAttribute("codebase", "plain")] + [document.createElementNS(svg, "script"), element => element.setAttributeNS(xlink, "xlink:href", policy.createScript("wrong"))] ]) { try { setter(element); @@ -1352,6 +1373,11 @@ fn url_attribute_writes_enforce_the_non_iframe_trusted_script_url_sink_table() { uppercaseSvg.setAttributeNS(null, "HREF", "uppercase"); const div = document.createElement("div"); div.setAttribute("src", "plain-div"); + const legacyEmbed = document.createElement("embed"); + legacyEmbed.setAttribute("src", "plain-embed"); + const legacyObject = document.createElement("object"); + legacyObject.setAttribute("data", "plain-object-data"); + legacyObject.setAttribute("codebase", "plain-object-codebase"); const defaultCalls = []; trustedTypes.createPolicy("default", { @@ -1381,7 +1407,10 @@ fn url_attribute_writes_enforce_the_non_iframe_trusted_script_url_sink_table() { ordinary: [ ordinary.getAttributeNS("urn:test", "src"), uppercaseSvg.getAttribute("HREF"), - div.getAttribute("src") + div.getAttribute("src"), + legacyEmbed.getAttribute("src"), + legacyObject.getAttribute("data"), + legacyObject.getAttribute("codebase") ], defaultValues: [ defaultHtml.getAttribute("src"), @@ -1397,7 +1426,7 @@ fn url_attribute_writes_enforce_the_non_iframe_trusted_script_url_sink_table() { assert_eq!( result, - r#"{"trustedValues":["script.js","script-ns.js","svg.js","svg-xlink.js","embed.js","object-data.js","object-codebase.js"],"rejected":[true,true,true,true,true,true,true],"ordinary":["namespaced","uppercase","plain-div"],"defaultValues":["safe-default-html","safe-default-svg","safe-default-object"],"defaultCalls":[["default-html","TrustedScriptURL","HTMLScriptElement src"],["default-svg","TrustedScriptURL","SVGScriptElement href"],["default-object","TrustedScriptURL","HTMLObjectElement codebase"]]}"# + r#"{"trustedValues":["script.js","script-ns.js","svg.js","svg-xlink.js","embed.js","object-data.js","object-codebase.js"],"rejected":[true,true,true,true],"ordinary":["namespaced","uppercase","plain-div","plain-embed","plain-object-data","plain-object-codebase"],"defaultValues":["safe-default-html","safe-default-svg","default-object"],"defaultCalls":[["default-html","TrustedScriptURL","HTMLScriptElement src"],["default-svg","TrustedScriptURL","SVGScriptElement href"]]}"# ); }