Add trusted type checks for eval arguments (#39263)

Also bumps mozjs to the latest version that has support for
`GStackVector` which is what this callback uses.

Part of #36258

Fixes #38877

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-09-12 21:08:26 +02:00 committed by GitHub
parent 033da09800
commit d1c3e5f58f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 97 additions and 185 deletions

8
Cargo.lock generated
View file

@ -1643,7 +1643,7 @@ dependencies = [
[[package]]
name = "content-security-policy"
version = "0.5.4"
source = "git+https://github.com/servo/rust-content-security-policy?branch=servo-csp#b437ae2001616161ce4729b49408e7785b2f6960"
source = "git+https://github.com/servo/rust-content-security-policy?branch=servo-csp#7b7bbb54905f44fb6b001fcd4d7ad45503c2e664"
dependencies = [
"base64 0.22.1",
"bitflags 2.9.4",
@ -5349,7 +5349,7 @@ dependencies = [
[[package]]
name = "mozjs"
version = "0.14.1"
source = "git+https://github.com/servo/mozjs#af903e0fc6f634699a8a7fc021adcbb3389beefd"
source = "git+https://github.com/servo/mozjs#ae642f8cd3d2b462b40b99a11da06df9912ecc2c"
dependencies = [
"bindgen 0.71.1",
"cc",
@ -5361,8 +5361,8 @@ dependencies = [
[[package]]
name = "mozjs_sys"
version = "0.140.0-1"
source = "git+https://github.com/servo/mozjs#af903e0fc6f634699a8a7fc021adcbb3389beefd"
version = "0.140.0-3"
source = "git+https://github.com/servo/mozjs#ae642f8cd3d2b462b40b99a11da06df9912ecc2c"
dependencies = [
"bindgen 0.71.1",
"cargo_metadata",

View file

@ -72,9 +72,9 @@ impl TrustedScript {
global: &GlobalScope,
code_string: DOMString,
compilation_type: CompilationType,
_parameter_strings: u8, // FIXME in bindings generation
parameter_strings: Vec<DOMString>,
body_string: DOMString,
_parameter_args: u8, // FIXME in bindings generation
parameter_args: Vec<TrustedScriptOrString>,
body_arg: HandleValue,
can_gc: CanGc,
) -> bool {
@ -87,7 +87,7 @@ impl TrustedScript {
};
// 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) {
let mut 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.
@ -96,13 +96,28 @@ impl TrustedScript {
_ => 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.
if is_trusted {
// Step 2.4.1. Assert: parameterArgs [list/size=] is equal to [parameterStrings]' size.
assert!(parameter_args.len() == parameter_strings.len());
// Step 2.4.2. For each index of the range 0 to |parameterArgs]' [list/size=]:
for index in 0..parameter_args.len() {
// Step 2.4.2.1. Let arg be parameterArgs[index].
match &parameter_args[index] {
// Step 2.4.2.2. If arg implements TrustedScript, then:
TrustedScriptOrString::TrustedScript(trusted_script) => {
// Step 2.4.2.2.1. if parameterStrings[index] is not equal to args data,
// set isTrusted to false.
if parameter_strings[index] != trusted_script.data() {
is_trusted = false;
}
},
// Step 2.4.2.3. Otherwise, set isTrusted to false.
TrustedScriptOrString::String(_) => {
is_trusted = 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 {

View file

@ -20,6 +20,7 @@ use std::{os, ptr, thread};
use background_hang_monitor_api::ScriptHangAnnotation;
use js::conversions::jsstr_to_string;
use js::gc::StackGCVector;
use js::glue::{
CollectServoSizes, CreateJobQueue, DeleteJobQueue, DispatchablePointer, DispatchableRun,
JS_GetReservedSlot, JobQueueTraps, RUST_js_GetErrorMessage, SetBuildId, SetUpEventLoopDispatch,
@ -29,19 +30,19 @@ use js::glue::{
use js::jsapi::{
AsmJSOption, BuildIdCharVector, CompilationType, ContextOptionsRef,
Dispatchable_MaybeShuttingDown, GCDescription, GCOptions, GCProgress, GCReason,
GetPromiseUserInputEventHandlingState, HandleObject, HandleString,
GetPromiseUserInputEventHandlingState, Handle as RawHandle, HandleObject, HandleString,
HandleValue as RawHandleValue, Heap, InitConsumeStreamCallback, JS_AddExtraGCRootsTracer,
JS_InitDestroyPrincipalsCallback, JS_InitReadPrincipalsCallback, JS_NewObject,
JS_NewStringCopyN, JS_SetGCCallback, JS_SetGCParameter, JS_SetGlobalJitCompilerOption,
JS_SetOffthreadIonCompilationEnabled, JS_SetReservedSlot, JS_SetSecurityCallbacks,
JSCLASS_RESERVED_SLOTS_MASK, JSCLASS_RESERVED_SLOTS_SHIFT, JSClass, JSClassOps,
JSContext as RawJSContext, JSGCParamKey, JSGCStatus, JSJitCompilerOption, JSObject,
JSSecurityCallbacks, JSTracer, JobQueue, MimeType, MutableHandleObject, MutableHandleString,
PromiseRejectionHandlingState, PromiseUserInputEventHandlingState, RuntimeCode,
SetDOMCallbacks, SetGCSliceCallback, SetJobQueue, SetPreserveWrapperCallbacks,
JSSecurityCallbacks, JSString, JSTracer, JobQueue, MimeType, MutableHandleObject,
MutableHandleString, PromiseRejectionHandlingState, PromiseUserInputEventHandlingState,
RuntimeCode, SetDOMCallbacks, SetGCSliceCallback, SetJobQueue, SetPreserveWrapperCallbacks,
SetProcessBuildIdOp, SetPromiseRejectionTrackerCallback, StreamConsumer as JSStreamConsumer,
};
use js::jsval::{ObjectValue, UndefinedValue};
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::panic::wrap_panic;
pub(crate) use js::rust::ThreadSafeJSContext;
use js::rust::wrappers::{GetPromiseIsHandled, JS_GetPromiseResult};
@ -62,6 +63,7 @@ use crate::body::BodyMixin;
use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
use crate::dom::bindings::conversions::{
get_dom_class, private_from_object, root_from_handleobject, root_from_object,
};
@ -504,9 +506,9 @@ unsafe extern "C" fn content_security_policy_allows(
runtime_code: RuntimeCode,
code_string: HandleString,
compilation_type: CompilationType,
parameter_strings: u8, // FIXME in bindings generation
parameter_strings: RawHandle<StackGCVector<*mut JSString>>,
body_string: HandleString,
parameter_args: u8, // FIXME in bindings generation
parameter_args: RawHandle<StackGCVector<JSVal>>,
body_arg: RawHandleValue,
can_compile_strings: *mut bool,
) -> bool {
@ -516,21 +518,66 @@ unsafe extern "C" fn content_security_policy_allows(
// SpiderMonkey provides null pointer when executing webassembly.
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
let global = &GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
let csp_list = global.get_csp_list();
allowed = match runtime_code {
RuntimeCode::JS => TrustedScript::can_compile_string_with_trusted_type(
cx,
global,
safely_convert_null_to_string(cx, code_string),
compilation_type,
parameter_strings,
safely_convert_null_to_string(cx, body_string),
parameter_args,
HandleValue::from_raw(body_arg),
CanGc::note(),
),
RuntimeCode::WASM => global.get_csp_list().is_wasm_evaluation_allowed(global),
};
// If we don't have any CSP checks to run, short-circuit all logic here
allowed = csp_list.is_none() ||
match runtime_code {
RuntimeCode::JS => {
let parameter_strings = Handle::from_raw(parameter_strings);
let parameter_strings_length = parameter_strings.len();
let mut parameter_strings_vec =
Vec::with_capacity(parameter_strings_length as usize);
for i in 0..parameter_strings_length {
let Some(str_) = parameter_strings.at(i) else {
unreachable!();
};
parameter_strings_vec.push(safely_convert_null_to_string(cx, str_.into()));
}
let parameter_args = Handle::from_raw(parameter_args);
let parameter_args_length = parameter_args.len();
let mut parameter_args_vec = Vec::with_capacity(parameter_args_length as usize);
for i in 0..parameter_args_length {
let Some(arg) = parameter_args.at(i) else {
unreachable!();
};
let value = arg.into_handle().get();
if value.is_object() {
if let Ok(trusted_script) =
root_from_object::<TrustedScript>(value.to_object(), *cx)
{
parameter_args_vec
.push(TrustedScriptOrString::TrustedScript(trusted_script));
} else {
unreachable!();
}
} else if value.is_string() {
let string_ptr = std::ptr::NonNull::new(value.to_string()).unwrap();
let dom_string = unsafe { jsstr_to_string(*cx, string_ptr) };
parameter_args_vec
.push(TrustedScriptOrString::String(dom_string.into()));
} else {
unreachable!();
}
}
TrustedScript::can_compile_string_with_trusted_type(
cx,
global,
safely_convert_null_to_string(cx, code_string),
compilation_type,
parameter_strings_vec,
safely_convert_null_to_string(cx, body_string),
parameter_args_vec,
HandleValue::from_raw(body_arg),
CanGc::note(),
)
},
RuntimeCode::WASM => global.get_csp_list().is_wasm_evaluation_allowed(global),
};
});
*can_compile_strings = allowed;
true

View file

@ -1,12 +0,0 @@
[report-clips-sample.https.html]
[Function constructor - the other kind of eval - is clipped.]
expected: FAIL
[Async Function constructor is also clipped.]
expected: FAIL
[Generator Function constructor is also clipped.]
expected: FAIL
[AsyncGenerator Function constructor is also clipped.]
expected: FAIL

View file

@ -1,3 +0,0 @@
[eval-csp-tt-no-default-policy.html]
[Function constructor of string and TrustedScript fails.]
expected: FAIL

View file

@ -1,18 +0,0 @@
[eval-function-constructor-untrusted-arguments-and-applying-default-policy.html]
[plain string at index 0 (default policy modifying the function text).]
expected: FAIL
[plain string at index 1 (default policy modifying the function text).]
expected: FAIL
[plain string at index 2 (default policy modifying the function text).]
expected: FAIL
[TrustedScript with forged toString() at index 0 (default policy modifying the function text).]
expected: FAIL
[TrustedScript with forged toString() at index 1 (default policy modifying the function text).]
expected: FAIL
[TrustedScript with forged toString() at index 2 (default policy modifying the function text).]
expected: FAIL

View file

@ -1,93 +0,0 @@
[eval-function-constructor.html]
[Function constructor with mixed plain and trusted strings, mask #8]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #8]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #8]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #8]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #9]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #9]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #9]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #9]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #10]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #10]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #10]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #10]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #11]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #11]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #11]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #11]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #12]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #12]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #12]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #12]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #13]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #13]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #13]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #13]
expected: FAIL
[Function constructor with mixed plain and trusted strings, mask #14]
expected: FAIL
[AsyncFunction constructor with mixed plain and trusted strings, mask #14]
expected: FAIL
[GeneratorFunction constructor with mixed plain and trusted strings, mask #14]
expected: FAIL
[AsyncGeneratorFunction constructor with mixed plain and trusted strings, mask #14]
expected: FAIL
[Function constructor with trusted strings, and a forged toString() for the one at index 0]
expected: FAIL
[Function constructor with trusted strings, and a forged toString() for the one at index 1]
expected: FAIL
[Function constructor with trusted strings, and a forged toString() for the one at index 2]
expected: FAIL

View file

@ -1,12 +0,0 @@
[trusted-types-reporting-for-DedicatedWorker-function-constructor.html]
[Violation report for Function with plain string.]
expected: FAIL
[Violation report for AsyncFunction with plain string.]
expected: FAIL
[Violation report for GeneratorFunction with plain string.]
expected: FAIL
[Violation report for AsyncGeneratorFunction with plain string.]
expected: FAIL

View file

@ -1,12 +0,0 @@
[trusted-types-reporting-for-Window-function-constructor.html]
[Violation report for Function with plain string.]
expected: FAIL
[Violation report for AsyncFunction with plain string.]
expected: FAIL
[Violation report for GeneratorFunction with plain string.]
expected: FAIL
[Violation report for AsyncGeneratorFunction with plain string.]
expected: FAIL