Please report security issues privately through GitHub private vulnerability reporting: open the Security tab of the repository and choose "Report a vulnerability". Do not open a public issue or pull request for a vulnerability.
Useful things to include: the zerostack version (zerostack --version), the
platform, the configuration involved (with API keys removed), and the smallest
reproduction you have.
Fixes ship in the next release from main. There are no long-term support
branches, so please upgrade to the latest release before reporting.
zerostack can run the commands issued by the bash tool inside an isolated
environment. This is opt in: --sandbox on the command line, or sandbox = true in the config file. The backend is selected with --sandbox-backend /
sandbox-backend: bwrap (bubblewrap,
the default, Linux only) or zerobox (zerobox,
macOS and Linux, installable with cargo install zerobox among others).
The sandbox exists to contain accidental damage from commands the model proposes. It is not a full security boundary against an adversary who controls the command being run.
With the default bwrap backend, a sandboxed command sees:
/bind mounted read only, so writes outside the allowed paths fail- the current working directory bind mounted read write, so the project you are working in stays editable
- a fresh
/tmp(tmpfs), a private/proc, and a minimal/dev - separate IPC, PID, UTS, and cgroup namespaces, so the command cannot see or signal processes outside the sandbox
- a cleared environment, repopulated with an allowlist of common variables
(
PATH,HOME, toolchain and locale variables, and so on) --die-with-parent, so the sandboxed process tree does not outlive zerostack
In practice this stops the common accidents: a stray rm -rf outside the
project, an installer writing into system directories, a build script editing
files elsewhere on the machine.
The zerobox backend is shaped differently. zerobox is powered by the OpenAI
Codex sandbox runtime and denies writes, network access, and environment
variables by default, with network access grantable per domain. zerostack
invokes it as zerobox --allow-write <cwd> -- <shell> -c <command>, so the only
hole zerostack opens in that policy is write access to the working directory,
and everything else follows zerobox's own defaults.
These are known gaps, listed so you can decide whether the sandbox is enough for
your situation. Most of them come from how zerostack configures the default
bwrap backend; the last group applies whichever backend you pick.
With the bwrap backend:
- Network access is fully open. zerostack does not unshare the network
namespace, so a sandboxed command can reach the internet and your local
network, which means it can exfiltrate anything it can read. The
zeroboxbackend behaves differently here: it denies network access by default under its own policy. - Your home directory is readable.
/is mounted read only, not hidden, so everything under$HOMEis visible inside the sandbox: SSH keys, cloud credentials,.envfiles, browser profiles, shell history. - The whole user cache directory is writable.
~/.cache(or$XDG_CACHE_HOME) is bind mounted read write so that build tooling works. Anything cached there, including tool caches other programs trust, can be modified. - A running SSH agent stays reachable. The environment allowlist includes
SSH_AUTH_SOCKandSSH_AGENT_PID, so a sandboxed command can sign with the keys your agent holds. - Kernel level escapes are out of scope of this design. bubblewrap uses user namespaces; a kernel vulnerability, or a host configured to grant more than the usual namespace privileges, can defeat the isolation.
With any backend:
- Only the
bashtool is sandboxed. File reads and writes performed by zerostack's own tools, MCP servers, hooks, and shell commands you run yourself with the!prefix all run outside the sandbox. They are governed by the permission system, not by this isolation. - Backend availability depends on the platform.
bwrapis Linux only, so on macOS the default backend is missing andsandbox = truealone gives you no isolation: commands run bare, with a warning in the logs. Install zerobox and setsandbox-backend = "zerobox"for real isolation on macOS, or setsandbox-requiredso those commands are refused instead of run bare. - zerostack does not verify what the backend enforces. It launches the backend with the arguments described here and trusts the result. What this document says about zerobox is its documented default behavior, not the result of an audit of its implementation.
Treat the sandbox as a seatbelt against mistakes, not as a container for untrusted code. If you need a real boundary, run zerostack itself inside a VM or a container with the network and credentials you are willing to expose.
| Backend | Isolation |
|---|---|
bwrap (default) |
Linux only. The bubblewrap mounts and namespaces described above, with the network left open. |
zerobox |
macOS and Linux. Denies writes, network access, and environment variables by default, with per-domain network allowances. zerostack invokes zerobox --allow-write <cwd> -- <shell> -c <command>, so the working directory is writable and the rest of the policy is whatever zerobox enforces. |
| none | With the sandbox off (the default), bash commands run directly as your user with no isolation at all. The permission system is the only gate. |
Two different contracts:
sandbox = true(or--sandbox) is best effort. If the backend binary is not installed, zerostack logs a warning at startup and each command runs unsandboxed rather than failing. Sessions keep working on machines without the backend, but with no isolation.sandbox-required = true(or--sandbox-required) is the guarantee. When the backend binary is unavailable, bash commands are refused with an error that says why, instead of running bare.sandbox-requiredimpliessandbox.
sandbox-required does not exit at startup. Everything else in the session
(reading files, editing, planning) keeps working, only bash execution is
refused. This is the setting to use for unattended or automated runs, where
nobody is watching the log for the "running unsandboxed" warning.
Neither setting changes the gaps listed above. sandbox-required guarantees
that the isolation is present, not that the isolation is complete.