fix(sandbox): install bubblewrap/socat + probe so bash isolation actually engages#58
Merged
Conversation
…ally engages The bash tool is meant to be confined to its project dir via @anthropic-ai/sandbox-runtime (bubblewrap on Linux), blocking cross-project reads. But the image never installed bwrap/socat, so SandboxManager.initialize() threw on the missing deps, the extension caught it, and bash silently ran UNSANDBOXED in production — any prompt-injected command could read/write sibling projects under /app/data/projects/<id> (the in-process read/ls/grep/find tools stay safe via JS path checks; the gap was bash-specific). - Dockerfile: install bubblewrap + socat (ripgrep already present). - Add a runtime probe (sandboxActuallyRuns): initialize() only which-checks the binaries, but a present bwrap still fails if the container forbids user/mount namespaces. Wrap a trivial command and run it; only trust the sandbox if it exits cleanly. Without this, a deploy that ships bwrap but blocks userns would break *every* bash command instead of falling back. - Log sandbox-unavailable loudly via the server logger (was a silent ctx.ui.notify), so a fail-open can't go unnoticed. - PI_REQUIRE_BASH_SANDBOX=1 makes bash fail *closed* (refused) when the sandbox can't engage — flip it on once the deploy is verified so a regression can't silently re-open the hole. PLATFORM FOLLOW-UP (not expressible in .ocd-deploy.json): bubblewrap needs the container launched with --security-opt seccomp=unconfined (or CAP_SYS_ADMIN / userns). Until OCD grants that, the probe fails and bash falls back to unsandboxed (now loudly logged).
Pairs with the OCD per-app userns flag — launches the zero-server container with --security-opt=seccomp=unconfined so the agent's bash sandbox (bubblewrap) can create user/mount namespaces.
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.
Problem
The
bashtool is supposed to be confined to its project dir via@anthropic-ai/sandbox-runtime(bubblewrap on Linux) —denyReadcovers the whole projects root,allowReadre-allows only the current project. But the image never installedbwrap/socat, so on LinuxSandboxManager.initialize()threw (bubblewrap (bwrap) not installed), the extension caught it, and bash silently ran unsandboxed in production.Verified on
zero-server:So a prompt-injected/buggy bash command could read or write any sibling project under
/app/data/projects/<id>. (The in-processread/ls/grep/find/write/edittools stay safe — they enforce project scope with JS path checks — so the gap was bash-specific.)Changes (this repo)
bubblewrap+socat(ripgrepalready present).sandboxActuallyRuns):initialize()onlywhich-checks the binaries, but a presentbwrapstill fails if the container forbids user/mount namespaces. We wrap a trivial command and run it; only trust the sandbox if it exits cleanly. Without this, a deploy that shipsbwrapbut blocks userns would break every bash command instead of falling back.error(was a silentctx.ui.notify), so a fail-open can't go unnoticed.PI_REQUIRE_BASH_SANDBOX=1: makes bash fail closed (refused) when the sandbox can't engage. Flip it on once the deploy is verified so a regression can't silently re-open the hole.bubblewrap needs user/mount namespaces, which the container currently can't create (
CapEff=0, seccomp blocksunshare). The.ocd-deploy.jsonmanifest has no field forseccomp/cap_add/privileged, so this must be granted by how the OCD platform launches the container — e.g.--security-opt seccomp=unconfined(orCAP_SYS_ADMIN/ userns).Until that lands, the probe fails and bash falls back to unsandboxed (now loudly logged) — same behavior as today, minus the silence. Once the platform grants the privilege and a redeploy confirms
🔒 Bash sandboxed, setPI_REQUIRE_BASH_SANDBOX=1.Safety of merging now
Auto-deploy will rebuild with bwrap/socat present. The probe will fail (no userns yet) → bash falls back to unsandboxed exactly as before, but with a loud error log. No regression to bash behavior.