Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
# VmEngine
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`VmEngine` is the default sandbox engine. It uses `node:vm` directly: caller code is compiled with `new vm.Script(...)` and executed via `script.runInContext(ctx, { timeout })`. The context is created with `codeGeneration: { strings: false, wasm: false }` so `eval` and the Function constructor are syntactically unavailable inside the sandbox even if the code-transformer's regex filter fails. Context variables and registered modules are injected as own properties of the context object; the host's intrinsics (`Object`, `Array`, etc.) are reachable through the shared realm.
`VmEngine` is NOT a security boundary against adversarial code. `node:vm`'s
context-sharing model means a determined attacker with code-injection can
escape via `this.constructor.constructor("...")` or prototype-chain tricks.
For adversarial isolation use `worker-engine` (OS-thread isolation) or
`spawn-engine` (process isolation).
## Features
- Real `node:vm` execution — synchronous, deterministic.
- Frozen intrinsics flag (`codeGeneration: { strings: false, wasm: false }`).
- Captured console (`log`/`warn`/`error`) with output-size cap.
- Module + context-variable injection per execution.
- V8 heap statistics via `v8.getHeapStatistics()`.
- OTel `recordExecution` + `recordDuration` emission per call.
- `executeSync` companion for pool callers that prefer synchronous APIs.
## Usage: Basic
```ts
import { VmEngine } from "@suaveplan/sandbox/vm-engine";
const engine = new VmEngine({
timeout: 5000,
maxOutputSize: 1024,
memoryLimit: 50 * 1024 * 1024,
});
const r = await engine.execute("1 + 1");
console.log(r.result); // 2
engine.destroy();
```
## Usage: Advanced
Inject a host bridge as a module so user code can interact with the
controlled-vocabulary API rather than the host's intrinsics:
```ts
import { VmEngine } from "@suaveplan/sandbox/vm-engine";
const engine = new VmEngine({
timeout: 5000,
maxOutputSize: 1024,
memoryLimit: 50 * 1024 * 1024,
modules: [
{
name: "host",
exports: {
fetchProduct: (id: string) => loadProduct(id),
},
},
],
});
const r = await engine.execute('host.fetchProduct("sku-42")');
```
## API Reference
- `new VmEngine(config: SandboxEngineConfig)`.
- `execute(code, context?, options?): Promise<SandboxResult>`.
- `executeSync(code, context?, options?): SandboxResult`.
- `registerModule(mod): void`.
- `unregisterModule(name): boolean`.
- `getHeapStatistics(): { usedHeapSize, heapSizeLimit } | null`.
- `reset(): void`.
- `destroy(): void`.
## Implementation Notes
The compiled script wraps the user code in `(${code})` so a bare expression
(e.g. `1 + 1`) returns its value rather than throwing a syntax error from a
missing semicolon. An IIFE (`function(){...}()`) compiles to
`(function(){...}())` which is also valid. The wrapper is intentionally
lightweight; callers who want statement-level execution should use
`executeFunction` on the facade or wrap their code in a function themselves.
The `breakOnSigint: true` flag is set so a SIGINT delivered to the host
terminates the running script rather than killing the entire process.