anthropics / anthropics/claude-code
Sandboxed Bash: "Can't create file at <path>" when permissions.deny Read globs expand to symlink-spelled paths (bind destination uncreatable)
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
## Environment
- Claude Code: 2.1.263 (native CLI), Linux x64
- OS: Ubuntu 24.04 (kernel 6.8.0-139-generic), bubblewrap 0.9.0-1ubuntu0.1
- Sandboxing enabled globally (`sandbox.enabled: true` in `~/.claude/settings.json`)
- Project `permissions.deny` contains broad `Read(...)` deny globs over a
build/dependency cache tree (structure described below; exact patterns withheld
from a public issue, available on request)
## Observed
In a project whose deny globs cover a cache tree containing **symlinked
directories**, every sandboxed Bash command fails immediately at sandbox
construction and nothing executes:
Can't create file at /home///.npm-cache////external//.envrc: No such file or directory
This is fatal for the whole sandbox, so all Bash tooling in that project is
unusable: in one recorded session, 18 out of 18 distinct commands failed
identically this way.
## Trigger condition (analysis)
`permissions.deny` Read globs are expanded per-session into ~130 concrete
absolute paths. The expansion walks the real filesystem, and when it traverses
a directory symlink (in our case a Bazel workspace convenience symlink whose
target lives elsewhere on disk, e.g. `bazel/...` vs the realpath twin under
`bazel-contents-cache///...`), the produced deny paths are
**spelled through the symlink**.
The deny list therefore contains the same concrete content twice - once
spelled, once realpath-resolved - and each entry is handed to the sandbox as a
bind at the **spelled** destination. Inside the sandbox, a destination whose
path crosses an existing symlink cannot be created, and bwrap aborts with
`Can't create file at`, killing the entire namespace rather than just that one
entry.
Supporting evidence from fresh A/B sessions on the same tree:
- Before: every sandboxed command dies instantly at mount construction with
`Can't create file at ` (18/18 commands).
- After adding a project-level carve-out:
{ "sandbox": { "filesystem": {
"allowRead": [ "/home///.npm-cache/codex-host-build/bazel/**/*" ] } } }
the sandbox's existing re-allowed-deny skip logic (debug log:
`Skipping read deny for re-allowed path`) drops the spelled-path binds, mount
construction completes, and execution proceeds to the later seccomp stage
(a separate issue - different layer, unrelated root cause). Same deny list,
same tree; the only delta is the `allowRead` carve-out.
## Suggested fix
1. **Realpath-resolve bind destinations.** When constructing deny binds, resolve
each expanded deny path to its realpath and bind at the realpath destination
(that is the content the deny is meant to protect anyway). Spelled-vs-realpath
duplicates collapse naturally.
2. **Actionable fatal error.** If a bind destination still cannot be created,
fail with a message naming the originating `permissions.deny` entry (the
current error names only the concrete path, so the user has no idea which
settings glob produced it).
Note: I deliberately did *not* suggest silently skipping uncreatable deny
binds - a denied path that silently isn't masked would be a protection hole.
## Workaround used
A scoped `sandbox.filesystem.allowRead` glob in project-local settings covering
the symlink-spelled subtree, so the failing binds hit the existing
"re-allowed path" skip. Caveat: this also unmasks reads under that subtree,
which is a deliberate, owner-approved trade-off and should not be needed once
destinations are realpath-resolved.
Not attaching transcripts (they contain project source); happy to provide the
debug-log excerpts and an anonymized minimal repro on request.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
調査の方向性
Start in the sandboxed Bash setup path that expands permissions.deny Read globs and turns them into filesystem binds. Reproduce with a deny glob over a tree containing symlinked directories, or ask for the offered anonymized repro/debug logs. Done means bind destinations are realpath-resolved/deduplicated and any remaining bind failure reports the originating permissions.deny entry without silently skipping denies.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- bash, linux
- 領域
- cli, security
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 52/100