sandbox.filesystem.denyWrite/denyRead silently ignore relative paths; only absolute paths work
Summary
When configuring sandbox.filesystem.denyWrite and denyRead in settings.json, relative path patterns are silently ignored — no bind-mount is created, writes and reads still succeed, and no warning is emitted. Only absolute paths take effect.
Repro
Environment: Linux x86_64 (aarch64 wrapper), Claude Code 2.1.114 (Nix-built precompiled binary).
Works — absolute path creates a /dev/null devtmpfs bind-mount, same mechanism as the hardcoded shadows for .bashrc / .zshrc / .gitconfig / etc.:
{
"sandbox": {
"filesystem": {
"denyWrite": ["/home/user/project/tmp", "/home/user/project/tmp/**"],
"denyRead": ["/home/user/project/tmp", "/home/user/project/tmp/**"]
}
}
}
After relaunch:
mount | grep project/tmp→devtmpfs on /home/user/project/tmp type devtmpfs (ro,nosuid,nodev)ls -la tmp→crw-rw-rw- ... 1, 3 tmp(char device =/dev/null)mkdir tmp→File existstouch tmp/x→Not a directorycat tmp/x→Not a directory
Does nothing — every relative variant I tried:
{
"sandbox": {
"filesystem": {
"denyWrite": ["./tmp", "./tmp/**", "tmp", "tmp/**", "**/tmp", "**/tmp/**"],
"denyRead": ["./tmp", "./tmp/**", "tmp", "tmp/**", "**/tmp", "**/tmp/**"]
}
}
}
After relaunch:
- No bind-mount created (
mount | grep MaxCan-Code/tmp→ empty) mkdir tmp && touch tmp/foo→ succeedscat tmp/foo→ succeeds- No warning logged anywhere I could find
Expected behavior
At least one of:
- (Preferred) Accept relative paths, resolved against the project root — so the same rule works across projects, mirroring how the hardcoded shadow list (
.bashrc,.zshrc, …) is project-relative. - Emit a visible warning at startup when a
denyWrite/denyReadentry can't be applied, so users aren't silently unprotected. - Document that absolute paths are required.
Why it matters
Users who try relative paths (reasonable, since the built-in shadow list is project-relative) silently get no enforcement and assume the feature is working. And a user wanting a path like ./tmp/ shadowed in every project has to hardcode the home path per-project rather than writing it once in ~/.claude/settings.json.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗