Implement attribute and property lookup for Trusted Types (#36422)

These algorithms are used to check whether an attribute/property can
accept a Trusted Type.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-04-11 10:38:00 +02:00 committed by GitHub
parent 2c7aeca404
commit 0aa08042d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 143 additions and 192 deletions

View file

@ -4,6 +4,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, QualName, local_name, namespace_url, ns};
use js::rust::HandleValue; use js::rust::HandleValue;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{ use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
@ -85,6 +86,49 @@ impl TrustedTypePolicyFactory {
// Step 9: Return policy. // Step 9: Return policy.
Ok(policy) Ok(policy)
} }
/// <https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-get-trusted-type-data-for-attribute>
#[allow(clippy::if_same_then_else)]
fn get_trusted_type_data_for_attribute(
element: QualName,
attribute: String,
attribute_namespace: Option<Namespace>,
) -> Option<DOMString> {
// Step 1: Let data be null.
let mut data = None;
// Step 2: If attributeNs is null, and attribute is the name of an event handler content attribute, then:
// TODO(36258): look up event handlers
// Step 3: Find the row in the following table, where element is in the first column,
// attributeNs is in the second column, and attribute is in the third column.
// If a matching row is found, set data to that row.
if element.ns == ns!(html) &&
element.local == local_name!("iframe") &&
attribute_namespace.is_none() &&
attribute == "srcdoc"
{
data = Some(DOMString::from("TrustedHTML"))
} else if element.ns == ns!(html) &&
element.local == local_name!("script") &&
attribute_namespace.is_none() &&
attribute == "src"
{
data = Some(DOMString::from("TrustedScriptURL"))
} else if element.ns == ns!(svg) &&
element.local == local_name!("script") &&
attribute_namespace.is_none() &&
attribute == "href"
{
data = Some(DOMString::from("TrustedScriptURL"))
} else if element.ns == ns!(svg) &&
element.local == local_name!("script") &&
attribute_namespace == Some(ns!(xlink)) &&
attribute == "href"
{
data = Some(DOMString::from("TrustedScriptURL"))
}
// Step 4: Return data.
data
}
} }
impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicyFactory { impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicyFactory {
@ -135,23 +179,99 @@ impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicy
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getattributetype> /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getattributetype>
fn GetAttributeType( fn GetAttributeType(
&self, &self,
_: DOMString, tag_name: DOMString,
_: DOMString, attribute: DOMString,
_: Option<DOMString>, element_namespace: Option<DOMString>,
_: Option<DOMString>, attribute_namespace: Option<DOMString>,
) -> Option<DOMString> { ) -> Option<DOMString> {
// TODO(36258): implement algorithm // Step 1: Set localName to tagName in ASCII lowercase.
Some(DOMString::from("".to_string())) let local_name = tag_name.to_ascii_lowercase();
// Step 2: Set attribute to attribute in ASCII lowercase.
let attribute = attribute.to_ascii_lowercase();
// Step 3: If elementNs is null or an empty string, set elementNs to HTML namespace.
let element_namespace = match element_namespace {
Some(namespace) if !namespace.is_empty() => Namespace::from(namespace),
Some(_) | None => ns!(html),
};
// Step 4: If attrNs is an empty string, set attrNs to null.
let attribute_namespace = match attribute_namespace {
Some(namespace) if !namespace.is_empty() => Some(Namespace::from(namespace)),
Some(_) | None => None,
};
// Step 5: Let interface be the element interface for localName and elementNs.
let interface = QualName::new(None, element_namespace, LocalName::from(local_name));
// Step 6: Let expectedType be null.
let mut expected_type = None;
// Step 7: Set attributeData to the result of Get Trusted Type data for attribute algorithm,
// with the following arguments: interface as element, attribute, attrNs
let attribute_data = TrustedTypePolicyFactory::get_trusted_type_data_for_attribute(
interface,
attribute,
attribute_namespace,
);
// Step 8: If attributeData is not null, then set expectedType to the interfaces name of
// the value of the fourth member of attributeData.
if let Some(trusted_type) = attribute_data {
expected_type = Some(trusted_type)
}
// Step 9: Return expectedType.
expected_type
} }
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getpropertytype> /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getpropertytype>
#[allow(clippy::if_same_then_else)]
fn GetPropertyType( fn GetPropertyType(
&self, &self,
_: DOMString, tag_name: DOMString,
_: DOMString, property: DOMString,
_: Option<DOMString>, element_namespace: Option<DOMString>,
) -> Option<DOMString> { ) -> Option<DOMString> {
// TODO(36258): implement algorithm // Step 1: Set localName to tagName in ASCII lowercase.
Some(DOMString::from("".to_string())) let local_name = tag_name.to_ascii_lowercase();
// Step 2: If elementNs is null or an empty string, set elementNs to HTML namespace.
let element_namespace = match element_namespace {
Some(namespace) if !namespace.is_empty() => Namespace::from(namespace),
Some(_) | None => ns!(html),
};
// Step 3: Let interface be the element interface for localName and elementNs.
let interface = QualName::new(None, element_namespace, LocalName::from(local_name));
// Step 4: Let expectedType be null.
let mut expected_type = None;
// Step 5: Find the row in the following table, where the first column is "*" or interfaces name,
// and property is in the second column. If a matching row is found, set expectedType to
// the interfaces name of the value of the third column.
let property = property.str();
if interface.ns == ns!(html) &&
interface.local == local_name!("iframe") &&
property == "srcdoc"
{
expected_type = Some(DOMString::from("TrustedHTML"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "innerText"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "src"
{
expected_type = Some(DOMString::from("TrustedScriptURL"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "text"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "textContent"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if property == "innerHTML" {
expected_type = Some(DOMString::from("TrustedHTML"))
} else if property == "outerHTML" {
expected_type = Some(DOMString::from("TrustedHTML"))
}
// Step 6: Return expectedType.
expected_type
} }
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-defaultpolicy> /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-defaultpolicy>
fn GetDefaultPolicy(&self) -> Option<DomRoot<TrustedTypePolicy>> { fn GetDefaultPolicy(&self) -> Option<DomRoot<TrustedTypePolicy>> {

View file

@ -1,5 +1,4 @@
[HTMLElement-generic.html] [HTMLElement-generic.html]
expected: ERROR
[TT enabled: script.src\n = String on a\n connected element\n ] [TT enabled: script.src\n = String on a\n connected element\n ]
expected: FAIL expected: FAIL
@ -71,3 +70,15 @@
[TT enabled: script.textContent\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive] [TT enabled: script.textContent\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive]
expected: FAIL expected: FAIL
[TT enabled: script.src\n = TrustedScript on a\n connected element\n ]
expected: FAIL
[TT enabled: script.src\n = TrustedScript on a\n non-connected element\n ]
expected: FAIL
[TT enabled: script.src\n = TrustedScript on a\n connected element\n after removing the "require-trusted-types-for 'script' directive]
expected: FAIL
[TT enabled: script.src\n = TrustedScript on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive]
expected: FAIL

View file

@ -1,63 +0,0 @@
[TrustedTypePolicyFactory-getAttributeType-namespace.html]
[0: getAttributeType with full namespace info.]
expected: FAIL
[0: getAttributeType with element namespace and empty attribute namespace]
expected: FAIL
[0: getAttributeType without namespaces.]
expected: FAIL
[0: getAttributeType with undefined and empty namespace.]
expected: FAIL
[0: getAttributeType with empty and undefined namespace.]
expected: FAIL
[0: getAttributeType with empty namespaces.]
expected: FAIL
[0: getAttributeType with element namespace and empty attribute namespace.]
expected: FAIL
[1: getAttributeType with full namespace info.]
expected: FAIL
[1: getAttributeType with element namespace and empty attribute namespace]
expected: FAIL
[1: getAttributeType without namespaces.]
expected: FAIL
[1: getAttributeType with undefined and empty namespace.]
expected: FAIL
[1: getAttributeType with empty and undefined namespace.]
expected: FAIL
[1: getAttributeType with empty namespaces.]
expected: FAIL
[1: getAttributeType with element namespace and empty attribute namespace.]
expected: FAIL
[2: getAttributeType with full namespace info.]
expected: FAIL
[2: getAttributeType with element namespace and empty attribute namespace]
expected: FAIL
[2: getAttributeType without namespaces.]
expected: FAIL
[2: getAttributeType with undefined and empty namespace.]
expected: FAIL
[2: getAttributeType with empty and undefined namespace.]
expected: FAIL
[2: getAttributeType with empty namespaces.]
expected: FAIL
[2: getAttributeType with element namespace and empty attribute namespace.]
expected: FAIL

View file

@ -1,12 +0,0 @@
[TrustedTypePolicyFactory-getAttributeType-svg.html]
[trustedTypes.getAttributeType html script[href\]]
expected: FAIL
[trustedTypes.getAttributeType svg script[href\]]
expected: FAIL
[trustedTypes.getAttributeType svg script[href\] xlink href]
expected: FAIL
[trustedTypes.getAttributeType svg script[href\] other href]
expected: FAIL

View file

@ -7,63 +7,3 @@
[getAttributeType(\n "mrow",\n "onmousedown",\n "http://www.w3.org/1998/Math/MathML",\n "null") == "TrustedScript"] [getAttributeType(\n "mrow",\n "onmousedown",\n "http://www.w3.org/1998/Math/MathML",\n "null") == "TrustedScript"]
expected: FAIL expected: FAIL
[getAttributeType(\n "IFRAME",\n "srcdoc",\n "http://www.w3.org/1999/xhtml",\n "null") == "TrustedHTML"]
expected: FAIL
[getAttributeType(\n "SCRIPT",\n "src",\n "http://www.w3.org/1999/xhtml",\n "null") == "TrustedScriptURL"]
expected: FAIL
[getAttributeType(\n "script",\n "href",\n "http://www.w3.org/2000/svg",\n "null") == "TrustedScriptURL"]
expected: FAIL
[getAttributeType(\n "script",\n "href",\n "http://www.w3.org/2000/svg",\n "http://www.w3.org/1999/xlink") == "TrustedScriptURL"]
expected: FAIL
[getAttributeType(\n "foo",\n "onmouseup",\n "https://example.com/namespace",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "DIV",\n "onclick",\n "http://www.w3.org/1999/xhtml",\n "https://example.com/namespace") == "null"]
expected: FAIL
[getAttributeType(\n "DIV",\n "ondoesnotexist",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "DIV",\n "data-onclick",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "DIV",\n "srcdoc",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "iframe",\n "srcdoc",\n "https://example.com/namespace",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "IFRAME",\n "srcdoc",\n "http://www.w3.org/1999/xhtml",\n "https://example.com/namespace") == "null"]
expected: FAIL
[getAttributeType(\n "IFRAME",\n "data-srcdoc",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "DIV",\n "src",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "script",\n "src",\n "https://example.com/namespace",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "SCRIPT",\n "src",\n "http://www.w3.org/1999/xhtml",\n "https://example.com/namespace") == "null"]
expected: FAIL
[getAttributeType(\n "SCRIPT",\n "data-src",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "g",\n "href",\n "http://www.w3.org/2000/svg",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "SCRIPT",\n "href",\n "http://www.w3.org/1999/xhtml",\n "null") == "null"]
expected: FAIL
[getAttributeType(\n "script",\n "href",\n "http://www.w3.org/2000/svg",\n "https://example.com/namespace") == "null"]
expected: FAIL
[getAttributeType(\n "script",\n "src",\n "http://www.w3.org/2000/svg",\n "null") == "null"]
expected: FAIL

View file

@ -1,48 +1,9 @@
[TrustedTypePolicyFactory-getPropertyType.tentative.html] [TrustedTypePolicyFactory-getPropertyType.tentative.html]
[sanity check trustedTypes.getPropertyType for the HTML script element.]
expected: FAIL
[sanity check trustedTypes.getAttributeType.] [sanity check trustedTypes.getAttributeType.]
expected: FAIL expected: FAIL
[getPropertyType tests adapted from w3c/trusted-types polyfill]
expected: FAIL
[getAttributeType tests adapted from w3c/trusted-types polyfill] [getAttributeType tests adapted from w3c/trusted-types polyfill]
expected: FAIL expected: FAIL
[iframe.srcDoc is maybe defined]
expected: FAIL
[IFRAME.srcDoc is maybe defined]
expected: FAIL
[iFrAmE.srcDoc is maybe defined]
expected: FAIL
[iframe.SRCDOC is maybe defined]
expected: FAIL
[IFRAME.SRCDOC is maybe defined]
expected: FAIL
[iFrAmE.SRCDOC is maybe defined]
expected: FAIL
[getPropertyType vs getAttributeType for event handler.] [getPropertyType vs getAttributeType for event handler.]
expected: FAIL expected: FAIL
[ASCII case-insensitivity of tag name and attribute parameters]
expected: FAIL
[getPropertyType with an explicit elementNs parameter]
expected: FAIL
[getAttributeType with explicit elementNs and attrNs parameters]
expected: FAIL
[getAttributeType with qualified attribute name]
expected: FAIL
[getPropertyType/getAttributeType with explicit null elementNs/attrNs]
expected: FAIL

View file

@ -1,6 +0,0 @@
[legacy-trusted-script-urls.html]
[getPropertyType() with legacy TrustedScriptURLs properties return null.]
expected: FAIL
[getAttributeType() with legacy TrustedScriptURLs attributes return null.]
expected: FAIL