- Add CREDS_FILE/CREDS_MOUNT detection after mkdir ~/.claudebox - Conditional --bind in exec bwrap via BWRAP_ARGS array - Mirror conditional bind in --dry-run display block - Read-write mount (not ro-bind) for OAuth token refresh - Silent skip when credentials file absent (no error/warning) - Refactor exec bwrap to BWRAP_ARGS array for conditional mount support
401 lines
21 KiB
Markdown
401 lines
21 KiB
Markdown
# Domain Pitfalls
|
|
|
|
**Domain:** Bubblewrap sandbox wrappers for CLI tools on NixOS
|
|
**Researched:** 2026-04-09
|
|
**Confidence:** MEDIUM (training data only, no live verification available)
|
|
|
|
## Critical Pitfalls
|
|
|
|
Mistakes that cause sandbox escapes, broken tools, or full rewrites.
|
|
|
|
### Pitfall 1: Environment Variable Leaks via Inherited Env
|
|
|
|
**What goes wrong:** Using `--unshare-all` or selectively binding vars but forgetting that bwrap inherits the parent environment by default. Every env var the shell has leaks into the sandbox unless you explicitly clear it. Variables like `DBUS_SESSION_BUS_ADDRESS`, `SSH_AUTH_SOCK`, `XDG_RUNTIME_DIR`, `GNOME_KEYRING_CONTROL`, `GPG_AGENT_INFO`, `AWS_PROFILE`, `KUBECONFIG`, and `DOCKER_HOST` silently pass through.
|
|
|
|
**Why it happens:** bwrap does NOT start with an empty environment. People assume `--unshare-*` flags affect env vars -- they do not. Namespace unsharing and environment are completely orthogonal.
|
|
|
|
**Consequences:** The entire security model of claudebox collapses. Claude Code can read `SSH_AUTH_SOCK`, connect to the agent, and sign things. AWS/GCP tokens pass through. The sandbox is theater.
|
|
|
|
**Prevention:** Use `env -i` before the bwrap call, then explicitly set only allowlisted variables:
|
|
|
|
```bash
|
|
exec env -i \
|
|
HOME="$HOME" \
|
|
TERM="$TERM" \
|
|
PATH="$SANDBOX_PATH" \
|
|
LANG="$LANG" \
|
|
bwrap [flags] ...
|
|
```
|
|
|
|
Alternatively, `--clearenv` is available in bwrap 0.8.0+. Verify the version in nixpkgs before relying on it.
|
|
|
|
**Detection:** Run `env` inside the sandbox. If it shows more than your allowlist, you have a leak. Build this as an automated test.
|
|
|
|
**Phase:** Must be correct from Phase 1. This is the core security invariant.
|
|
|
|
---
|
|
|
|
### Pitfall 2: Missing /dev Nodes Causing Silent Failures
|
|
|
|
**What goes wrong:** A minimal `--dev /dev` mount is used, but tools need specific device nodes. Claude Code (Node.js) needs `/dev/null`, `/dev/zero`, `/dev/urandom`, and crucially `/dev/fd`, `/dev/stdin`, `/dev/stdout`, `/dev/stderr`. Git needs `/dev/tty` for credential prompts. Node.js needs `/dev/shm` for V8's shared memory segments. Missing `/dev/ptmx` and `/dev/pts` means no PTY allocation, breaking interactive features.
|
|
|
|
**Why it happens:** `--dev /dev` in bwrap creates a minimal devtmpfs, but the exact set of nodes depends on bwrap version. People test simple commands and miss that complex runtimes need more.
|
|
|
|
**Consequences:** Node.js crashes or hangs. Git operations that prompt for input hang forever. `nix shell` commands fail because nix-daemon communication breaks. The sandbox "works" for trivial tests but fails under real use.
|
|
|
|
**Prevention:** Use `--dev /dev` (not `--dev-bind /dev /dev` which exposes host devices) and then verify these exist inside:
|
|
|
|
- `/dev/null`, `/dev/zero`, `/dev/full`
|
|
- `/dev/random`, `/dev/urandom`
|
|
- `/dev/fd` (symlink to `/proc/self/fd`)
|
|
- `/dev/stdin`, `/dev/stdout`, `/dev/stderr`
|
|
- `/dev/shm` (tmpfs mount)
|
|
- `/dev/pts` and `/dev/ptmx` (for PTY)
|
|
- `/dev/tty` (for git, ssh prompts)
|
|
|
|
If `--dev /dev` does not provide `/dev/shm`, add `--tmpfs /dev/shm`. If PTY is missing, bind-mount from host: `--dev-bind /dev/pts /dev/pts`.
|
|
|
|
**Detection:** Run `ls -la /dev/` inside the sandbox. Run `node -e "process.stdout.write('test')"`. Run `git status` in a repo. If any hang or error, missing dev nodes.
|
|
|
|
**Phase:** Phase 1. Without correct /dev, nothing works.
|
|
|
|
---
|
|
|
|
### Pitfall 3: Nix Store and Nix Daemon Socket Access
|
|
|
|
**What goes wrong:** Mounting `/nix/store` read-only is remembered, but the nix-daemon socket at `/nix/var/nix/daemon-socket/socket` is forgotten. Without it, `nix shell`, `nix build`, and comma (`,`) all fail because they need to talk to the daemon for store operations. Additionally, `/nix/var/nix/db` may be needed for local queries.
|
|
|
|
**Why it happens:** People think of Nix store as just the read-only `/nix/store` path. But `nix shell nixpkgs#foo` needs the daemon to fetch and realize store paths. The daemon socket is in a completely different path.
|
|
|
|
**Consequences:** The entire "Claude can self-install tools via comma" feature is dead. Claude is stuck with whatever is in the pre-declared PATH. This defeats a core design goal.
|
|
|
|
**Prevention:**
|
|
|
|
```bash
|
|
--ro-bind /nix/store /nix/store \
|
|
--ro-bind /nix/var/nix/db /nix/var/nix/db \
|
|
--ro-bind /nix/var/nix/daemon-socket /nix/var/nix/daemon-socket \
|
|
```
|
|
|
|
The socket itself needs to be accessible (not just the directory). Ensure the user inside the sandbox is in the `nixbld` group or that the daemon socket permissions allow access (they usually do for all users on NixOS).
|
|
|
|
Also mount `--ro-bind /etc/nix /etc/nix` for nix.conf (channels, substituters, trusted keys).
|
|
|
|
**Detection:** Run `nix shell nixpkgs#hello -c hello` inside the sandbox. If it hangs or errors with "cannot connect to daemon", the socket is not mounted.
|
|
|
|
**Phase:** Phase 1. This is a core requirement per PROJECT.md.
|
|
|
|
---
|
|
|
|
### Pitfall 4: DNS Resolution Fails Inside Sandbox
|
|
|
|
**What goes wrong:** DNS resolution breaks because `/etc/resolv.conf`, `/etc/nsswitch.conf`, and the NSS libraries are not available inside the sandbox. On NixOS specifically, `/etc/resolv.conf` is often a symlink into the Nix store or managed by systemd-resolved (pointing to a stub resolver at 127.0.0.53), and the NSS shared libraries live in the Nix store at paths determined at build time.
|
|
|
|
**Why it happens:** People bind-mount `/etc/resolv.conf` but forget that glibc's NSS needs `nsswitch.conf` plus the actual `.so` files for `nss_dns`, `nss_files`, `nss_resolve`. On NixOS these are in the Nix store, not in `/lib`. Without them, `getaddrinfo()` returns `EAI_NONAME` even though resolv.conf is present.
|
|
|
|
**Consequences:** `curl`, `git clone`, `nix shell` (which fetches from cache.nixos.org), and Claude Code's own API calls all fail. The sandbox is offline despite having network access.
|
|
|
|
**Prevention:**
|
|
|
|
```bash
|
|
--ro-bind /etc/resolv.conf /etc/resolv.conf \
|
|
--ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \
|
|
--ro-bind /etc/ssl /etc/ssl \
|
|
--ro-bind /etc/hosts /etc/hosts \
|
|
```
|
|
|
|
Since `/nix/store` is already mounted and your tools come from there, their RUNPATH/LD_LIBRARY_PATH should resolve the NSS `.so` files from the store. If you use `--symlink` for `/etc/resolv.conf` because it is itself a symlink, make sure the symlink target is also mounted.
|
|
|
|
On NixOS with systemd-resolved, the stub resolver at 127.0.0.53 works fine inside the sandbox since network namespaces are not used (per PROJECT.md, no network isolation).
|
|
|
|
**Detection:** Run `curl -s https://cache.nixos.org` or `getent hosts github.com` inside the sandbox. If they fail, DNS is broken.
|
|
|
|
**Phase:** Phase 1. Without DNS, nothing network-dependent works.
|
|
|
|
---
|
|
|
|
### Pitfall 5: /tmp Handling Breaks Build Tools and Nix
|
|
|
|
**What goes wrong:** Using `--tmpfs /tmp` (correct instinct -- don't share host /tmp) but then Nix builds fail because the nix-daemon expects to be able to create build directories in `/tmp` that are accessible to both the daemon and the client. Also, some tools use `/tmp` for Unix domain sockets (X11, Wayland, PulseAudio) whose paths are referenced by env vars that were cleared.
|
|
|
|
**Why it happens:** `/tmp` is both a security-sensitive directory (shared temp files = symlink attacks, info leaks) and a critical coordination point for IPC.
|
|
|
|
**Consequences:** `nix build` and `nix shell` fail with permission errors or "cannot create temp directory". Node.js `os.tmpdir()` works but may fill a small tmpfs. Build operations that need significant temp space fail silently.
|
|
|
|
**Prevention:** Use `--tmpfs /tmp` but size it generously:
|
|
|
|
```bash
|
|
--tmpfs /tmp # defaults to 50% of RAM, which is usually fine
|
|
```
|
|
|
|
For Nix specifically: the daemon runs outside the sandbox, so it uses its own `/tmp`. The client inside the sandbox only needs `/tmp` for its own temp files, not for build directories. This means `--tmpfs /tmp` works fine for `nix shell` -- the potential issue is only if you try to run `nix-build` in single-user mode inside the sandbox (don't).
|
|
|
|
Set `TMPDIR=/tmp` explicitly in the env allowlist so tools don't inherit a host-specific `TMPDIR` pointing to a non-existent path.
|
|
|
|
**Detection:** Run `mktemp` and `nix shell nixpkgs#hello -c hello` inside the sandbox.
|
|
|
|
**Phase:** Phase 1.
|
|
|
|
---
|
|
|
|
### Pitfall 6: Git Broken Inside Sandbox
|
|
|
|
**What goes wrong:** Git fails in multiple ways inside a bwrap sandbox:
|
|
|
|
1. **Git config not found:** `~/.gitconfig` is not mounted, so user identity (name, email) is missing. Commits fail.
|
|
2. **Safe directory check:** Git 2.35.2+ rejects repositories owned by a different user. Inside bwrap, UID mapping can cause the repo files to appear owned by a different UID, triggering `fatal: detected dubious ownership in repository`.
|
|
3. **Git credential helpers:** Configured helpers (e.g., `git-credential-libsecret`, `gh auth`) reference binaries and sockets not in the sandbox.
|
|
4. **Git hooks:** Pre-commit hooks may invoke tools not available in the minimal PATH.
|
|
5. **SSH remotes:** `~/.ssh` is intentionally hidden, so `git push/pull` over SSH fails.
|
|
|
|
**Why it happens:** Git has grown a large surface area of external dependencies (config, credentials, hooks, gpg signing). Sandboxing the filesystem breaks all of them.
|
|
|
|
**Consequences:** Claude cannot commit (the primary use case). Or worse, commits succeed but with wrong identity, triggering CI failures or attribution issues.
|
|
|
|
**Prevention:**
|
|
|
|
```bash
|
|
# Mount gitconfig read-only (strip credential helper lines if needed)
|
|
--ro-bind "$HOME/.gitconfig" "$HOME/.gitconfig" \
|
|
|
|
# OR generate a minimal gitconfig inside the sandbox
|
|
# with just user.name and user.email
|
|
|
|
# For safe.directory, add the CWD:
|
|
git config --global safe.directory "$PWD"
|
|
# Or set GIT_CONFIG_GLOBAL to a sandbox-specific config
|
|
```
|
|
|
|
For the `safe.directory` issue: since claudebox does NOT use `--unshare-user` (UID mapping), this should not trigger. But verify -- if you do use user namespaces, the repo UID won't match and git will refuse.
|
|
|
|
For credentials: Claude Code should not push to remotes. If it tries, failing is the correct behavior. Document this as intentional.
|
|
|
|
For hooks: pre-commit hooks need tools in PATH. Either include common hook tools (node, python) in the sandbox PATH or accept that hooks may fail.
|
|
|
|
**Detection:** Run `git log`, `git diff`, `git commit` inside the sandbox. Check `git config --list` for expected values.
|
|
|
|
**Phase:** Phase 1. Git is in the core PATH per requirements.
|
|
|
|
---
|
|
|
|
## Moderate Pitfalls
|
|
|
|
### Pitfall 7: /proc Mount Leaks Host Information
|
|
|
|
**What goes wrong:** Using `--proc /proc` (correct) but not realizing that `/proc` exposes the host's process list, network config, and system info unless PID namespace is unshared. `/proc/net/tcp` reveals all host connections. `/proc/*/environ` of other processes may leak secrets.
|
|
|
|
**Why it happens:** `--proc /proc` mounts a procfs, but its contents depend on whether `--unshare-pid` is used.
|
|
|
|
**Consequences:** Claude Code can read host process info. Not a direct secret leak, but violates the principle of minimal exposure.
|
|
|
|
**Prevention:** Use `--unshare-pid --proc /proc`. This gives the sandbox its own PID namespace, so `/proc` only shows sandbox processes. Note: `--unshare-pid` requires a new mount namespace (`--unshare-all` or `--unshare-pid` standalone). Verify the bwrap invocation order.
|
|
|
|
**Detection:** Run `ls /proc/` inside the sandbox. If you see hundreds of PIDs, PID namespace is not isolated.
|
|
|
|
**Phase:** Phase 1, but not blocking. Enhancement after basic functionality works.
|
|
|
|
---
|
|
|
|
### Pitfall 8: TTY/PTY Not Properly Forwarded
|
|
|
|
**What goes wrong:** Claude Code is an interactive CLI tool that needs a proper terminal. Inside bwrap, if the controlling TTY is not forwarded, `isatty()` returns false, disabling color output, interactive prompts, and progress indicators. Worse, if Claude Code tries to allocate a PTY (for running subcommands), it fails without `/dev/pts`.
|
|
|
|
**Why it happens:** bwrap by default inherits the parent's stdio file descriptors, so basic TTY works. But PTY allocation for subprocesses needs `/dev/pts` and `/dev/ptmx` properly mounted.
|
|
|
|
**Consequences:** Claude Code "works" but looks broken -- no colors, no interactive prompts. Subprocess execution may hang.
|
|
|
|
**Prevention:** Ensure these are available:
|
|
|
|
```bash
|
|
--dev /dev \ # provides basic TTY
|
|
--dev-bind /dev/pts /dev/pts \ # PTY allocation
|
|
```
|
|
|
|
Verify `TERM` is in the env allowlist. Verify the stdin/stdout/stderr fds are inherited (they are by default in bwrap, no special flag needed).
|
|
|
|
**Detection:** Run `tput colors` and `tty` inside the sandbox. Check that Claude Code shows colored output.
|
|
|
|
**Phase:** Phase 1. Claude Code is a terminal app.
|
|
|
|
---
|
|
|
|
### Pitfall 9: XDG and Cache Directories Missing
|
|
|
|
**What goes wrong:** Tools inside the sandbox expect `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, and `XDG_RUNTIME_DIR` to exist and be writable. Node.js writes to `~/.npm`, `~/.node_repl_history`. Nix wants `~/.cache/nix`. Without writable cache dirs, tools fail with `EACCES` or `ENOENT` or silently degrade.
|
|
|
|
**Why it happens:** The allowlist env model correctly strips these vars, but the underlying directories also need to exist and be writable. `XDG_RUNTIME_DIR` is particularly tricky -- it is per-user, often `/run/user/1000`, and some tools refuse to work without it.
|
|
|
|
**Consequences:** `nix shell` may fail to cache anything. Node.js writes warnings to stderr on every invocation. Tools that need a config dir crash on first run.
|
|
|
|
**Prevention:**
|
|
|
|
```bash
|
|
# Map a persistent cache dir
|
|
--bind "$HOME/.claudebox/cache" "$HOME/.cache" \
|
|
--bind "$HOME/.claudebox/local" "$HOME/.local" \
|
|
# Create a tmpfs for XDG_RUNTIME_DIR
|
|
--tmpfs /run/user/$(id -u) \
|
|
# Set the env vars
|
|
XDG_CACHE_HOME="$HOME/.cache"
|
|
XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
|
```
|
|
|
|
Create `$HOME/.claudebox/cache` and `$HOME/.claudebox/local` directories in the wrapper script before launching bwrap.
|
|
|
|
**Detection:** Run `echo $XDG_RUNTIME_DIR` and `ls -la ~/.cache` inside the sandbox.
|
|
|
|
**Phase:** Phase 1. Required for Nix and Node.js operation.
|
|
|
|
---
|
|
|
|
### Pitfall 10: Symlink Resolution Across Mount Boundaries
|
|
|
|
**What goes wrong:** On NixOS, many paths under `/etc` and `/usr` are symlinks into the Nix store. Bind-mounting the symlink does not follow it -- you get the symlink, but the target is only accessible if `/nix/store` is also mounted. People bind-mount `/etc/resolv.conf` not realizing it is a symlink to `/run/systemd/resolve/stub-resolv.conf` or a Nix store path.
|
|
|
|
**Why it happens:** NixOS is symlink-heavy by design. The entire `/etc` is largely managed through symlinks. Bwrap `--ro-bind` mounts the file/symlink literally, so the target must be reachable.
|
|
|
|
**Consequences:** Bind mounts silently succeed but the file is empty or inaccessible. DNS breaks, SSL certs are missing, etc.
|
|
|
|
**Prevention:** In the wrapper script, resolve symlinks before mounting:
|
|
|
|
```bash
|
|
resolve_path() {
|
|
readlink -f "$1"
|
|
}
|
|
--ro-bind "$(resolve_path /etc/resolv.conf)" /etc/resolv.conf \
|
|
```
|
|
|
|
Or mount entire directory trees that are known to be symlink farms (`/etc/ssl`, `/etc/static`).
|
|
|
|
Since `/nix/store` is already mounted read-only, most NixOS symlinks will resolve correctly. The issue is paths like `/run/systemd/resolve/` which are outside the store.
|
|
|
|
**Detection:** Run `cat /etc/resolv.conf` and `ls -la /etc/ssl/certs/ca-certificates.crt` inside the sandbox.
|
|
|
|
**Phase:** Phase 1. NixOS-specific but critical.
|
|
|
|
---
|
|
|
|
### Pitfall 11: SSL/TLS Certificate Chain Missing
|
|
|
|
**What goes wrong:** HTTPS requests fail with certificate validation errors because the CA certificate bundle is not mounted. On NixOS, the cert bundle is at a Nix store path symlinked from `/etc/ssl/certs/ca-certificates.crt` and/or `/etc/pki/tls/certs/ca-bundle.crt`. Some tools also check `SSL_CERT_FILE` and `NIX_SSL_CERT_FILE` environment variables.
|
|
|
|
**Why it happens:** The env allowlist strips `SSL_CERT_FILE` and `NIX_SSL_CERT_FILE`. The filesystem mounts don't include `/etc/ssl`. Both must be present for HTTPS to work.
|
|
|
|
**Consequences:** `curl https://...` fails. `nix shell` cannot download from cache.nixos.org. Claude Code cannot reach the Anthropic API. Game over.
|
|
|
|
**Prevention:**
|
|
|
|
```bash
|
|
--ro-bind /etc/ssl /etc/ssl \
|
|
--ro-bind /etc/pki /etc/pki \ # if it exists
|
|
```
|
|
|
|
Add `NIX_SSL_CERT_FILE` and `SSL_CERT_FILE` to the env allowlist, pointing to the cert bundle path. On NixOS:
|
|
|
|
```bash
|
|
NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
|
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
|
```
|
|
|
|
**Detection:** Run `curl -v https://cache.nixos.org` inside the sandbox. Check for certificate errors.
|
|
|
|
**Phase:** Phase 1. Without this, nothing network-related works.
|
|
|
|
---
|
|
|
|
## Minor Pitfalls
|
|
|
|
### Pitfall 12: Locale Data Missing
|
|
|
|
**What goes wrong:** Passing `LANG=en_US.UTF-8` in the allowlist but not mounting the locale data. On NixOS, locale data lives in the Nix store (referenced by `LOCALE_ARCHIVE`).
|
|
|
|
**Prevention:** Add `LOCALE_ARCHIVE` to the env allowlist, keeping its original value (which points into `/nix/store`, already mounted).
|
|
|
|
**Phase:** Phase 1. Easy to include.
|
|
|
|
---
|
|
|
|
### Pitfall 13: Home Directory Confusion
|
|
|
|
**What goes wrong:** `HOME` inside the sandbox points to the real home directory path, but most of it is not mounted. Tools try to read `~/.bashrc`, `~/.profile`, `~/.config/*` and fail. Worse, if `HOME` is bind-mounted entirely, all secrets are exposed.
|
|
|
|
**Prevention:** Mount only the specific home subdirectories needed:
|
|
|
|
```bash
|
|
--tmpfs "$HOME" \ # empty home
|
|
--bind "$HOME/.claudebox" "$HOME/.claude" \ # claude config
|
|
--bind "$CWD" "$CWD" \ # working directory
|
|
# Then selectively mount safe config files
|
|
```
|
|
|
|
Set `HOME` in the env allowlist to the real path so paths are consistent.
|
|
|
|
**Phase:** Phase 1.
|
|
|
|
---
|
|
|
|
### Pitfall 14: bwrap Argument Order Matters
|
|
|
|
**What goes wrong:** bwrap processes mount arguments in order. A later `--tmpfs /foo` overwrites an earlier `--bind /bar /foo`. People declare mounts in logical groups but don't realize that order determines the final mount table. A `--tmpfs $HOME` after `--bind ... $HOME/.claude` wipes the bind mount.
|
|
|
|
**Prevention:** Order mounts from general to specific. tmpfs base directories first, then bind mounts on top:
|
|
|
|
```bash
|
|
--tmpfs "$HOME" \ # 1. empty home base
|
|
--bind ... "$HOME/.claude" \ # 2. specific dirs on top
|
|
```
|
|
|
|
**Detection:** `mount` or `cat /proc/mounts` inside the sandbox to verify the mount table.
|
|
|
|
**Phase:** Phase 1.
|
|
|
|
---
|
|
|
|
### Pitfall 15: Hardcoded Paths in Nix-Built Binaries
|
|
|
|
**What goes wrong:** Nix-built binaries have hardcoded RUNPATH and interpreter paths pointing into `/nix/store`. This is fine as long as `/nix/store` is mounted. But if you try to use non-Nix binaries or if you accidentally mount a partial `/nix/store`, binaries fail with "no such file or directory" (the dynamic linker is missing).
|
|
|
|
**Prevention:** Always mount ALL of `/nix/store` read-only, not just specific paths:
|
|
|
|
```bash
|
|
--ro-bind /nix/store /nix/store # the whole thing, not individual derivations
|
|
```
|
|
|
|
**Phase:** Phase 1.
|
|
|
|
---
|
|
|
|
## Phase-Specific Warnings
|
|
|
|
| Phase Topic | Likely Pitfall | Mitigation |
|
|
|-------------|---------------|------------|
|
|
| Basic bwrap invocation | Env leak (#1), /dev (#2), DNS (#4), SSL (#11) | Test with `env`, `curl https://`, `node -e` inside sandbox |
|
|
| Nix/comma integration | Daemon socket (#3), /tmp (#5), symlinks (#10) | Test `nix shell nixpkgs#hello -c hello` and `, cowsay` |
|
|
| Git operations | Git config/ownership (#6) | Test `git log`, `git diff`, `git commit` in a real repo |
|
|
| Interactive use | TTY (#8), XDG dirs (#9) | Test full Claude Code session, check colors and prompts |
|
|
| Pre-launch env audit | Env allowlist completeness (#1) | Print env before and after, diff against allowlist |
|
|
| Hardening | /proc isolation (#7) | Add `--unshare-pid` after basic functionality works |
|
|
|
|
## The Meta-Pitfall: Testing Only the Happy Path
|
|
|
|
The single biggest risk with sandbox wrappers is declaring success after `echo hello` works inside bwrap. Every pitfall above manifests only under real workloads. The test plan must include:
|
|
|
|
1. `env` -- verify env is clean
|
|
2. `curl https://api.anthropic.com` -- verify DNS + SSL
|
|
3. `nix shell nixpkgs#hello -c hello` -- verify nix-daemon
|
|
4. `git log && git diff` -- verify git works
|
|
5. `node -e "console.log('test')"` -- verify Node.js runtime
|
|
6. Actually run `claude --dangerously-skip-permissions` and have a conversation
|
|
7. Have Claude run a build command, install a tool with comma, edit a file
|
|
|
|
If all seven pass, the sandbox is solid.
|
|
|
|
## Sources
|
|
|
|
- bubblewrap documentation and man page (training data, MEDIUM confidence)
|
|
- NixOS filesystem layout knowledge (training data, HIGH confidence -- well-established, unlikely to have changed)
|
|
- Git safe.directory behavior from Git 2.35.2+ (training data, HIGH confidence)
|
|
- Node.js /dev requirements (training data, MEDIUM confidence)
|
|
- General Linux namespace/mount/sandbox knowledge (training data, HIGH confidence)
|
|
|
|
Note: Web search and fetch tools were unavailable during this research. All findings are from training data. The bwrap-specific mount behaviors and NixOS symlink patterns are well-established and unlikely to have changed, but the `--clearenv` flag availability should be verified against the current nixpkgs bwrap version.
|