[Bug] Sandbox re-expands permissions.deny globs on every Bash call causing 1.6s/glob overhead
Bug Description
Title:
Sandbox re-expands permissions.deny globs on every Bash call: 1.6s per glob (24s overhead on a trivial command)
Body:
Claude Code 2.1.223 | WSL2 (Linux 6.18.33.2-microsoft-standard-WSL2) | bubblewrap 0.9.0
Workspace: 278,775 files
SUMMARY
Each "**/"-rooted Read(...) glob in permissions.deny adds ~1.6s of sandbox
setup to EVERY Bash tool call. With 15 such globs, echo hi took 24.1s of
overhead on 0.03s of actual work. Removing the globs: 0.55s. A 44x penalty
from a config most users would consider modest.
REPRO
- Set 15 Read(/...) globs in permissions.deny (e.g. /.env, **/id_rsa*,
**/.key, */credentials.json).
- Run a trivial Bash command that prints time.time() before and after its
own body.
- Compare the in-shell start timestamp against the tool call's start.
The gap is pre-execution overhead.
MEASURED (one variable at a time, config restored between each)
deny-list shape overhead
0 globs 0.53s
1 anchored literal path 0.53s <- free, no walk
1 tree-rooted Read(**/.env) 1.9s
15 tree-rooted (realistic config) 24.1s
40 tree-rooted 60.2s
Linear at ~1.6s per tree-rooted glob. A-B-A confirmed: 24.0 -> 60.2 -> 24.2.
MECHANISM
The globs are not passed to the sandbox as patterns. On every Bash call they
are expanded against the filesystem and each match is bind-mounted. Evidence:
- The live sandbox policy lists resolved paths (".../project-api/.env.local"),
not "**/.env.local".
- Deleting a matched file from inside the sandbox fails with "Device or
resource busy" - the bind mount is live.
So each "**/" glob is a full recursive tree walk, per glob, per command. Cost
scales with glob count x tree size.
WHY IT'S BAD
The expansion appears to be recomputed per command rather than cached. The
deny list changes rarely; the tree changes rarely. The result is that a
security config the docs encourage silently makes the tool unusable on a
large workspace, with no diagnostic surfacing the cause. It took a day of
bisecting to find.
Anchored rules (Read(project-api/.env.local)) are free, but that is not
documented, and it trades away the coverage that makes "**/" worth using.
SUGGESTED FIX
Cache the expansion per session, invalidating on settings change; or pass the
patterns to the sandbox and match at access time instead of pre-expanding; or
at minimum warn when deny-glob expansion exceeds some threshold.
Environment Info
- Platform: linux
- Terminal: wsl-Ubuntu
- Version: 2.1.223
- Feedback ID: ce3c9313-b3d0-4ab8-9dcf-fe7fcd522592
Errors
[]This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗