Implement Trusted Type eval checks (#37834)

It implements the new codeForEvalGets callback to retrieve the
value for a trusted script object. Additionally, it implements
the new logic in can-compile-strings to call the policy
factory if required.

Note that parameter and argument checks aren't implemented yet,
as they require updates to binding generation (see TODO in
script_runtime).

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-08-11 14:26:56 +02:00 committed by GitHub
parent 4c05758ded
commit 82ca2b92cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 159 additions and 345 deletions

View file

@ -4,6 +4,8 @@
use std::fmt;
use dom_struct::dom_struct;
use js::jsapi::CompilationType;
use js::rust::HandleValue;
use crate::dom::bindings::codegen::Bindings::TrustedScriptBinding::TrustedScriptMethods;
use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
@ -11,10 +13,11 @@ use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::csp::CspReporting;
use crate::dom::globalscope::GlobalScope;
use crate::dom::trustedtypepolicy::TrustedType;
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
use crate::script_runtime::CanGc;
use crate::script_runtime::{CanGc, JSContext};
#[dom_struct]
pub struct TrustedScript {
@ -39,18 +42,16 @@ impl TrustedScript {
pub(crate) fn get_trusted_script_compliant_string(
global: &GlobalScope,
value: TrustedScriptOrString,
containing_class: &str,
field: &str,
sink: &str,
can_gc: CanGc,
) -> Fallible<DOMString> {
match value {
TrustedScriptOrString::String(value) => {
let sink = format!("{} {}", containing_class, field);
TrustedTypePolicyFactory::get_trusted_type_compliant_string(
TrustedType::TrustedScript,
global,
value,
&sink,
sink,
"'script'",
can_gc,
)
@ -59,6 +60,83 @@ impl TrustedScript {
TrustedScriptOrString::TrustedScript(trusted_script) => Ok(trusted_script.data.clone()),
}
}
pub(crate) fn data(&self) -> DOMString {
self.data.clone()
}
/// <https://www.w3.org/TR/CSP/#can-compile-strings>
#[allow(clippy::too_many_arguments)]
pub(crate) fn can_compile_string_with_trusted_type(
cx: JSContext,
global: &GlobalScope,
code_string: DOMString,
compilation_type: CompilationType,
_parameter_strings: u8, //FIXME in bindings generation
body_string: DOMString,
_parameter_args: u8, //FIXME in bindings generation
body_arg: HandleValue,
can_gc: CanGc,
) -> bool {
// Step 2.1. Let compilationSink be "Function" if compilationType is "FUNCTION",
// and "eval" otherwise.
let compilation_sink = if compilation_type == CompilationType::Function {
"Function"
} else {
"eval"
};
// Step 2.2. Let isTrusted be true if bodyArg implements TrustedScript,
// and false otherwise.
let is_trusted = match TrustedTypePolicyFactory::is_trusted_script(cx, body_arg) {
// Step 2.3. If isTrusted is true then:
Ok(trusted_script) => {
// Step 2.3.1. If bodyString is not equal to bodyArgs data, set isTrusted to false.
body_string == trusted_script.data
},
_ => false,
};
// Step 2.4. If isTrusted is true, then:
// Step 2.4.1. Assert: parameterArgs [list/size=] is equal to [parameterStrings]' size.
// Step 2.4.2. For each index of the range 0 to |parameterArgs]' [list/size=]:
// Step 2.4.2.1. Let arg be parameterArgs[index].
// Step 2.4.2.2. If arg implements TrustedScript, then:
// Step 2.4.2.2.1. if parameterStrings[index] is not equal to args data,
// set isTrusted to false.
// Step 2.4.2.3. Otherwise, set isTrusted to false.
// Step 2.5. Let sourceToValidate be a new TrustedScript object created in realm
// whose data is set to codeString if isTrusted is true, and codeString otherwise.
let source_string = if is_trusted {
// We don't need to call the compliant string algorithm, as it would immediately
// unroll the type as allowed by copying the data. This allows us to skip creating
// the DOM object.
code_string
} else {
// Step 2.6. Let sourceString be the result of executing the
// Get Trusted Type compliant string algorithm, with TrustedScript, realm,
// sourceToValidate, compilationSink, and 'script'.
match TrustedScript::get_trusted_script_compliant_string(
global,
TrustedScriptOrString::String(code_string.clone()),
compilation_sink,
can_gc,
) {
// Step 2.7. If the algorithm throws an error, throw an EvalError.
Err(_) => {
return false;
},
Ok(source_string) => {
// Step 2.8. If sourceString is not equal to codeString, throw an EvalError.
if source_string != code_string {
return false;
}
source_string
},
}
};
global
.get_csp_list()
.is_js_evaluation_allowed(global, &source_string)
}
}
impl fmt::Display for TrustedScript {