Files
deepseek-harness/packages/bash/bash-sandbox/README.md
Tianyi Cui 8590ba00a4 Merge remote-tracking branch 'origin/master' into codex/trim-ai-prose
# Conflicts:
#	docs/AGENTS.md
#	docs/config-catalog.md
#	packages/bash/bash-sandbox/src/index.ts
#	packages/bash/bash/src/session-mode.ts
#	packages/bash/tool-bash/README.md
#	packages/code-runtime/code-runtime-worker/README.md
#	packages/compact/compact/src/index.ts
#	packages/core/agent-core/README.md
#	packages/hooks/hooks-claude/src/config.ts
#	packages/hooks/hooks-claude/src/index.ts
#	packages/hooks/hooks-codex/src/config.ts
#	packages/hooks/hooks-codex/src/index.ts
#	packages/llm/llm/README.md
#	packages/session-persistence/session-persistence-jsonl/README.md
#	packages/session-persistence/session-persistence/README.md
#	packages/skill/skill-local/README.md
#	packages/support/acp-snapshot/README.md
#	packages/support/invariants/src/index.ts
#	packages/ui/acp/README.md
#	packages/ui/jsonrpc-agent/README.md
#	packages/ui/jsonrpc/README.md
#	packages/ui/permission/README.md
#	packages/ui/user-approval/README.md
#	packages/ui/user-interaction/README.md
#	packages/web/web-search-deepseek/README.md
2026-07-14 14:37:16 +08:00

6.8 KiB

@deepseek-ai/dsh-bash-sandbox

Sandbox-consuming implementation of the @deepseek-ai/dsh-bash executor seam. Load it instead of @deepseek-ai/dsh-bash-local, together with a ctx.sandbox provider (e.g. @deepseek-ai/dsh-sandbox-local) — no alternate tool plugin is needed; dsh-tool-bash detects the executor's sandboxMode capability and adds the escalation fields.

Every command is confined by handing the provider the exact ['bash', '-c', command] argv this executor is about to spawn and spawning the returned (wrapped) argv instead. WHICH platform runner confines it — and whether one is usable at all (fail closed with a structured SANDBOX_UNAVAILABLE error, never a silent unconfined run) — is the provider's concern; this package owns the bash side only.

Mode File effects
read-only (default) No writes anywhere (of /dev, only the /dev/null node is writable, so >/dev/null keeps working)
workspace-write Writes only under workspaceRoot + /tmp (ephemeral under bwrap, the host /tmp under Landlock, /private/tmp plus the per-user temp dir under Seatbelt)
danger-full-access No confinement; the provider is never consulted. Execution is dsh-bash-local's verbatim — foreground results still carry sandbox: { mode, denied: false } (no enforcement: nothing was confined), background tasks carry no sandbox facts

Semantics:

  • Denials are result facts. A failed run whose stderr carries the selected backend's own denial dialect — the signatures the provider stamps on every wrap (EROFS text under bwrap, EACCES under Landlock, EPERM under Seatbelt) — is reported as BashRunResult.sandbox.denied: true (conservative classification, read from the collected stderr tail); every CONFINED run also carries the mode it executed under (result.sandbox.mode) and the provider's enforcement completeness (result.sandbox.enforcement: full, or partial on an older Landlock ABI).
  • Runner failures are sandbox failures, never task failures. A failed run matching the wrap's runnerFailureSignatures (the runner's own error prefix — also what the shell prints for a missing runner) means the sandbox itself broke and the command NEVER RAN; the check outranks denial classification because a runner's error text can contain denial words. The foreground path re-throws it as the structured fail-closed SANDBOX_UNAVAILABLE error, with the runner's first stderr line as the cause; a settled background task stamps task.sandbox.runnerFailed instead (no error channel remains after settle), which bash_output renders as its own marker.
  • Config default, per-call override. resolve() stamps the configured sandbox mode onto each spec unless an approved request supplies a wider mode. That override affects only its call or background task. ctx.bash.sandboxMode reports the default so the tool advertises escalation only when supported; results report the effective mode. The model learns standing mode only from tool/result facts, not a system-prompt announcement.
  • File effects only. Network and process visibility are deliberately not restricted — the mode vocabulary does not pretend to cover what the backend does not enforce.
  • Process mechanics (spawn, process-group kills, output collection/spill, background tasks, credential scrub) are inherited verbatim from dsh-bash-local; the runner ladder, probes, and the per-platform Landlock launcher packages live with dsh-sandbox-local.

Deny-only at the seam: a denial is a reported fact, and this executor never negotiates permissions itself — the approval question lives in the tool layer (dsh-tool-bash), which drives the override this package honors.

- id: sandbox
  name: '@deepseek-ai/dsh-sandbox-local'
- id: bash
  name: '@deepseek-ai/dsh-bash-sandbox'
  config:
    mode: read-only
    workspaceRoot: !!js process.cwd()

The keyless consumer-integration proofs are tests/bwrap.e2e.ts, tests/landlock.e2e.ts, and tests/seatbelt.e2e.ts (the real provider + real runner driven through ctx.bash, world-verified, each self-skipping where its runner is absent); see the acp-agent example's default composition for the runnable demo.

Model Experience

Bash tool schema, indirectly

What the model sees: The generated dsh-tool-bash schemas are the baseline. By advertising a confining sandboxMode, this backend augments bash with sandbox_permissions using enum workspace-write | danger-full-access and with justification. The backend adds no prompt prose, and the session's effective mode remains unstated.

Token effect: Small fixed schema increment on requests where bash is visible; mode switches add no context tokens.

Bash tool result, indirectly

What the model sees: After ordinary bounded output, a denied call appends exactly [sandbox: file access denied under <mode> mode]. When escalation is available it next appends [sandbox: escalation available — retry this exact command once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]. A settled background runner failure instead appends [sandbox: the sandbox runner itself failed under <mode> mode — the command did not run; this is a sandbox problem, not a command failure].

Token effect: Zero additional tokens on an unremarkable allowed run beyond ordinary output. Denial or failure adds the quoted conditional marker, retained until compaction.

Bash tool error, indirectly

What the model sees: If no runner can enforce a confined mode, the foreground call propagates the SANDBOX_UNAVAILABLE error owned by dsh-sandbox. For an execution-time runner failure, this backend supplies the first stderr line as its detail.

Token effect: Conditional error text is visible for that call and retained in history until compaction.

Known Limitations and Deferred Work

  • Confinement covers file effects only — network access and process visibility are unchanged, so the modes are not a general-purpose security sandbox.
  • Denials are inferred from failed-command stderr — backend signatures make the inference portable, but a matching application error can be classified as a denial and a denial omitted from the retained tail can be missed.
  • A background runner failure has no immediate error channel — it is recorded on the settled task and surfaces when the caller polls with bash_output.
  • danger-full-access deliberately bypasses ctx.sandbox — it is an explicit unconfined mode, not a wider sandbox profile.