mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
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.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Exact model-facing schemas: [the generated tool catalog](../../../docs/tool-cata
|
||||
|
||||
## Trust stance
|
||||
|
||||
The sandbox isolates the global context only — it is not a security boundary. No Node API is provided: `require`, the timers, and `fetch` are callable traps that throw a redirect to the cordis alternative (`ctx.fs` / `ctx.web` / `ctx.bash` / `inject: ['timer']` + `ctx.setTimeout`); `process` and `Buffer` are `undefined`; `globalThis` writes stay inside. The `ctx` a mounted plugin's `apply` receives is a whitelist façade — register tools, observe events, provide/consume services, use timers; framework internals (`ctx.root`, `ctx.fiber`, `ctx.extend`, `ctx.plugin`, …) are withheld — but the capabilities it does expose reach the real runtime, so load this plugin as deliberately as you would grant a bash tool.
|
||||
The sandbox isolates the global context only — it is not a security boundary. No Node API is provided: `require`, the timers, and `fetch` are callable traps that throw a redirect to the cordis alternative (`ctx.fs` / `ctx.web` / `ctx.bash` / `inject: ['timer']` + `ctx.setTimeout`); `process` and `Buffer` are `undefined`; `globalThis` writes stay inside. These traps steer honest code onto the cordis services; they do not contain a mount that goes looking — the host-realm helpers on the sandbox global (`harness`, `console`, `btoa`) are reachable functions, so mount code can reach the host realm and Node through one of them, which is fine because `ctx` is fully privileged anyway. The `ctx` a mounted plugin's `apply` receives is a whitelist façade — register tools, observe events, provide/consume services, use timers; framework internals (`ctx.root`, `ctx.fiber`, `ctx.extend`, `ctx.plugin`, …) are withheld — but the capabilities it does expose reach the real runtime, so load this plugin as deliberately as you would grant a bash tool.
|
||||
|
||||
## Config
|
||||
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
* routed through cordis services, never Node built-ins: filesystem work goes
|
||||
* through `ctx.fs`, network through `ctx.web`, processes through `ctx.bash`,
|
||||
* timers through the `ctx.timer` helpers (fiber effects, unwound on unmount)
|
||||
* — so everything a mounted plugin does stays inspectable and disposable. The
|
||||
* sandbox guards against ACCIDENTAL global pollution only — it is not a
|
||||
* security boundary; the `ctx` a mounted plugin's `apply` later receives is
|
||||
* the real, fully privileged runtime handle, and that is the point of the
|
||||
* toolset.
|
||||
* — so a well-behaved mount stays inspectable and disposable. That routing is
|
||||
* STEERING toward the cordis services, not containment: the sandbox guards
|
||||
* against ACCIDENTAL global pollution, and it is not a security boundary. The
|
||||
* host-realm helpers on the sandbox global (`harness`, `console`, `btoa`) are
|
||||
* reachable functions, so a mount that goes looking — e.g. through such a
|
||||
* helper's `.constructor` — can still reach the host realm; that is accepted,
|
||||
* because the `ctx` a mounted plugin's `apply` later receives is the real,
|
||||
* fully privileged runtime handle, and that is the point of the toolset.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-tool-cordis/sandbox
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user