Repository navigation
Conversation
…ng Seatbelt Without the write sandbox the guard refused every operand that reached a manager-owned dependency link, so cheap hosted lanes paid a model round for `ls node_modules/.bin`. Read-only commands (cat, ls, grep, head, ...) may now read through the link on every lane; writes, redirections, and any other command still need Seatbelt. The lane-parity check found the Claude hook asked hasMacosWriteSandbox(), which is true on macOS even though Claude Code runs Bash outside baro's profile: that lane allowed `cp x node_modules/...` and `node -e` with no sandbox at all. Whether a command runs sandboxed is now a field of the access context: the native lane sets it from the sandbox it actually spawns, the hook sets it false. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #182
Summary
#189 fixed the refusal text. This PR covers the two items the issue thread left open.
1. Dependency reads are allowed on every lane; writes are still refused.
Before this change, any operand that reached a manager-owned dependency link (
node_modules,.venv,vendor) was allowed only when the command ran under Seatbelt. As the design note on #182 explains, the guard can't tell a read from a write by looking at a path token. So on Linux/hosted lanes,ls node_modules/.bin,ls ./node_modulesandcat node_modules/xwere all refused. That accounts for 3 of the 7 refusals inrun-116-mubbgmhx.The guard now looks at the command that owns the operand, using option (a) from the design note. A small allowlist of commands that have no option to write or spawn on a file operand may read through the link without the sandbox:
cat head tail ls wc grep egrep fgrep diff cmp stat du readlink realpath test [ md5sum sha1sum sha256sum shasum.scripts/catis not treated as the systemcat.|,&∧. So incat x | tee node_modules/y, theteeoperand is still refused.cdtargets never get the exception.cp,mv,tee,touch,ln,find,sort -oand the opaque interpreters.node_modules/.bin/vitest …) already passed the guard. A test now pins that behaviour on the portable guard.2. Lane-parity check: the Claude hook was more permissive than intended on macOS.
The hook called
hasMacosWriteSandbox(), which is true on any Mac withsandbox-exec. But Claude Code runs Bash itself, outside baro's Seatbelt profile. As a result, on macOS the Claude lane accepted:cp README.md node_modules/fx/index.js, a write into the dependency dir that every concurrent story shares;node -einline code with no sandbox at all.I reproduced both against
hookShellAccessContextbefore changing anything. "Does this command run sandboxed?" is now a field onShellAccessContext(writeSandbox):prepareShellSandboxreads the same field, so the two can't drift;false.Behaviour change on macOS: the Claude lane now refuses
node -e/python -c/perl -e/ruby -einline code, the same way Linux lanes already do. Because of item 1, dependency reads stay allowed there.3. Refusal texts.
"$VAR"can expand to any path. Both lanes already run the same tokenizer since Claude lane has no shell containment: a story agent created and removed a second git worktree under mktemp, touching the host checkout's .git/worktrees #169, so the verdicts were identical by construction. What actually differed between lanes was the sandbox claim in item 2.isDependencyReadCommandkeeps its set inline and is added toHOOK_SOURCE_GRAPH. This follows the file-header contract that the hook source is serialized withfn.toString().Test plan
npm run typecheck -w @baro/orchestrator: cleantest/story-tool-containment.test.ts, all pass. Changes:ls/test/grep -r/head | wcthrough the link and runningnode_modules/.bin/<tool>work, whilecp/mv/| tee/>/touch/ln/find -delete/sort -o/cdinto the link are refused and the dependency file is left untouched;scripts/catgets no read exception.test/execution/shell-containment.test.ts, all pass. New test: the hook context reportswriteSandbox: false; reads through the link are allowed;cp, redirection andnode -eare refused. The serialized hook gives the same verdicts.test/harness/hook-bridge.test.ts: passnpm run test:fast: 1636/1644 pass. The failures are timing-sensitive tests on a heavily loaded machine:run-architect-awake-budget,verification/command-timing,continuous-gate-runnerandprovider-ownership-manifest. The first three fail the same way on unmodifiedorigin/main, checked by stashing this change.provider-ownership-manifestpassed when re-run on its own. No Rust changes, so cargo was not run.node_modulesescape refusals for read-only commands.Not in this PR
rgis not on the read allowlist becauserg --pre <cmd>runs a command per file. It could be added later with a--predenial.node -eon macOS for that lane.Generated with Devin