feat(cli): enforce /add-dir via sandbox writable roots — in every host - #214
Merged
Conversation
Reworks #166, which was correct in approach but is now incomplete and carries stale docs. `permissions.additionalDirectories` has been declared in the settings schema and read by /permissions for display, but consumed for enforcement nowhere — /add-dir only printed "recorded ... (effective in M3)". Users could reasonably believe it did something. Why the sandbox is the right boundary: the file tools (Read/Write/Edit/ Glob/Grep) already accept any absolute path, so there is no cwd containment to widen. The only thing that actually restricts writes is the OS sandbox wrapping Bash. Enforcing /add-dir therefore means adding those directories to `filesystem.allowWrite`. The part #166 could not have covered: it predates the app-server, and wired only CLI REPL + headless. Since #192/#195/#196, desktop, VS Code and LSP all run through apps/server, so that wiring would have enforced the setting in the CLI and silently ignored it everywhere else — for a security-relevant setting, partial enforcement is worse than none, because it is indistinguishable from full enforcement at the UI. All 7 sandbox assembly sites across 4 files now route through the helper. - core `withAdditionalWritableDirs(sandbox, dirs, cwd?)`: pure, never mutates input, no-op when the sandbox is disabled (never silently enables it), dedupes, and drops non-absolute entries rather than handing them to profile writers that require absolute paths - /add-dir validates the path is an existing directory, stores it absolute (resolved against cwd), refuses duplicates, and lists the current set with no args - BEHAVIOR_PARITY: only the /add-dir row changes. #166 rewrote the whole table with June-era content that would have regressed /btw, /voice, /tasks and /background back to unshipped. (Prettier reflows the table columns; `git diff -w` shows the 2 real lines.) tsc -b --force, lint (--max-warnings=0), format:check, docs:check clean; 1048 tests pass (9 new helper + 6 new /add-dir); build clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oratis
added a commit
that referenced
this pull request
Aug 2, 2026
…ajors (#215) Covers the 31-PR Codex alignment stack (#180-#210) plus the dependency work (#211-#214, #155, #177-#179). Leads with the security section because that is what this release is: the central tool gate could be skipped entirely when `mode` was omitted (#181), and the desktop renderer held provider credentials (#192). Upgrade notes call out the two things a user could otherwise be surprised by — the ~6.7 MB → ~115 MB desktop size increase from the Node sidecar, and that the plugin capability RPC is not an OS boundary. Co-authored-by: t <t@t> Co-authored-by: Claude Opus 5 <noreply@anthropic.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.
Reworks #166. Same core idea, but that PR is now both incomplete and carries stale docs — details below.
The gap
permissions.additionalDirectoriesis declared in the settings schema and read by/permissionsfor display, but consumed for enforcement nowhere./add-dirprinted:M3 shipped. Nothing ever started reading it. A user running
/add-dirwould reasonably believe it took effect.Why the sandbox is the right boundary
The file tools (Read/Write/Edit/Glob/Grep) already accept any absolute path — there is no cwd containment to widen, so there's nothing for
/add-dirto "unlock" there. The only thing that actually restricts writes is the OS sandbox wrapping Bash. So enforcing/add-dirmeans folding those directories intofilesystem.allowWrite.That's #166's insight and it's correct.
What #166 could not have covered
#166 was written before the app-server existed, and wires only the CLI REPL and headless paths. Since #192/#195/#196, desktop, VS Code, and LSP all run through
apps/server. Merging #166 as-is would enforce the setting in the CLI and silently ignore it in every other client.For a security-relevant setting that's worse than not shipping it, because the two states are indistinguishable from the UI —
/add-dirreports success either way.All 7 sandbox assembly sites across 4 files now route through one helper:
apps/cli/src/repl.tsapps/cli/src/headless.tsapps/server/src/default-runtime.tsapps/server/src/runtime-composition.tsThe plugin-bridge sites matter as much as the RuntimeHost ones: a plugin's
bashcapability goes through the same sandbox, so missing those would leave plugin-issued writes on the old, narrower root set.The helper
withAdditionalWritableDirs(sandbox, dirs, cwd?)in core — pure, and deliberately conservative:allowWriteentries, and returns the original object unchanged when nothing new is added./add-diritselfValidates the path is an existing directory (a file is rejected explicitly), stores it absolute (resolved against
cwd— a relative entry would re-anchor to whatever cwd a later session happened to start in), refuses duplicates, and lists the current set when called with no args.Docs
Only the
/add-dirrow ofBEHAVIOR_PARITY.mdchanges. #166 rewrote the entire table with June-era content that would have regressed/btw,/voice,/tasksand/backgroundback to unshipped — all four have since landed in #168, #175 and #172. Prettier reflows the table columns on any edit, sogit diff -wis the useful view: 2 real lines.Verification
tsc -b --force,pnpm lint(--max-warnings=0),pnpm format:check,pnpm docs:check— cleanpnpm test— 1048 pass, including 9 new helper tests and 6 new/add-dirtestspnpm build— cleanThe helper tests pin the properties that make this safe rather than just the happy path: no-mutation, disabled-sandbox no-op, dedupe identity, and relative-entry dropping.
Closes #166.
🤖 Generated with Claude Code