docs(tool-cordis): state the sandbox stance as steering, not containment
The sandbox docs overclaimed a containment contract the design never makes: "capability access is routed through cordis services, never Node built-ins, so everything a mounted plugin does stays inspectable and disposable". The host-realm helpers on the sandbox global (harness, console, btoa) are reachable functions, so mount code that goes looking can reach the host realm through one of them — accepted under the trust stance, because the ctx a mount ultimately receives is fully privileged anyway. Reword the sandbox module doc, the README trust stance, and the RFC sandbox-semantics section to say exactly that: the traps and small global surface STEER honest code onto the cordis services; they are not a security boundary.
This commit is contained in:
@@ -26,7 +26,7 @@ The trust stance, stated once and threaded through the rest: the `node:vm` sandb
|
||||
|
||||
### Sandbox semantics
|
||||
|
||||
Mount code runs via `vm.createContext` + `runInContext`, wrapped as the body of an async function under a per-mount filename (`cordis-mount-<id>.js`). The vm gives the code a fresh realm: writes to `globalThis` stay inside the sandbox, and no Node API is provided — capability access is routed through the cordis services (`ctx.fs` for files, `ctx.web` for HTTP, `ctx.bash` for processes, the `ctx.timer` helpers for timing), never Node built-ins, so everything a mounted plugin does stays inspectable through `cordis_inspect` and disposable with its fiber. The `vmTimeoutMs` config bounds only the synchronous portion of evaluation; an async body escapes the bound (acceptable under the trust stance above).
|
||||
Mount code runs via `vm.createContext` + `runInContext`, wrapped as the body of an async function under a per-mount filename (`cordis-mount-<id>.js`). The vm gives the code a fresh realm: writes to `globalThis` stay inside the sandbox, and no Node API is handed in — capability access is *steered* toward the cordis services (`ctx.fs` for files, `ctx.web` for HTTP, `ctx.bash` for processes, the `ctx.timer` helpers for timing) rather than Node built-ins, so a well-behaved mount stays inspectable through `cordis_inspect` and disposable with its fiber. This is steering, not containment: consistent with the trust stance above, the small global surface keeps *honest* code on the cordis services but is not a security boundary — the host-realm helpers it exposes (`harness`, `console`, `btoa`) are reachable functions, so mount code that goes looking (through such a helper's `.constructor`, say) can still reach the host realm and Node itself, which is accepted because the `ctx` a mount ultimately receives is fully privileged anyway. The `vmTimeoutMs` config bounds only the synchronous portion of evaluation; an async body escapes the bound (also acceptable under the trust stance).
|
||||
|
||||
Sandbox globals are deliberately small: a tagged write-through `console` (`[cordis:<id>] …` on the host stdout/stderr, so a listener that fires long after the mount call still lands somewhere the user sees), the `harness.defineTool` / `harness.registerTool` registration pair, the encoding primitives fresh vm contexts lack (`btoa`/`atob` as host closures over `Buffer` — a sanctioned exception, `Buffer` itself is never exposed — plus `TextEncoder`/`TextDecoder`), and callable traps over the withheld Node APIs (`require`, `setTimeout`/`setInterval`/`setImmediate`/`clearTimeout`/`clearInterval`, `fetch`) that throw a redirect naming the cordis alternative. Only function-shaped globals are trapped; `process` and `Buffer` stay `undefined` so a `typeof` feature probe stays inert rather than detonating a throwing accessor.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user