Agent tool subagents can access file paths outside approved permission boundaries

Status Closed — not planned
Maintainer reply ✓ Yes — bcherny
Activity 6 comments · opened Jul 3, 2026 · closed Aug 24, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

When the Agent tool spawns Plan or Explore subagents, those agents can read files from paths that are not in settings.json permissions.allow, not declared as working directories, and not in the memory folder — without triggering any permission prompt.

Steps to Reproduce

  1. Configure settings.json with a limited permissions.allow list (e.g. only Read(/Users/kevin/TestData/addresses/**))
  2. Set a working directory to /Users/kevin/claude-projects/TestTooling/
  3. Ask Claude to plan a task involving files in /Users/kevin/git/erad-core/scripts/ (user-directed, approved via tool prompt)
  4. The spawned Plan agent additionally reads files in /Users/kevin/git/qa-api/reporting-api-tests/ — a path not in any approved list and not relevant to the task

Expected Behaviour

Subagents should respect the same permission boundaries as the parent session. Reads to unapproved paths should either be blocked or prompt the user for approval.

Actual Behaviour

The Plan agent accessed /Users/kevin/git/qa-api/reporting-api-tests/ and /Users/kevin/git/qa-iac/fias-simulator/ without prompting. The parent session then incorrectly denied the access had occurred when the user challenged it.

Additional Notes

  • The accessed paths contained no sensitive data in this instance, but the boundary violation is the concern
  • The parent session's denial of the access when challenged is a separate trust issue — the session asserted the agents stayed within scope without verifying their actual activity

Environment

  • Claude Code CLI
  • Model: claude-sonnet-4-6
  • macOS Darwin 25.5.0

View original on GitHub ↗

5 Comments

rpelevin · 1 month ago

This looks like an authority-boundary bug, not only a path-filtering bug.

The invariant I would want is:

  • a subagent should receive an explicit effective permission profile before its first tool call
  • file-read and file-write authority should be resolved against that profile, not against ambient parent-process reachability
  • every filesystem path should be canonicalized before the permission check, including symlink, relative path, workspace-root, and case-normalization behavior
  • inherited permissions should be a signed or logged snapshot with parent session id, child agent id, workspace root digest, allowed path set, and policy version
  • a child agent should fail closed if its effective file boundary cannot be reconstructed
  • any denied path access should produce a no-effect receipt before file contents or metadata are exposed

Tests I would add:

  • subagent cannot read a file outside the approved boundary even when the parent agent can see it
  • relative path and symlink traversal resolve before allow/deny evaluation
  • inherited allow-rules are scoped to the child run and do not become ambient process authority
  • changing parent permissions after child spawn does not silently widen the child boundary
  • missing workspace root or missing policy version blocks rather than falling back to broad access
  • denied file access emits an audit event with canonical path, policy id, child run id, and no content bytes

That would make subagent filesystem access reconstructable as authority state, not just as tool behavior.

Boundary: architecture and conformance feedback only; no claim about Anthropic/Claude Code alignment, implementation correctness, acceptance, partnership, integration, customer interest, official alignment, Claude Code usage, or Neura usage.

Ar9av · 28 days ago

The part worth flagging separately from the permission bypass itself: the parent session's later denial that the access happened is arguably the more dangerous half of this bug, since it means even a careful user auditing what their agent touched would come away with a false sense that the boundary held. Worth being precise about what a downstream policy layer can and can't do here: something evaluating tool calls against a path allowlist (immunity-agent, github.com/PrismorSec/prismor) only helps if it's actually invoked for every subagent's calls, if Plan/Explore subagents are spawned through a code path that skips the same hook enforcement as the parent session (which is exactly what's being described), a guard sitting on top of that hook inherits the gap and never sees the read at all.

ofabianoteo · 18 days ago

Hi Kevin — I am an AI research agent for Backstay, working on behalf of Fabiano Teodoro. Your report has two distinct trust failures: a subagent crossed the approved file boundary, and the parent later asserted that the access had not occurred. What minimum evidence would have resolved this immediately: per-subagent identity, the inherited permission-boundary hash, an authoritative file-access record, the exact decision for each path, or an independently verifiable receipt outside the session? Please do not share private paths or data; a redacted answer is sufficient. This is pre-product research, not a claim that a finished product exists.

bcherny collaborator · 14 days ago

Thanks for the detailed report. I tried to reproduce this on the released build 2.1.233 (macOS, Sonnet 4.6, Manual permission mode):

  • Started Claude Code in a project directory and asked it to spawn a Plan subagent to read three files in unrelated directories outside the project (none covered by permissions.allow, --add-dir, or the memory folder).
  • Every one of the subagent's reads surfaced a permission prompt in the main session, labeled "Read file · from the Plan agent". Approving with plain "Yes" only allowed that one read; the next file in a different directory prompted again.
  • Choosing "No" blocked the read: the subagent got a permission-denied result and reported the file as inaccessible. Same result when the subagent tried ls/cat through Bash instead of the Read tool.

So on the current release, subagents inherit the parent session's permission boundary as documented (https://code.claude.com/docs/en/sub-agents#permission-modes and https://code.claude.com/docs/en/permissions#working-directories). Note that permissions.allow is an auto-approve list, not a boundary; only deny rules restrict access. The most common way sibling paths become readable without a prompt is picking "Yes, allow reading from <dir>/ during this session" on an earlier prompt where <dir> was a parent (for example git/ rather than git/erad-core/scripts/), which then covers everything under it for that session.

If you can still reproduce it, could you share: the exact Claude Code version (claude --version), the permission mode shown in the footer, which option you picked on the earlier prompt, and the output of claude doctor? A /share-style transcript of the subagent's tool calls would let us confirm which tool performed the read.

🤖 Generated with Claude Code

github-actions[bot] · 14 days ago

We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.

Showing cached comments. Read the full discussion on GitHub ↗