Files
simstudioai--sim/.claude/rules/sim-sandbox.md
T
wehub-resource-sync d25d482dc2
CI / Migrate DB (push) Blocked by required conditions
CI / Migrate Dev DB (push) Waiting to run
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Blocked by required conditions
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Blocked by required conditions
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Blocked by required conditions
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Blocked by required conditions
CI / Deploy Trigger.dev (Dev) (push) Blocked by required conditions
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Blocked by required conditions
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Blocked by required conditions
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Blocked by required conditions
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Blocked by required conditions
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Blocked by required conditions
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Blocked by required conditions
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Blocked by required conditions
CI / Check Docs Changes (push) Waiting to run
CI / Process Docs (push) Blocked by required conditions
CI / Create GitHub Release (push) Blocked by required conditions
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Test and Build (push) Waiting to run
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Blocked by required conditions
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Blocked by required conditions
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Blocked by required conditions
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Blocked by required conditions
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Blocked by required conditions
CI / Detect Version (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

4.2 KiB

description, paths
description paths
Isolated-vm sandbox worker security policy. Hard rules for anything that lives in the worker child process that runs user code.
apps/sim/lib/execution/isolated-vm-worker.cjs
apps/sim/lib/execution/isolated-vm.ts
apps/sim/lib/execution/sandbox/**
apps/sim/sandbox-tasks/**

Sim Sandbox — Worker Security Policy

The isolated-vm worker child process at apps/sim/lib/execution/isolated-vm-worker.cjs runs untrusted user code inside V8 isolates. The process itself is a trust boundary. Everything in this rule is about what must never live in that process.

Hard rules

  1. No app credentials in the worker process. The worker must not hold, load, or receive via IPC: database URLs, Redis URLs, AWS keys, Stripe keys, session-signing keys, encryption keys, OAuth client secrets, internal API secrets, or any LLM / email / search provider API keys. If you catch yourself require'ing @/lib/auth, @sim/db, @/lib/uploads/core/storage-service, or anything that imports env directly inside the worker, stop and use a host-side broker instead.

  2. Host-side brokers own all credentialed work. The worker can only access resources through ivm.Reference / ivm.Callback bridges back to the host process. Today the only broker is workspaceFileBroker (apps/sim/lib/execution/sandbox/brokers/workspace-file.ts); adding a new one requires co-reviewing this file.

  3. Host-side brokers must scope every resource access to a single tenant. The SandboxBrokerContext always carries workspaceId. Any new broker that accesses storage, DB, or an external API must use ctx.workspaceId to scope the lookup — never accept a raw path, key, or URL from isolate code without validation.

  4. Nothing that runs in the isolate is trusted, even if we wrote it. The task bootstrap and finalize strings in apps/sim/sandbox-tasks/ execute inside the isolate. They must treat globalThis as adversarial — no pulling values from it that might have been mutated by user code. The hardening script in executeTask undefines dangerous globals before user code runs.

Why

A V8 JIT bug (Chrome ships these roughly monthly) gives an attacker a native code primitive inside the process that owns whatever that process can reach. If the worker only holds isolated-vm + a single narrow workspace-file broker, a V8 escape leaks one tenant's files. If the worker holds a Stripe key or a DB connection, a V8 escape leaks the service.

The original doc-worker.cjs vulnerability (CVE-class, 225 production secrets leaked via /proc/1/environ) was the forcing function for this architecture. Keep the blast radius small.

Checklist for changes to isolated-vm-worker.cjs

Before landing any change that adds a new require(...) or process.send(...) payload or ivm.Reference wrapper in the worker:

  • Does it load a credential, key, connection string, or secret? If yes, move it host-side and expose as a broker.
  • Does it import from @/lib/auth, @sim/db, @/lib/uploads/core/*, @/lib/core/config/env, or any module that reads process.env of the main app? If yes, same — move host-side.
  • Does it expose a resource that's workspace-scoped without taking a workspaceId? If yes, re-scope.
  • Did you update the broker limits (IVM_MAX_BROKER_ARGS_JSON_CHARS, IVM_MAX_BROKER_RESULT_JSON_CHARS, IVM_MAX_BROKERS_PER_EXECUTION) if the new broker can emit large payloads or fire frequently?

What the worker may hold

  • isolated-vm module
  • Node built-ins: node:fs (only for reading the checked-in bundle .cjs files) and node:path
  • The three prebuilt library bundles under apps/sim/lib/execution/sandbox/bundles/*.cjs
  • IPC message handlers for execute, cancel, fetchResponse, brokerResponse

The worker deliberately has no host-side logger. All errors and diagnostics flow through IPC back to the host, which has @sim/logger. Do not add createLogger or console-based logging to the worker — it would require pulling the main app's config / env, which is exactly what this rule is preventing.

Anything else is suspect.