Summary - Adds Windows agent terminal sandboxing by routing commands through WSL and Bubblewrap. - Supports native Windows and WSL project paths, including elevated write grants for WSL paths. - Shows a confirmation prompt to turn off sandboxing when WSL sandbox setup is unavailable. This builds on the work in the sandbox-linux branch. Closes AI-376 Release Notes: - Added Windows terminal sandboxing for agent commands when sandboxing is enabled. --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com> Co-authored-by: Richard Feldman <oss@rtfeldman.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
4.8 KiB
4.8 KiB
Bubblewrap sandbox — remaining work
Status of the Landlock → bwrap migration and what's left. Concise by
intent; see sandbox-bubblewrap-migration.md for the original rationale.
Done
- Landlock removed (code, dep,
Cargo.lock). linux_bubblewrap: locate + setuid rejection,build_bwrap_args(incl.--tmpfs /tmp), launcher (run_launcher_if_invoked, encode/decode), unit tests.apply_sandbox_wrapwired to bwrap;main.rshook.- Linux: no special
$TMPDIR(relies on tmpfs/tmp); prompt updated. - Network deny via
bwrap --unshare-net. The current policy is a coarse on/off, whichbwrap's own network namespace expresses directly — no seccomp,NO_NEW_PRIVS, orseccompilerdependency. Tradeoff: a network-denied command can't reach abstractAF_UNIXendpoints (D-Bus etc.); pathname unix sockets still work. Seccomp returns only when we need a finer policy (allow-list/egress proxy). - Thick launcher + status reporting (was §1). All sandbox work
(locate/probe) lives in the launcher — check and run are one process, no
parent/launcher TOCTOU. The launcher reports a
LauncherStatusas a one-shot datagram (SOCK_DGRAM) over an abstractAF_UNIXsocket (StatusChannel) whose address is passed in argv. Statuses:Success | BwrapNotFound | SetuidRejected | SandboxProbeFailed. The probe runsbwrap <real args> -- true, so it validates the exact policy we're about to use.- The launcher never runs a command unsandboxed: on any non-
Successoutcome it reports the reason and aborts. Choosing what to do about a failure is the consumer's job:- The agent (
run_terminal_tool) fails open for now — it checksSandboxWrap::can_create_sandboxup front and, if the sandbox can't be created, runs the command without one and prepends a note for the model ("failed to create the sandbox … ran without a sandbox"). No UI yet; see "UI tiers" below. - The NixOS tests fail closed — they assert the reported status and that the command did not run.
- The agent (
- The launcher never runs a command unsandboxed: on any non-
apply_sandbox_wraprework (was §2). Parent passes raw policy (socket, permissions, cwd, writable dirs, program, args) to the launcher and listens on the channel in a background thread (diagnostic only now). Parent-sidelocate_bwrap/is_available+ theirOnceLockcaches deleted; the up-front viability check islinux_bubblewrap::check_can_create_sandbox.- NixOS tests (was §5). Landlock kernel matrix replaced by three scenarios
(
sandbox-userns-enabled,sandbox-userns-disabled,sandbox-no-bwrap) undernix/tests/sandboxing. Newbwrap_test_helperbin (sandbox crate,nixos-testfeature) drives the real launcher and reads the status; an enforcement probe skips (not fails) when the VM can't enforce. Checks wired into the flake (packages.nix); xtask prefix is nowsandbox-. - Windows WSL behavior tests. Parity with the NixOS bwrap tests for the
Windows WSL sandbox (
windows_wsl). Newwsl_sandbox_test_helperbin (sandbox crate,wsl-testfeature) drives the realwrap_invocation, spawns the producedwsl.execommand, and asserts the same grants/restrictions hold end-to-end (writable binds land on host, reads allowed,/tmpephemeral, network on/off, fs-write escape hatch) plus the Windows-specific ones: a nativeC:\writable path is translated and bound, and WSL interop is blocked so a sandboxed process can't exec a Windows binary to escape bwrap. Core FS checks run against the WSL rootfs (robust regardless of how drvfs/mntsubmounts behave under the recursive root bind). Like the Linux helper it skips when the environment can't enforce; setZED_TEST_SANDBOX_REQUIRE_ENFORCED=1to turn that into a failure. Run withcargo xtask wsl-sandbox-testsorscript/test-wsl-sandbox.ps1(the latter provisionsbubblewrap+ unprivileged user namespaces in the default distro first). Not wired into CI — matching the NixOS tests, which aren't either. Degraded scenarios (no bwrap / userns disabled) have no boot-level toggle in WSL, so that coverage stays at the unit level (probe parsing + the bad-request-vs-unavailable error classification, also re-checked end-to-end).
Remaining
1. UI tiers
Success→ sandboxed, no warning.- Any failure → surface the reason and offer Run unsandboxed / Deny (unidirectional: on "run unsandboxed" the parent re-spawns a normal terminal; the launcher never blocks for a decision).
- Orange "unsandboxed" indicator on terminals that ran without the sandbox.
2. Bundled bwrap
bundled_bwrap_path() is a None stub, so today sandboxing needs a system
bwrap. Build a static musl, non-setuid bwrap (Nix pkgsStatic,
-Dselinux=disabled) per arch, bundle it, ship LGPL source/notice. Open: bundle
vs download.