Bundle of template- and docs-only changes surfaced by a sandbox breakout audit. No code-behavior changes.
1. Make the template's default home an allowlist
Simplest and strongest change. The current logic already supports this — blacklist applies tmpfs, whitelist (now ro as of 202604.4.1a0) binds specific paths back read-only, writable binds rw, and all of them are applied after the initial --ro-bind $HOME $HOME, so later args override. Shift the template from:
blacklist = [
"~/.ssh", "~/.gnupg", "~/.aws", "~/.kube", "~/.config/gcloud",
"~/.azure", "~/.docker", "~/.npmrc", "~/.pypirc", "~/.boto", "/mnt",
]
to:
blacklist = [
"~", # deny-by-default home
"/mnt", # WSL Windows drives (cmd.exe/powershell interop escape)
]
whitelist = [ # read-only exceptions (dotfiles a shell needs)
"~/.gitconfig",
"~/.config/fish", # or "~/.bashrc" / "~/.zshrc" depending on shell
"~/.terminfo",
"~/.local/share/fish", # fish history + functions; adjust per shell
]
This closes the whole class of "tool X added a credential file we forgot to blacklist" regressions (#24 folded in here). Supersedes items (3) below — with ~ blacklisted, .claude.json/.bash_history/.age/.config/op/.config/pypoetry/.config/uv are all hidden by default with no enumeration needed.
Action needed: figure out the minimum whitelist set that keeps a typical fish / bash / zsh login functional — probe empirically.
2. Move ~/.pyenv/shims from writable to whitelist
Template currently suggests writable = ["~/.pyenv/shims"]. Shims only need read+exec to run; write access inside the sandbox is a persistent-code-execution vector (drop a shim, host picks it up next time the user runs python outside the sandbox).
Now that whitelist is read-only, move pyenv to a read-only whitelist entry — or at minimum add a warning comment that writable grants host-writable access.
3. (Obsoleted by (1))
Previously: expand the enumerated blacklist with .claude.json, .bash_history, .age, .config/op, .config/pypoetry, .config/uv. Unnecessary if (1) lands — blacklisting ~ covers all of them and every future credential file too.
4. Recommend clean_env = true in template
Probe caught ANTROPIC_API_KEY (note the typo — treat as real and rotate, or label as decoy) and CODESTRAL_API_KEY leaking from host env into the sandbox. clean_env already exists and clears everything but PATH/HOME/USER/SHELL/TERM/LANG, then re-adds from [env]. Template currently has it commented out.
Uncomment it and make clean_env = true the template default; document the [env] pass-through pattern.
5. README: WSL-specific guidance
/etc/resolv.conf on WSL is a symlink into /mnt/wsl, so users blacklisting /mnt need to whitelist something back or DNS breaks. Current template comment suggests whitelist = ["/mnt/wsl"] — too broad; that tree contains Docker Desktop sockets and other host-reachable surfaces.
Change the template recommendation and README to:
whitelist = ["/mnt/wsl/resolv.conf"]
If the user needs wslg (GUI apps), suggest "/mnt/wslg" rather than the /mnt/wsl parent.
Scope
All template/README edits. No behavioral change, no new config keys. Should land as one commit on a dedicated branch.
Bundle of template- and docs-only changes surfaced by a sandbox breakout audit. No code-behavior changes.
1. Make the template's default home an allowlist
Simplest and strongest change. The current logic already supports this —
blacklistapplies tmpfs,whitelist(now ro as of202604.4.1a0) binds specific paths back read-only,writablebinds rw, and all of them are applied after the initial--ro-bind $HOME $HOME, so later args override. Shift the template from:to:
This closes the whole class of "tool X added a credential file we forgot to blacklist" regressions (#24 folded in here). Supersedes items (3) below — with
~blacklisted,.claude.json/.bash_history/.age/.config/op/.config/pypoetry/.config/uvare all hidden by default with no enumeration needed.Action needed: figure out the minimum whitelist set that keeps a typical
fish/bash/zshlogin functional — probe empirically.2. Move
~/.pyenv/shimsfromwritabletowhitelistTemplate currently suggests
writable = ["~/.pyenv/shims"]. Shims only need read+exec to run; write access inside the sandbox is a persistent-code-execution vector (drop a shim, host picks it up next time the user runspythonoutside the sandbox).Now that
whitelistis read-only, move pyenv to a read-only whitelist entry — or at minimum add a warning comment thatwritablegrants host-writable access.3. (Obsoleted by (1))
Previously: expand the enumerated blacklist with
.claude.json,.bash_history,.age,.config/op,.config/pypoetry,.config/uv. Unnecessary if (1) lands — blacklisting~covers all of them and every future credential file too.4. Recommend
clean_env = truein templateProbe caught
ANTROPIC_API_KEY(note the typo — treat as real and rotate, or label as decoy) andCODESTRAL_API_KEYleaking from host env into the sandbox.clean_envalready exists and clears everything butPATH/HOME/USER/SHELL/TERM/LANG, then re-adds from[env]. Template currently has it commented out.Uncomment it and make
clean_env = truethe template default; document the[env]pass-through pattern.5. README: WSL-specific guidance
/etc/resolv.confon WSL is a symlink into/mnt/wsl, so users blacklisting/mntneed to whitelist something back or DNS breaks. Current template comment suggestswhitelist = ["/mnt/wsl"]— too broad; that tree contains Docker Desktop sockets and other host-reachable surfaces.Change the template recommendation and README to:
If the user needs wslg (GUI apps), suggest
"/mnt/wslg"rather than the/mnt/wslparent.Scope
All template/README edits. No behavioral change, no new config keys. Should land as one commit on a dedicated branch.