vendor the sandbox-routing hook and native MCP http transport - #447
Merged
Conversation
`rm`, `mv`, `ln`, `chmod`, `chown` and `chgrp` carry no allow rule and are rejected by compound-bash-allow.py's DENY_BINARIES, so a bare invocation reaches the user as a permission prompt — and because that hook's verdict is all-or-nothing per invocation, one such segment forfeits auto-approval for an entire compound chain. redirect-fs-mutation.py returns `deny` with instructions to re-issue the command prefixed with sbx, but only when every path the segment mutates lies inside a root sbx binds read-write. Writable roots are derived the way bin/sbx derives them, so a redirect cannot hand back a command the sandbox would fail on with EROFS. The hook stays silent — normal permission flow — for anything it cannot prove: an operand outside the writable roots, `..`, a boundary-crossing symlink, a flag whose value is a separate token, `dd`'s `if=`/`of=` pairs, or a relative operand in a chain that `cd`s outside its first segment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deployed workspaces reach the workflow-server MCP endpoint directly, declaring `type: http` with the endpoint URL. The deploy script generates that entry for both `.mcp.json` and `.cursor/mcp.json`, and the checked-in example workspace carries the same shape, so a workspace copied by hand matches a deployed one. The generated entry overwrites whatever the merge source held, so the shape in deploy-cursor-workspace.sh is what every deployed workspace gets; the example files are what a reader copies. Co-Authored-By: Claude Opus 5 (1M context) <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.
Vendors the current Claude Code baseline into the deploy source, so the next
deploy-cursor-workspace.shrun reproduces what is actually being run today rather than reverting it.redirect-fs-mutation.py(new hook)rm/mv/ln/chmod/chown/chgrpcarry no allow rule and are rejected bycompound-bash-allow.py'sDENY_BINARIES, so a bare invocation reaches the user as a permission prompt. Because that hook's verdict is all-or-nothing per invocation, one such segment forfeits auto-approval for an entire chain — a 12-command chain whose only unmatched segment wasrm -rf /tmp/scratchprompted in full.The new hook returns
denywith instructions to re-issue the command prefixed withsbx, but only when every path the segment mutates provably lies inside a rootsbxbinds read-write. Writable roots are derived the waybin/sbxderives them (/tmp, plus the git top-level of the launch cwd when under the projects base), so a redirect cannot hand back a command the sandbox would fail on withEROFS.It stays silent — today's behaviour, a normal prompt — for anything it cannot prove: an operand outside the writable roots,
.., a symlink crossing the boundary in either direction, a flag whose value is a separate token (-t,--reference), any--opt=value,dd(itsif=/of=operands are not positional), or a relative operand in a chain thatcds outside its first segment. Fails open on any parse error.redirect-inline-eval.py's docstring previously disclaimed this case ("filesystem-mutating binaries ... are NOT forced"); it now points at the new hook and narrows its own disclaimer toddand undecidable operand shapes.Verified: 16 cases covering both directions, plus the hook firing on a real bare
chmodand the re-issuedsbx chmodauto-approving.Native MCP http transport
merge_mcp_jsongenerated theworkflow-serverentry asnpx -y mcp-remote <url>. It now emits{"type": "http", "url": <url>}, and the two checked-in example configs carry the same shape so a hand-copied workspace matches a deployed one.Note this is the transport shape Claude Code is being run with locally today; the same shape now also goes to
.cursor/mcp.json, since deploy writes one merged document to both files.Deliberately not included
examples/cursor-workspace/.claude/rules/bash-composition.md— the rule prose still describes the pre-hook behaviour and listsddamong thesbx-able mutators. Updating it was explicitly declined earlier, so it is left alone; the vendored bundle therefore registers a hook its own rule text does not describe.workflow-canonskill — the vendored copy is newer than the live workspace copy (it carries the change-surface / I-O-contract-closure material the workspace lacks), so nothing was copied workspace-ward.~/.claude/hooks—curl-allow.jsonandwebfetch-allow.jsonname midnight-specific hosts andblock-dynamic-shell.pyhardcodes an absoluteCLAUDE.mdpath. The generic vendored forms are correct for a portable baseline.Checks
bash -n scripts/deploy-cursor-workspace.shpasses; the embeddedmerge_mcp_jsonPython block compiles clean.🤖 Generated with Claude Code