5.9 KiB
5.9 KiB
name, description
| name | description |
|---|---|
| sp-sandbox | Server-tier sandboxed code execution with vm/worker/spawn/isolate engines, pre-warmed pools, and audit logging. |
Sandbox for Suaveplan
Server-tier sandboxed code execution. Ships four SandboxEngine
implementations (node:vm default, node:worker_threads,
node:child_process, and a structural isolated-vm adapter), a pre-warmed
SandboxPool, an async ResultStreamer, a pattern-based CodeTransformer,
an allowlist-based ModuleResolver, a sliding-window ResourceLimiter, and a
SHA-256 AuditLogger — all behind a createSandbox() facade with
Zod-validated config and full OpenTelemetry instrumentation.
Architecture
| Component | Responsibility |
|---|---|
createSandbox |
Public facade — composes engine, resolver, limiter, logger, and transformer into a Sandbox interface. |
VmEngine |
Default engine using node:vm directly — fastest, but NOT a security boundary against adversarial code. |
WorkerEngine |
Spawns a fresh node:worker_threads Worker per execution — true OS-thread isolation. |
SpawnEngine |
Spawns a fresh node:child_process — strongest isolation, highest overhead. |
IsolateEngine |
Structural adapter over isolated-vm (optional peer dep) — strongest in-process isolation. |
SandboxPool |
Pre-warmed engine pool with lazy growth, execution-count recycling, and idle reclamation. |
ResourceLimiter |
Sliding-window rate limit + output-size truncation. |
ModuleResolver |
Allowlist/denylist gatekeeper for modules exposed to sandboxed code. |
CodeTransformer |
Regex-based defence-in-depth layer blocking dangerous patterns (process.exit, require, eval). |
AuditLogger |
SHA-256-hashed structured audit log of every execution. |
ResultStreamer |
Incremental async output delivery instead of end-of-execution dumps. |
SandboxError |
Typed error carrying SANDBOX_CODES.* via defineCodes. |
File Structure
devtools/tooling/sandbox/
src/sandbox/ # createSandbox facade
src/vm-engine/ # VmEngine — default node:vm engine
src/worker-engine/ # WorkerEngine — node:worker_threads
src/spawn-engine/ # SpawnEngine + runner.ts child entry point
src/isolate-engine/ # IsolateEngine — isolated-vm structural adapter
src/pool/ # SandboxPool — pre-warmed engine recycling
src/resource-limiter/ # rate limit + output truncation
src/module-resolver/ # allowlist/denylist module gatekeeper
src/code-transformer/ # pattern-based dangerous-code guard
src/audit-logger/ # structured SHA-256 audit log
src/result-streamer/ # incremental async output
src/schemas/ # Zod schemas for every public boundary
src/error-codes/ # SANDBOX_CODES registry + SandboxError
src/telemetry/ # OTel instruments for the sandbox itself
src/types/ # shared SandboxEngine, SandboxResult, etc.
Sibling packages:
@suaveplan/linter— unified markdown + biome linting pipeline@suaveplan/profiler— runtime performance profiling toolkit@suaveplan/readline— terminal I/O primitive@suaveplan/test-vite— Vite + Vitest configuration factories
Patterns
Quick Start — execute code in a vm sandbox
import { createSandbox } from "@suaveplan/sandbox";
const sandbox = createSandbox({ timeout: 5000, engine: "vm" });
const result = await sandbox.execute("2 + 3");
console.log(result.result); // 5
sandbox.destroy();
Engine selection guide
import { createSandbox } from "@suaveplan/sandbox";
// Trusted accident-isolation, fastest path
const vm = createSandbox({ engine: "vm" });
// Adversarial code, OS-thread isolation
const worker = createSandbox({ engine: "worker" });
// Adversarial code, full process isolation
const spawn = createSandbox({ engine: "spawn" });
// Strongest in-process isolation (requires isolated-vm peer dep + customEngine)
const isolate = createSandbox({ engine: "isolate", customEngine: myIsolateFactory });
Using the pool for high-throughput execution
import { SandboxPool, VmEngine } from "@suaveplan/sandbox";
const pool = new SandboxPool({
engineConfig: { timeout: 5000, maxOutputSize: 1024 * 1024 },
factory: (config) => new VmEngine(config),
minSize: 2,
maxSize: 10,
});
const result = await pool.execute("1 + 1");
pool.destroy();
Register custom modules
import { createSandbox } from "@suaveplan/sandbox";
const sandbox = createSandbox();
sandbox.registerModule({ name: "math", exports: { add: (a: number, b: number) => a + b } });
const result = await sandbox.execute("math.add(2, 3)");
Constraints
- Node ≥ 20 — uses
node:vm,node:worker_threads,node:child_process, andnode:crypto. vmengine is NOT a security boundary —vm.runInContextshares the host realm; prototype-chain escapes are possible. Useworkerorspawnfor adversarial code.engine: "isolate"requires acustomEnginefactory — the package does not bundleisolated-vm; supply anIsolateFactoryviacustomEngine.- Zod validates every public boundary —
createSandbox,execute,registerModule, and pool config all run through strict schemas. - CodeTransformer is defence-in-depth only — it catches obvious accidents (
process.exit,require,eval) but is not a substitute for an engine-level boundary. - Error codes go through
defineCodes—SANDBOX_CODESis produced bydefineCodes("SANDBOX", [...]); never hand-write the string literal.
Checklist
- Choose the engine by threat model:
vmfor trusted code,worker/spawnfor untrusted. - Use
SandboxPoolwhen you have many short executions to amortise startup cost. - Register modules before executing code; modules are re-synced to the engine on every
executecall. - Call
sandbox.destroy()(orpool.destroy()) when done to release OS resources. - Import from subpath exports rather than deep
src/paths for tree-shakeability.