docs: trim generated prose
This commit is contained in:
@@ -1,24 +1,5 @@
|
||||
/**
|
||||
* `LocalSandboxProvider`: the local implementation of the
|
||||
* `@deepseek-ai/dsh-sandbox` seam. Wraps a caller's argv in a platform
|
||||
* confinement runner selected BY PLATFORM: each platform names its runner
|
||||
* chain ({@link PLATFORM_CHAINS}), a chain of one is selected directly (no
|
||||
* probe — there is nothing to arbitrate), and a chain of several is probed
|
||||
* FUNCTIONALLY in preference order (build and enforce a real profile once,
|
||||
* not `--version`), the verdict cached for the provider's lifetime. Linux:
|
||||
* `bwrap`, else the `landlock-run` Landlock launcher (kernel confinement
|
||||
* that needs no userns/mount privileges; distributed as the npm package
|
||||
* family `node-addon-landlock-run` — the decision recorded in
|
||||
* docs/rfc/implemented/feature/2026-07-06-sandbox.md); darwin: macOS
|
||||
* `sandbox-exec` speaking a Seatbelt (SBPL) profile, unprobed.
|
||||
* When the platform has no chain or no candidate passes,
|
||||
* {@link LocalSandboxProvider.confine} FAILS CLOSED with the seam's
|
||||
* structured `SANDBOX_UNAVAILABLE` error instead of passing the argv
|
||||
* through unconfined; an unusable runner selected WITHOUT a probe fails
|
||||
* closed at execution time instead (it refuses to run the command), which
|
||||
* the wrap's `runnerFailureSignatures` let consumers classify as a sandbox
|
||||
* failure rather than a task failure.
|
||||
*
|
||||
* `LocalSandboxProvider`: the local implementation of the `@deepseek-ai/dsh-sandbox` seam.
|
||||
* @module @deepseek-ai/dsh-sandbox-local
|
||||
*/
|
||||
|
||||
@@ -35,20 +16,7 @@ import type { ConfinedArgv, ConfinedSandboxMode, SandboxEnforcement, SandboxPoli
|
||||
/** Plugin config. All optional — `static Config` supplies the defaults. */
|
||||
export interface Config {
|
||||
/**
|
||||
* Override the sandbox runner argv (the bwrap-shaped profile arguments are
|
||||
* appended). A NON-EMPTY argv is the operator's assertion that this runner
|
||||
* exists and FULLY enforces the profile (confinement reports
|
||||
* `enforcement: 'full'`, and — the runner's kernel mechanism being unknown
|
||||
* — carries both Linux file-denial dialects as its denial signatures) —
|
||||
* the runner chain and its probes are skipped,
|
||||
* and a broken runner fails loudly at execution time. The operator also
|
||||
* supplies {@link runnerFailureSignatures}, which distinguish the runner
|
||||
* refusing its profile from the wrapped command failing normally.
|
||||
* Absent (or empty — the schema normalizes an omitted array to `[]`): the
|
||||
* built-in platform chains — Linux `bwrap` then the Landlock launcher
|
||||
* (probed in that order), darwin `sandbox-exec` (the sole candidate,
|
||||
* selected without a probe). Used for custom/alternative runners and
|
||||
* for deterministic fake runners in keyless test tiers.
|
||||
* Override the sandbox runner argv (the bwrap-shaped profile arguments are appended).
|
||||
*/
|
||||
runnerCommand?: string[]
|
||||
/**
|
||||
@@ -74,14 +42,8 @@ export interface Config {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `bwrap` profile arguments for one policy. The whole host tree is bound
|
||||
* read-only; a fresh `/dev` keeps `>/dev/null` redirects working and a fresh
|
||||
* `/proc` keeps process-inspecting tools working. `workspace-write`
|
||||
* additionally mounts an ephemeral writable `/tmp` and rebinds the workspace
|
||||
* root read-write (bind order matters: later binds overlay earlier ones).
|
||||
* Deliberately NO `--unshare-pid` (it would break the process-group kill
|
||||
* semantics shell consumers rely on) and NO network unsharing (the seam's
|
||||
* mode vocabulary promises file effects only).
|
||||
* The `bwrap` profile arguments for one policy.
|
||||
*
|
||||
* @param policy - the file-effect policy to express as bwrap arguments.
|
||||
* @returns the bwrap profile arguments (before the trailing `--` + argv).
|
||||
*/
|
||||
@@ -95,19 +57,10 @@ export function bwrapProfileArgs(policy: SandboxPolicy): string[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `landlock-run` grant arguments for one policy — the bwrap
|
||||
* profile's file-effect semantics expressed as a Landlock allow-list
|
||||
* (Landlock cannot mount, so there are no fresh/ephemeral filesystems). The
|
||||
* whole tree is readable and executable; of `/dev`, ONLY `/dev/null` is
|
||||
* writable — a whole-`/dev` grant would expose real host paths beneath it
|
||||
* (`/dev/shm`, a shared tmpfs) to persistent writes, which `read-only`
|
||||
* promises never happen. bwrap can hand out a fresh ephemeral `/dev`; on the
|
||||
* host's own `/dev` the write grant must be node-by-node, and `>/dev/null`
|
||||
* is the one redirects need. `workspace-write` adds the HOST `/tmp` (shared
|
||||
* and persistent, where bwrap's is ephemeral — the honest difference,
|
||||
* recorded in the sandbox RFC's runner notes) plus the workspace
|
||||
* root read-write. The flag spelling belongs to `node-addon-landlock-run`'s
|
||||
* `grantArgs`; this function owns only the policy → grants mapping.
|
||||
* The `landlock-run` grant arguments for one policy — the bwrap profile's file-effect
|
||||
* semantics expressed as a Landlock allow-list (Landlock cannot mount, so there are no
|
||||
* fresh/ephemeral filesystems).
|
||||
*
|
||||
* @param policy - the file-effect policy to express as launcher grants.
|
||||
* @returns the launcher grant arguments (before `--` + argv).
|
||||
*/
|
||||
@@ -131,9 +84,6 @@ function canonicalPath(path: string): string {
|
||||
return realpathSync(path)
|
||||
} catch {
|
||||
// realpathSync failed: the path (or a prefix) is missing or unreadable.
|
||||
// Grant the spelling as-is — an unresolvable root matches nothing until
|
||||
// it exists, which is the conservative outcome, and inventing a fallback
|
||||
// resolution here would grant a path the caller never named.
|
||||
return path
|
||||
}
|
||||
}
|
||||
@@ -144,20 +94,12 @@ function sbplString(path: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `sandbox-exec` arguments for one policy: `-p` plus a Seatbelt (SBPL)
|
||||
* profile with the same file-effect semantics as the other dialects, built
|
||||
* as allow-default → `(deny file-write*)` → write allow-list (later rules
|
||||
* win), so exactly the mode's promised file effects are governed — network
|
||||
* and process visibility stay unrestricted, which is all the seam's mode
|
||||
* vocabulary claims. Of `/dev`, ONLY the `/dev/null` literal is writable
|
||||
* (the same node-not-directory reasoning as the Landlock grant).
|
||||
* `workspace-write` adds the workspace root, the host `/tmp`, and the
|
||||
* per-user darwin temp dir (`os.tmpdir()`, launchd's `TMPDIR`, inherited by
|
||||
* the confined child) — on darwin that directory IS the platform's `/tmp`
|
||||
* for every mkstemp-family tool, so omitting it would deny the mode's
|
||||
* promised temp area. All granted roots are canonicalized because Seatbelt
|
||||
* matches resolved paths ({@link canonicalPath}); duplicates after
|
||||
* resolution collapse.
|
||||
* The `sandbox-exec` arguments for one policy: `-p` plus a Seatbelt (SBPL) profile with the
|
||||
* same file-effect semantics as the other dialects, built as allow-default → `(deny
|
||||
* file-write*)` → write allow-list (later rules win), so exactly the mode's promised file
|
||||
* effects are governed — network and process visibility stay unrestricted, which is all the
|
||||
* seam's mode vocabulary claims.
|
||||
*
|
||||
* @param policy - the file-effect policy to express as an SBPL profile.
|
||||
* @returns the `sandbox-exec` arguments (`-p` + profile, before `--` + argv).
|
||||
*/
|
||||
@@ -239,13 +181,10 @@ type SelectedRunner = { runner: 'bwrap' | 'landlock' | 'seatbelt'; enforcement:
|
||||
const PLATFORM_CHAINS: Record<string, readonly SelectedRunner['runner'][]> = {
|
||||
linux: ['bwrap', 'landlock'],
|
||||
darwin: ['seatbelt'],
|
||||
// Reserved slot, deliberately empty: Windows support fills it with a
|
||||
// confinement runner (AppContainer / restricted-token family, shipped from
|
||||
// its own repository on the landlock-run template) plus a
|
||||
// SelectedRunner['runner'] union member — the switches' assertNever guards
|
||||
// then walk the implementer to every site. An empty chain fails closed at
|
||||
// confine(), identical to an unlisted platform: reserving the slot never
|
||||
// weakens the fail-closed end.
|
||||
// Reserved slot, deliberately empty: Windows support fills it with a confinement runner
|
||||
// (AppContainer / restricted-token family, shipped from its own repository on the
|
||||
// landlock-run template) plus a SelectedRunner['runner'] union member — the switches'
|
||||
// assertNever guards then walk the implementer to every site.
|
||||
win32: [],
|
||||
}
|
||||
|
||||
@@ -276,16 +215,9 @@ function assertPositiveFinite(name: string, value: number): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* The denial dialect each runner's kernel speaks — the case-insensitive
|
||||
* stderr substrings a denied file effect produces under it, carried on every
|
||||
* wrap (the seam's `ConfinedArgv.denialSignatures`). Kernel facts, not
|
||||
* tunables: bwrap denies through its read-only bind mounts (EROFS), Landlock
|
||||
* refuses with EACCES, Seatbelt with EPERM — whose text is also what
|
||||
* non-file EPERM boundaries print, the residual imprecision the consumer's
|
||||
* conservative classifier documents. An operator-configured `runnerCommand`
|
||||
* has an unknown kernel mechanism, so its wraps carry both Linux file-denial
|
||||
* dialects; bare EPERM stays excluded there (it names non-file boundaries
|
||||
* the mode vocabulary does not govern).
|
||||
* The denial dialect each runner's kernel speaks — the case-insensitive stderr substrings a
|
||||
* denied file effect produces under it, carried on every wrap (the seam's
|
||||
* `ConfinedArgv.denialSignatures`).
|
||||
*/
|
||||
const DENIAL_SIGNATURES = {
|
||||
bwrap: ['read-only file system'],
|
||||
@@ -295,15 +227,11 @@ const DENIAL_SIGNATURES = {
|
||||
} as const satisfies Record<SelectedRunner['runner'] | 'runnerCommand', readonly string[]>
|
||||
|
||||
/**
|
||||
* How each runner's OWN failure identifies itself on stderr (the seam's
|
||||
* `ConfinedArgv.runnerFailureSignatures`): every runner prefixes its error
|
||||
* lines with its program name, and the shell's runner-not-found message
|
||||
* carries the same `name: ` shape (`bash: bwrap: command not found`,
|
||||
* `bash: …/bin/landlock-run: No such file or directory`) — so one substring
|
||||
* per runner covers both "runner broke" and "runner missing". Consumers
|
||||
* match these BEFORE the denial dialect: a runner's error text can contain
|
||||
* denial words (an unopenable grant root reports `Permission denied`), and
|
||||
* a runner failure means the command never ran at all.
|
||||
* How each runner's own failure identifies itself on stderr (the seam's
|
||||
* `ConfinedArgv.runnerFailureSignatures`): every runner prefixes its error lines with its
|
||||
* program name, and the shell's runner-not-found message carries the same `name: ` shape
|
||||
* (`bash: bwrap: command not found`, `bash: …/bin/landlock-run: No such file or directory`) —
|
||||
* so one substring per runner covers both "runner broke" and "runner missing".
|
||||
*/
|
||||
const RUNNER_FAILURE_SIGNATURES = {
|
||||
bwrap: ['bwrap: '],
|
||||
@@ -356,17 +284,15 @@ export class LocalSandboxProvider extends SandboxProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap `argv` in the selected runner's invocation for `policy` — the
|
||||
* configured `runnerCommand` when present (the operator's assertion, no
|
||||
* probe), else the platform chain's runner speaking its own profile
|
||||
* dialect. Every wrap carries the runner's enforcement completeness, its
|
||||
* denial dialect, and its runner-failure signatures.
|
||||
* Wrap `argv` in the selected runner's invocation for `policy` — the configured
|
||||
* `runnerCommand` when present (the operator's assertion, no probe), else the platform
|
||||
* chain's runner speaking its own profile dialect.
|
||||
*
|
||||
* @param argv - the exact argv the caller is about to spawn.
|
||||
* @param policy - the file-effect policy this execution runs under.
|
||||
* @returns the wrapped argv plus the selected backend's enforcement
|
||||
* completeness, denial signatures, and runner-failure signatures;
|
||||
* throws the fail-closed `SANDBOX_UNAVAILABLE` error when the platform
|
||||
* has no usable runner.
|
||||
* @returns the wrapped argv plus the selected backend's enforcement completeness, denial
|
||||
* signatures, and runner-failure signatures; throws the fail-closed
|
||||
* `SANDBOX_UNAVAILABLE` error when the platform has no usable runner.
|
||||
*/
|
||||
confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv {
|
||||
if (this.runnerCommand !== undefined) {
|
||||
@@ -375,14 +301,9 @@ export class LocalSandboxProvider extends SandboxProvider {
|
||||
argv: [...this.runnerCommand, ...bwrapProfileArgs(policy), '--', ...argv],
|
||||
enforcement: 'full',
|
||||
denialSignatures: DENIAL_SIGNATURES.runnerCommand,
|
||||
// The operator names the configured runner's OWN pre-exec refusal
|
||||
// dialect; the consumer additionally re-joins the wrap through an
|
||||
// outer `bash -c 'exec …'`, so we can add the missing/unexecutable
|
||||
// outer-shell shapes ourselves. Scoping every automatic shape to
|
||||
// argv0 keeps in-command errors out (a bare `exec:`/`Permission
|
||||
// denied` prefix would claim tool output; `exec: <argv0>: not found`
|
||||
// cannot). The residual text-collision trade is documented by the
|
||||
// seam's conservative classifier contract.
|
||||
// The operator names the configured runner's own pre-exec refusal dialect; the consumer
|
||||
// additionally re-joins the wrap through an outer `bash -c 'exec …'`, so we can add the
|
||||
// missing/unexecutable outer-shell shapes ourselves.
|
||||
runnerFailureSignatures: [
|
||||
...this.configuredRunnerFailureSignatures,
|
||||
`exec: ${argv0}: not found`,
|
||||
@@ -428,11 +349,7 @@ export class LocalSandboxProvider extends SandboxProvider {
|
||||
const chain = this.internals.chain ?? PLATFORM_CHAINS[this.internals.platform ?? process.platform] ?? []
|
||||
const [first, ...rest] = chain
|
||||
if (first === undefined) return 'unavailable'
|
||||
// One candidate = nothing to arbitrate: select it without probing. Its
|
||||
// runner fails closed at EXECUTION time if unusable (refuses to run the
|
||||
// command), and the wrap's runnerFailureSignatures let the consumer
|
||||
// classify that as a sandbox failure — never a silent unconfined run,
|
||||
// never a plain task failure.
|
||||
// One candidate = nothing to arbitrate: select it without probing.
|
||||
if (rest.length === 0) return { runner: first, enforcement: STATIC_ENFORCEMENT[first] }
|
||||
for (const runner of chain) {
|
||||
const enforcement = this.probeRunner(runner)
|
||||
|
||||
Reference in New Issue
Block a user