Skip to content

feat(sandbox): wire selective network allowlist into BashTool (#10)#144

Merged
oratis merged 1 commit into
mainfrom
feat/bash-net-sandbox
Jun 1, 2026
Merged

feat(sandbox): wire selective network allowlist into BashTool (#10)#144
oratis merged 1 commit into
mainfrom
feat/bash-net-sandbox

Conversation

@oratis
Copy link
Copy Markdown
Owner

@oratis oratis commented Jun 1, 2026

Completes #10 by making the selective per-domain network allowlist (mechanism landed in #143) actually used by the Bash tool.

What

BashTool now routes a command through spawnNetworkSandbox when sandbox.network.allowedDomains is a non-empty allowlist on Linux. Otherwise the existing wrapBashCommand path is used (deny-all-net via --unshare-net for [], full network for undefined, macOS sandbox-exec, etc.).

Fail closed: if the slirp sandbox can't be set up — most commonly because the allowlisting DNS proxy can't bind 127.0.0.1:53 (privileged port) — BashTool re-runs the command under deny-all-net with a clear note, rather than running it unrestricted. Background commands always fail closed (the slirp helper can't safely outlive the turn).

Changes

  • netns.ts — pure helpers needsNetworkSandbox() and denyAllNetwork().
  • bash.ts — foreground net path (capture / timeout / abort driven by the handle), the fail-closed fallback, background deny-all; a shared summarize() helper for consistent output.
  • Testsnetns.test.ts (decision + config helpers, runs everywhere) and bash.test.ts wiring tests via an injected fake spawner (happy path, fail-closed fallback, background) — no real bwrap required. The real end-to-end path stays covered by netns-integration.test.ts on the Linux CI job.
  • docs/security-model.md — documents the Linux allowlist, its DNS-name threat model, the :53 requirement, and the fail-closed behavior.

Limits (documented)

DNS-name allowlisting — a raw-IP dial bypasses it (adequate for git/npm/pip-over-https). Per-domain allowlisting is Linux-only (macOS SBPL has no usable remote-host predicate). Where :53 isn't bindable, it fails closed to deny-all-net.

🤖 Generated with Claude Code

BashTool now routes commands through spawnNetworkSandbox when
network.allowedDomains is a non-empty allowlist on Linux. If the slirp
sandbox can't be set up (e.g. the DNS proxy can't bind 127.0.0.1:53), it
FAILS CLOSED — re-runs under deny-all-net with a clear note — rather than
running unrestricted. Background commands always fail closed (the slirp
helper can't safely outlive the turn).

- netns.ts: add pure helpers needsNetworkSandbox() (linux + enabled +
  non-empty allowlist) and denyAllNetwork() (fail-closed config).
- bash.ts: foreground net path (capture/timeout/abort via the handle),
  fail-closed fallback, background deny-all; shared summarize() helper.
- Tests: netns.test.ts (decision + config helpers, runs everywhere) +
  bash.test.ts wiring tests using an injected fake spawner (net path,
  fail-closed fallback, background) — no real bwrap needed.
- docs/security-model.md: document the Linux allowlist, its DNS-name threat
  model, the :53 requirement, and the fail-closed behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@oratis oratis merged commit 4dc5ee9 into main Jun 1, 2026
3 checks passed
@oratis oratis deleted the feat/bash-net-sandbox branch June 1, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant