@@ -4,6 +4,7 @@ import path from 'node:path'
44import { fileURLToPath } from 'node:url'
55import { createLogger } from '@sim/logger'
66import { getErrorMessage , toError } from '@sim/utils/errors'
7+ import { filterUndefined } from '@sim/utils/object'
78import { randomFloat } from '@sim/utils/random'
89import { env } from '@/lib/core/config/env'
910import { getRedisClient } from '@/lib/core/config/redis'
@@ -890,6 +891,37 @@ function resetWorkerIdleTimeout(workerId: number) {
890891 }
891892}
892893
894+ /**
895+ * Environment for the sandbox worker process. The worker runs untrusted user
896+ * code, so it must never inherit the app's `process.env` (DB URLs, encryption
897+ * keys, provider API keys — see `.claude/rules/sim-sandbox.md`): a V8 isolate
898+ * escape would read every inherited secret from the worker's environment.
899+ * Only an explicit allowlist is forwarded — `PATH` so `spawn('node', ...)` can
900+ * resolve the binary, `NODE_ENV`, the two `IVM_*` limits the worker reads,
901+ * timezone/locale vars so `Date`/`Intl` behavior inside isolates matches the
902+ * host, and the Windows system vars Node needs to boot (undefined elsewhere
903+ * and stripped).
904+ * Any new env var the worker reads must be added here and to the allowlist
905+ * regression test in `isolated-vm.test.ts`.
906+ */
907+ function buildWorkerEnv ( ) : NodeJS . ProcessEnv {
908+ const allowed : Record < string , string | undefined > = {
909+ PATH : process . env . PATH ,
910+ IVM_MAX_STDOUT_CHARS : env . IVM_MAX_STDOUT_CHARS ,
911+ IVM_MAX_FETCH_OPTIONS_JSON_CHARS : env . IVM_MAX_FETCH_OPTIONS_JSON_CHARS ,
912+ TZ : process . env . TZ ,
913+ LANG : process . env . LANG ,
914+ LC_ALL : process . env . LC_ALL ,
915+ SYSTEMROOT : process . env . SYSTEMROOT ,
916+ WINDIR : process . env . WINDIR ,
917+ COMSPEC : process . env . COMSPEC ,
918+ PATHEXT : process . env . PATHEXT ,
919+ TEMP : process . env . TEMP ,
920+ TMP : process . env . TMP ,
921+ }
922+ return { ...filterUndefined ( allowed ) , NODE_ENV : process . env . NODE_ENV }
923+ }
924+
893925function spawnWorker ( ) : Promise < WorkerInfo > {
894926 const workerId = nextWorkerId ++
895927 spawnInProgress ++
@@ -955,6 +987,7 @@ function spawnWorker(): Promise<WorkerInfo> {
955987 const proc = spawn ( 'node' , [ '--no-node-snapshot' , workerPath ] , {
956988 stdio : [ 'ignore' , 'pipe' , 'pipe' , 'ipc' ] ,
957989 serialization : 'json' ,
990+ env : buildWorkerEnv ( ) ,
958991 } )
959992 childProcess = proc
960993
0 commit comments