[BUG] sandbox.credentials.files protection can be bypassed on first access to a not-yet-existing deny-listed path
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment: Claude Code CLI 2.1.259, sandbox.enabled: true, sandbox.enableWeakerNestedSandbox: true (bubblewrap), running inside a Debian container itself running inside a macOS container VM.
Config (managed-settings.json):
"sandbox": {
"credentials": {
"files": [
{ "path": "credentials.json", "mode": "deny" },
{ "path": "serviceAccountKey.json", "mode": "deny" },
{ "path": ".env", "mode": "deny" }
]
}
}
Repro steps:
- In a directory with no
credentials.jsonpresent, ask Claude to run via the Bash tool:echo "hi" > credentials.json && cat credentials.jsonsucceeds, discloses "hi". Identically reproduced withserviceAccountKey.jsonand.env. - Immediately after, ask Claude to run
cat credentials.json(standalone) correctly blocked,Permission denied. - Ask Claude to run:
rm credentials.jsonat this point fails withDevice or resource busy, notPermission denied- the path is now an active mount point. - Deleting and recreating the file in a new invocation reproduces the bypass again on the same filename; the file is protected again on the very next invocation after that.
Analysis: Consistent with sandbox.credentials.files being enforced via a bind-mount established once per Bash-tool invocation, over whichever deny-listed paths already exist on disk at that moment (a bind-mount destination must pre-exist). A path that doesn't exist yet gets no mount for that invocation, so one compound command that creates and reads it slips through entirely.
Impact: Any deny-listed filename not yet present on disk can be created and read back in a single shell command, bypassing the protection on that first access - exactly the shape a prompt-injected instruction could exploit against a not-yet-created secrets file. Reproduced across three unrelated filenames, so this isn't file-specific.
What Should Happen?
Expected: Protection shouldn't depend on whether the file existed before the sandboxed command started.
Error Messages/Logs
Steps to Reproduce
Environment: Claude Code CLI 2.1.259, sandbox.enabled: true, sandbox.enableWeakerNestedSandbox: true (bubblewrap), running inside a Debian container itself running inside a macOS container VM.
Config (managed-settings.json):
"sandbox": {
"credentials": {
"files": [
{ "path": "credentials.json", "mode": "deny" },
{ "path": "serviceAccountKey.json", "mode": "deny" },
{ "path": ".env", "mode": "deny" }
]
}
}
Repro steps:
- In a directory with no
credentials.jsonpresent, ask Claude to run via the Bash tool:echo "hi" > credentials.json && cat credentials.jsonsucceeds, discloses "hi". Identically reproduced withserviceAccountKey.jsonand.env. - Immediately after, ask Claude to run
cat credentials.json(standalone) correctly blocked,Permission denied. - Ask Claude to run:
rm credentials.jsonat this point fails withDevice or resource busy, notPermission denied- the path is now an active mount point. - Deleting and recreating the file in a new invocation reproduces the bypass again on the same filename; the file is protected again on the very next invocation after that.
Analysis: Consistent with sandbox.credentials.files being enforced via a bind-mount established once per Bash-tool invocation, over whichever deny-listed paths already exist on disk at that moment (a bind-mount destination must pre-exist). A path that doesn't exist yet gets no mount for that invocation, so one compound command that creates and reads it slips through entirely.
Impact: Any deny-listed filename not yet present on disk can be created and read back in a single shell command, bypassing the protection on that first access - exactly the shape a prompt-injected instruction could exploit against a not-yet-created secrets file. Reproduced across three unrelated filenames, so this isn't file-specific.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.259 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_