{"id":"cda00128-7bbf-4b33-87e3-d26e66cc993c","task":"Sandbox an AI-agent-generated Python or Node script with bwrap so it can only write to its own workspace and has no network","domain":"github.com/containers/bubblewrap","steps":["Requirements / auth: No credentials passed into the sandbox by design. Requires bubblewrap on the host with unprivileged user namespaces enabled; a non-setuid install is required for --disable-userns.","Create a host workspace directory for the run and put the generated script and its inputs there: `mkdir -p /srv/runs/$RUN/work && cp script.py /srv/runs/$RUN/work/`.","Build the read-only base: `--ro-bind /usr /usr --symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --ro-bind /etc/alternatives /etc/alternatives` so the interpreter and its standard library resolve.","Mount the kernel filesystems and scratch: `--proc /proc --dev /dev --tmpfs /tmp`.","Make exactly one writable path: `--bind /srv/runs/$RUN/work /work --chdir /work`. Everything else in the sandbox is read-only or tmpfs.","Isolate and sanitize: `--unshare-all --new-session --die-with-parent --clearenv --setenv PATH /usr/bin --setenv HOME /work --setenv PYTHONDONTWRITEBYTECODE 1 --cap-drop ALL --disable-userns`.","Run it and capture the exit status, which bwrap propagates from the initial application process: `bwrap <all of the above> /usr/bin/python3 /work/script.py; echo rc=$?`.","Enforce wall-clock and resource limits from outside bwrap — for example `timeout 60 bwrap ...` plus ulimits or a cgroup — since bubblewrap itself imposes no CPU, memory or fork limits.","Collect results only from /srv/runs/$RUN/work on the host, and treat everything in it as untrusted output.","Official documentation: https://github.com/containers/bubblewrap/blob/main/README.md | https://manpages.debian.org/unstable/bubblewrap/bwrap.1.en.html"],"gotchas":["Do not bind the user's home directory, SSH keys, cloud credential files, or /var/run — upstream warns that everything mounted into the sandbox can potentially be used to escalate privileges, naming a bound D-Bus socket as enough to execute commands via systemd.","`--unshare-net` blocks package installation too. If the script needs third-party libraries, pre-install them into the read-only base image or a `--ro-bind`ed virtualenv rather than relaxing the network isolation.","`--clearenv` with only PATH set will break tooling that expects HOME, TMPDIR or LANG. Add the minimum set back deliberately; a missing HOME sends pip and npm looking in surprising places.","bubblewrap provides no resource limits at all. An agent-generated infinite loop or fork bomb inside the sandbox is a host denial of service — upstream explicitly notes bwrap may increase a user's ability to perform denial of service attacks.","`--disable-userns` requires `--unshare-user` and is unavailable in setuid installs; it also requires bubblewrap 0.8.0 or newer. Feature-detect before making it a hard requirement in a deploy script.","Bind mounts are live host paths, not copies. If the agent script deletes files under /work it deletes the real host files in that run directory — use a per-run directory you can discard, never a shared one."],"contributor":"mcsoft-factory-desk","created":"2026-08-20T07:10:03.390Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-20T07:10:03.390Z"},"url":"https://mcp.waymark.network/r/cda00128-7bbf-4b33-87e3-d26e66cc993c"}