Skip to content

feat(sandbox): Linux bwrap strategy for the Bash tool#124

Merged
emal-avala merged 1 commit intomainfrom
feat/sandbox-bwrap-linux
Apr 15, 2026
Merged

feat(sandbox): Linux bwrap strategy for the Bash tool#124
emal-avala merged 1 commit intomainfrom
feat/sandbox-bwrap-linux

Conversation

@emal-avala
Copy link
Copy Markdown
Member

Second platform slice of ROADMAP §7.4 Process-Level Sandboxing. Drops a bubblewrap-based strategy into the trait plumbing landed in #120 — no changes to bash.rs or the `ToolContext` wiring.

Argv structure

```
bwrap \
--unshare-user --unshare-ipc --unshare-uts --unshare-pid --unshare-cgroup \
[--unshare-net] # only when allow_network = false \
--die-with-parent \
--ro-bind / / # broad read access \
--dev /dev --proc /proc # clean kernel filesystems \
--bind # writable project root \
--bind # each allowed_write_paths entry \
--chdir \
-- <args...>
```

Canonical-path resolution mirrors the Seatbelt strategy so `/var/run → /run` style redirection on Linux distros does not trap the child in a directory it cannot write to.

Strategy picker

  • `auto_detect` returns `bwrap` on Linux when `bwrap` is on `$PATH`, else `noop`
  • `pick_strategy("bwrap")` works on Linux only; on macOS/Windows it silently degrades to `noop` (matches the existing `seatbelt`-off-macOS behavior)
  • `sandbox_exec_available()` generalized to `binary_on_path(name)` so both probes share one implementation

Tests

  • 14 new bwrap unit tests covering namespace flags, network toggling, `--ro-bind / /`, `/dev` and `/proc` overlays, project and allowed-path binds, `--die-with-parent`, `--chdir`, the `--` program terminator, empty config lists, strategy name, and `wrap_command` program/cwd/env preservation
  • 2 new mod.rs picker tests for `auto_detect_on_linux` and `pick_strategy_bwrap` — gated by `#[cfg(target_os = "linux")]`
  • 6 new Linux-gated integration tests mirroring the Seatbelt suite end-to-end through the real Bash tool:
    • `bwrap_blocks_writes_outside_project_dir`
    • `bwrap_allows_writes_inside_project_dir`
    • `bwrap_honors_allowed_write_paths`
    • `bwrap_preserves_cwd`
    • `bwrap_dangerously_disable_sandbox_bypasses_when_allowed`
    • `bwrap_dangerously_disable_sandbox_is_ignored_when_bypass_denied`

All six skip gracefully if `bwrap` is not installed on the runner (non-zero chance on minimal CI images).

macOS regression surface

All 9 existing Seatbelt integration tests still pass, plus the 37 sandbox unit tests from the first slice. Workspace totals: 553 lib unit tests (was 538), full clippy -D warnings clean, cargo fmt clean.

Still deferred

  • Forbidden-path masking on Linux (Seatbelt `subpath` deny doesn't translate to bwrap; needs per-file handling)
  • Windows Low Integrity strategy
  • `agent.rs` subagent and PowerShell wiring
  • Flipping `enabled: true` by default — still waiting on Windows

Test plan

  • `cargo check --all-targets` clean
  • `cargo clippy --all-targets -- -D warnings` clean
  • `cargo fmt --all -- --check` clean
  • `cargo test --all-targets` — 553 lib unit + 9 sandbox integration tests pass on macOS
  • Ubuntu CI will exercise the 6 new Linux-gated integration tests on the PR run

Second platform slice of ROADMAP §7.4 Process-Level Sandboxing.
Wires a bubblewrap-based strategy into the existing SandboxStrategy
trait with no changes to the wiring at call sites — bash.rs still
routes through ctx.sandbox.wrap() exactly as it does for Seatbelt.

bwrap argv structure:
- unshare user/ipc/uts/pid/cgroup namespaces (always)
- unshare net only when allow_network = false
- --die-with-parent so a crashed agent cannot leave sandbox children
- --ro-bind / / for broad read access
- --dev /dev and --proc /proc overlays over the ro-bind base
- --bind <project> <project> for writable project directory
- --bind <path> <path> for each allowed_write_paths entry, with
  canonical form emitted when it differs from the raw path (mirrors
  seatbelt's symlink handling for /var -> /run style redirection)
- --chdir <cwd> then `--` then the original program and args

Strategy picker:
- auto_detect returns bwrap on Linux when `bwrap` is on $PATH
- pick_strategy("bwrap") works on Linux only; silently degrades to
  noop elsewhere (matches the seatbelt-off-macOS behavior)
- sandbox_exec_available() generalized to binary_on_path(name)
- bwrap("bwrap") on macOS / seatbelt on Linux both degrade gracefully

Tests:
- 14 new bwrap unit tests covering namespace flags, network toggling,
  ro-bind / /, dev/proc overlays, project and allowed-path binds,
  --die-with-parent, --chdir, `--` program terminator, empty lists,
  BwrapStrategy name, wrap_command program/cwd/env preservation
- 2 new mod.rs picker tests (auto_detect_on_linux, pick_strategy_bwrap)
- 6 new Linux-gated integration tests mirroring the Seatbelt suite:
  blocks writes outside project, allows in-project writes, honors
  allowed_write_paths, preserves cwd, bypass gate honored both ways
- macOS suite still green (553 lib unit tests, 9 integration tests)

Forbidden-path masking is still deferred — Seatbelt's `subpath` deny
model doesn't translate directly to bwrap and needs per-file handling.
The current bwrap strategy will need a follow-up for that.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@emal-avala emal-avala merged commit fb79f28 into main Apr 15, 2026
13 of 14 checks passed
@emal-avala emal-avala deleted the feat/sandbox-bwrap-linux branch April 15, 2026 08:43
emal-avala added a commit that referenced this pull request Apr 15, 2026
Highlights since v0.15.2:
- feat(sandbox): Linux bwrap strategy for the Bash tool (#124)
- fix(cli): translate LF->CRLF in streaming sink to stop rendered drift (#125)
- fix(llm): propagate cancel token into provider streaming task (#126)
emal-avala added a commit that referenced this pull request Apr 15, 2026
Highlights since v0.15.2:
- feat(sandbox): Linux bwrap strategy for the Bash tool (#124)
- fix(cli): translate LF->CRLF in streaming sink to stop rendered drift (#125)
- fix(llm): propagate cancel token into provider streaming task (#126)
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