Skip to content

Bash bypasses the denylist entirely — a caller can read any denied path #419

Description

@KenTaniguchi-R

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 < ~/.zshrc121. 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:

A: no hook   → git log ran, correct output
B: log-only hook → git log ran, correct output

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.
return hit ? { 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 nocat, wc, grep, git, anything

So 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:

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.

  1. 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.
  2. Deny at the sink instead (Enforce at the sink, not only at the read: provenance backstop on the reply #399). A provenance check cannot see what a shell command read either, so this needs Bash usage to force a refusal — coarse, but sound.
  3. 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions