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

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