Run the plan's folder commands in a macOS sandbox with no network and no writes
Merged
CH
16d ago+142+142+012345678901234567890123456789-9-9-0123456789
Summary
- The problem. Since #1777, the service hands the Mac a bash command for each
run_in_repositorycall, and the Mac ran it as the developer with full permissions. Anyone who controls the service, or steers the planning model, could run any command on a tester's Mac. - The fix. On macOS each command now runs under
/usr/bin/sandbox-execwith a deny-by-default profile (packages/host/src/planning-commands.ts):- no network;
- no writes, except
/dev/null,/dev/zero, and/dev/fd; - reads only in the plan's folder (passed as a parameter, with links resolved) and system paths (
/usr,/bin,/System, Xcode tools,/opt/homebrew,/etc); - no system service lookups except the user lookup, so a command cannot ask a service such as
opento act outside the sandbox; - no reading a file named
.envor starting with.env(.env.local,.envrc), even in the folder. The deny is the profile's last rule, so it beats the folder's allow.
- The profile follows the shape of Codex's read-only Seatbelt policy. Claude Code's
sandbox-runtimeand Chrome use the same mechanism. Apple markssandbox-execdeprecated, but it is still the only documented way to sandbox an arbitrary process outside the App Store. - Environment. Every command gets a minimal environment of its own, so
envcannot send Luke's variables to the service.bash -lcis nowbash -c, since a login shell would read dotfiles outside the folder. - The tool description tells the planning model it can only read and has no network.
PRIVACY.mddescribes the sandbox.
Known gaps (fine for the friends MVP)
- Anything else inside the folder can still be read and sent to the service, such as a key committed in a config file.
- File metadata is readable everywhere, so a command can learn that a file outside the folder exists, but not read it.
- Off macOS, commands run without a sandbox. Luke ships only for macOS, so this only affects tests on Linux.
Evidence
./scripts/check.sh: exit 0.- New tests:
- a command sees none of Luke's environment (runs everywhere, fails without the change);
- on macOS, a command can neither write, nor read outside the folder or a
.env.localinside it (runIf(darwin), so it is skipped on Linux CI).
- CI could not verify this, and it has not run on a Mac. The work was done on Linux. Before it lands, run on a Mac:
pnpm exec vitest run packages/host/src/compose-planning.test.ts, then agit login a real plan (/usr/bin/gitgoes throughxcrun, which is the most likely thing to break in the sandbox), or./scripts/verify.sh.
š¤ Generated with Claude Code