You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every denied path is readable through Bash. Demonstrated, not theorised.
The demonstration
~/.zshrc is on the built-in denylist:
DENIED ~/.zshrc
Asked through the production prompt shape and the production flag pair
(--allowedTools "Read,Grep,Glob,LS,Skill" --permission-mode dontAsk):
How many lines are in ~/.zshrc? Just the number.
→ 121
Ground truth: wc -l < ~/.zshrc → 121. The agent read a denied file and reported its contents accurately.
The only tool it used was Bash.
Why it happens — two facts that compose
1. Bash is not gated by --allowedTools. It is absent from CLAUDE_READ_ONLY_TOOLS and runs anyway. Verified with and without a PreToolUse hook present — identical result both times, so this is not the hook granting it:
2. guard.ts's Bash branch records and allows by design (guard.ts:127-135):
// Record and allow: string matching is too weak to be a boundary and too// eager to be harmless. … and note this means an `exec`-granted task has NO read floor.returnhit ? {allow: true,flag: {...}} : {allow: true};
That reasoning is sound in isolation — inspecting a command string cannot bound what it reads. The problem is the conclusion drawn from it: the comment assumes Bash is only reachable when a task is exec-granted. It is reachable on every call.
What is actually bounded
surface
bounded by the denylist?
Read / Grep / Glob / LS
yes — verified, incl. root-reaches-denied-path on a $HOME scan
Bash
no — cat, wc, grep, git, anything
So the model bounds four tools and not the fifth, and the fifth can do everything the other four can.
a path outside every root, or on the denylist, is refused at the read
True for Read. False as a general statement. The same claim appears in both site guides and in docs/superpowers/specs/2026-08-07-invert-the-default-design.md.
This is the #390 defect again — a surface describing a boundary that is not there — but this time the boundary is the product's central claim, not a footnote.
Severity
~/.ssh/id_rsa and ~/.aws/credentials are reachable by the same route. I deliberately did not test those — a positive result would have put key material in a log. ~/.zshrc was chosen because it is denied, unambiguous, and harmless to count.
This is not mitigated by the model's caution. It refused some requests on judgement earlier today, and complied with this one without hesitation, because counting lines in a shell config does not look dangerous.
Options
None evaluated; ordered by how much they actually close.
Remove Bash from the spawn. If it is not gated by --allowedTools, find what does gate it (disallowedTools, permissions.deny, managed settings). This is the only option that closes the hole rather than narrowing it. The cost is real: git status, git log, running tests — the "ask my colleague's agent about their work" use case leans on exactly these.
Keep the string matching and stop calling it a boundary. Cheapest, and honest: it is what ships today. Requires correcting the README, both guides, and the spec.
Whatever is chosen, the documentation has to change today. The claim as written is false.
Related
#399/#400 (sink-side provenance) · #416 (injected context bypass — the other hole in the same model) · #390 (the pattern)
Every denied path is readable through
Bash. Demonstrated, not theorised.The demonstration
~/.zshrcis on the built-in denylist:Asked through the production prompt shape and the production flag pair
(
--allowedTools "Read,Grep,Glob,LS,Skill" --permission-mode dontAsk):Ground truth:
wc -l < ~/.zshrc→121. The agent read a denied file and reported its contents accurately.The only tool it used was
Bash.Why it happens — two facts that compose
1.
Bashis not gated by--allowedTools. It is absent fromCLAUDE_READ_ONLY_TOOLSand runs anyway. Verified with and without aPreToolUsehook present — identical result both times, so this is not the hook granting it:2.
guard.ts's Bash branch records and allows by design (guard.ts:127-135):That reasoning is sound in isolation — inspecting a command string cannot bound what it reads. The problem is the conclusion drawn from it: the comment assumes Bash is only reachable when a task is
exec-granted. It is reachable on every call.What is actually bounded
Read/Grep/Glob/LSroot-reaches-denied-pathon a$HOMEscanBashcat,wc,grep,git, anythingSo the model bounds four tools and not the fifth, and the fifth can do everything the other four can.
What this contradicts
README.md:129, shipped today in #409:True for
Read. False as a general statement. The same claim appears in both site guides and indocs/superpowers/specs/2026-08-07-invert-the-default-design.md.This is the #390 defect again — a surface describing a boundary that is not there — but this time the boundary is the product's central claim, not a footnote.
Severity
~/.ssh/id_rsaand~/.aws/credentialsare reachable by the same route. I deliberately did not test those — a positive result would have put key material in a log.~/.zshrcwas chosen because it is denied, unambiguous, and harmless to count.This is not mitigated by the model's caution. It refused some requests on judgement earlier today, and complied with this one without hesitation, because counting lines in a shell config does not look dangerous.
Options
None evaluated; ordered by how much they actually close.
Bashfrom the spawn. If it is not gated by--allowedTools, find what does gate it (disallowedTools,permissions.deny, managed settings). This is the only option that closes the hole rather than narrowing it. The cost is real:git status,git log, running tests — the "ask my colleague's agent about their work" use case leans on exactly these.Whatever is chosen, the documentation has to change today. The claim as written is false.
Related
#399/#400 (sink-side provenance) · #416 (injected context bypass — the other hole in the same model) · #390 (the pattern)