Sandbox crashes when auto-denied file paths traverse symlinks
Summary
Claude Code's sandbox (bwrap) becomes completely non-functional when auto-detected "secret" files exist behind a symlink. bwrap cannot mkdir -p through symlinks to set up deny bind mounts, so all sandboxed commands fail — not just those accessing denied paths.
Environment
- Claude Code CLI on Linux (Debian 13, kernel 6.12.74)
- Working directory: a Bazel monorepo at
/home/lucas/matx - Bazel creates a convenience symlink:
bazel-matx→/home/lucas/.cache/bazel/_bazel_lucas/.../execroot/_main
Reproduction
- Have a Bazel project (or any project with a symlink in the workspace root)
- Behind the symlink, have any file with "secret" or "credential" in the name (e.g.,
secrets.tf,secrets.py,gcredentials.h) - Run any sandboxed command — even
echo "hello"
$ echo "hello"
bwrap: Can't mkdir parents for /home/lucas/matx/bazel-matx/ops/terraform/buildbarn-db/secrets.tf: No such file or directory
Root cause
Two issues compound:
1. Auto-deny heuristic adds paths through symlinks
The sandbox auto-detects files with "secret" or "credential" in the name and adds them to the filesystem deny list. It finds these files through the bazel-matx symlink and adds the symlink-relative path (e.g., /home/lucas/matx/bazel-matx/.../secrets.tf) rather than the resolved real path.
2. bwrap can't create deny bind mounts through symlinks
bwrap tries to mkdir -p the parent directories of each deny path to set up bind mounts. It cannot create directories through a symlink, so it fails. Since this happens during sandbox setup, every sandboxed command fails, not just commands that would touch the denied files.
Additional issue: over-broad auto-deny heuristic
The auto-deny heuristic matches any file with "secret" or "credential" in the name. In practice this catches many non-sensitive files:
secrets.py— Python's stdlib module for generating cryptographically strong random numbersgcredentials.h/gunixcredentialsmessage.h— GLib/GIO system headersBUILD.gix-credentials-0.27.0.bazel— Rust crate universe build filesecrets.tf— matched through a Bazel symlink to a cached copy, not the original
In this repo, the deny list had 70+ entries, almost all false positives from Python stdlib, system headers, and Bazel runfiles/output directories containing copies of secrets.py.
Workaround
Use dangerouslyDisableSandbox: true for all commands. This defeats the purpose of sandboxing entirely.
Suggested fixes
- Resolve symlinks before adding paths to the deny list, or skip paths that traverse symlinks.
- Scope the heuristic more narrowly — e.g., only match
.env,.env.*, and files in well-known secret locations, rather than any filename containing "secret" or "credential" as a substring. - Make auto-deny configurable — allow users to see and override the auto-generated deny list.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗