enhance: Add support for unsafe-eval and wasm-unsafe-eval (#32893)

Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
This commit is contained in:
Chocolate Pie 2024-08-02 02:26:44 +09:00 committed by GitHub
parent 2cf207ddc8
commit 92866ab911
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 755 additions and 73 deletions

View file

@ -19,6 +19,8 @@ use std::time::{Duration, Instant};
use std::{fmt, os, ptr, thread};
use base::id::PipelineId;
use content_security_policy::{CheckResult, PolicyDisposition};
use js::conversions::jsstr_to_string;
use js::glue::{
CollectServoSizes, CreateJobQueue, DeleteJobQueue, DispatchableRun, JobQueueTraps,
RUST_js_GetErrorMessage, SetBuildId, StreamConsumerConsumeChunk,
@ -27,23 +29,23 @@ use js::glue::{
use js::jsapi::{
AsmJSOption, BuildIdCharVector, ContextOptionsRef, DisableIncrementalGC,
Dispatchable as JSRunnable, Dispatchable_MaybeShuttingDown, GCDescription, GCOptions,
GCProgress, GCReason, GetPromiseUserInputEventHandlingState, HandleObject, Heap,
GCProgress, GCReason, GetPromiseUserInputEventHandlingState, HandleObject, HandleString, Heap,
InitConsumeStreamCallback, InitDispatchToEventLoop, JSContext as RawJSContext, JSGCParamKey,
JSGCStatus, JSJitCompilerOption, JSObject, JSSecurityCallbacks, JSTracer,
JS_AddExtraGCRootsTracer, JS_InitDestroyPrincipalsCallback, JS_InitReadPrincipalsCallback,
JS_RequestInterruptCallback, JS_SetGCCallback, JS_SetGCParameter,
JS_SetGlobalJitCompilerOption, JS_SetOffthreadIonCompilationEnabled,
JS_SetParallelParsingEnabled, JS_SetSecurityCallbacks, JobQueue, MimeType,
PromiseRejectionHandlingState, PromiseUserInputEventHandlingState, SetDOMCallbacks,
SetGCSliceCallback, SetJobQueue, SetPreserveWrapperCallbacks, SetProcessBuildIdOp,
SetPromiseRejectionTrackerCallback, StreamConsumer as JSStreamConsumer,
PromiseRejectionHandlingState, PromiseUserInputEventHandlingState, RuntimeCode,
SetDOMCallbacks, SetGCSliceCallback, SetJobQueue, SetPreserveWrapperCallbacks,
SetProcessBuildIdOp, SetPromiseRejectionTrackerCallback, StreamConsumer as JSStreamConsumer,
};
use js::jsval::UndefinedValue;
use js::panic::wrap_panic;
use js::rust::wrappers::{GetPromiseIsHandled, JS_GetPromiseResult};
use js::rust::{
Handle, HandleObject as RustHandleObject, IntoHandle, JSEngine, JSEngineHandle, ParentRuntime,
Runtime as RustRuntime,
describe_scripted_caller, Handle, HandleObject as RustHandleObject, IntoHandle, JSEngine,
JSEngineHandle, ParentRuntime, Runtime as RustRuntime,
};
use lazy_static::lazy_static;
use malloc_size_of::MallocSizeOfOps;
@ -79,6 +81,7 @@ use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue};
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_module::EnsureModuleHooksInitialized;
use crate::script_thread::trace_thread;
use crate::security_manager::CSPViolationReporter;
use crate::task::TaskBox;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::{TaskSource, TaskSourceName};
@ -90,8 +93,7 @@ static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps {
};
static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks {
// TODO: Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>
contentSecurityPolicyAllows: None,
contentSecurityPolicyAllows: Some(content_security_policy_allows),
subsumes: Some(principals::subsumes),
};
@ -311,6 +313,61 @@ unsafe extern "C" fn promise_rejection_tracker(
})
}
#[allow(unsafe_code)]
unsafe extern "C" fn content_security_policy_allows(
cx: *mut RawJSContext,
runtime_code: RuntimeCode,
sample: HandleString,
) -> bool {
let mut allowed = false;
let cx = JSContext::from_ptr(cx);
wrap_panic(&mut || {
// SpiderMonkey provides null pointer when executing webassembly.
let sample = match sample {
sample if !sample.is_null() => Some(jsstr_to_string(*cx, *sample)),
_ => None,
};
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
let Some(csp_list) = global.get_csp_list() else {
allowed = true;
return;
};
let is_js_evaluation_allowed = csp_list.is_js_evaluation_allowed() == CheckResult::Allowed;
let is_wasm_evaluation_allowed =
csp_list.is_wasm_evaluation_allowed() == CheckResult::Allowed;
let scripted_caller = describe_scripted_caller(*cx).unwrap_or_default();
allowed = match runtime_code {
RuntimeCode::JS if is_js_evaluation_allowed => true,
RuntimeCode::WASM if is_wasm_evaluation_allowed => true,
_ => false,
};
if !allowed {
// FIXME: Don't fire event if `script-src` and `default-src`
// were not passed.
for policy in csp_list.0 {
let task = CSPViolationReporter::new(
&global,
sample.clone(),
policy.disposition == PolicyDisposition::Report,
runtime_code,
scripted_caller.filename.clone(),
scripted_caller.line,
scripted_caller.col,
);
global
.dom_manipulation_task_source()
.queue(task, &global)
.unwrap();
}
}
});
allowed
}
#[allow(unsafe_code, crown::unrooted_must_root)]
/// <https://html.spec.whatwg.org/multipage/#notify-about-rejected-promises>
pub fn notify_about_rejected_promises(global: &GlobalScope) {