Bash permissions.deny rules not enforced in subagent (Agent/Task tool) shells — denied command executes, only post-hoc security warning
Summary
A permissions.deny Bash rule that is correctly enforced in the main conversation loop is not enforced inside a subagent spawned via the Agent (Task) tool. The subagent executed the denied command successfully; the only trace was a post-hoc SECURITY WARNING in the parent's task-notification saying the command "matches the user's configured deny rule … this command should not have executed".
Environment
- Claude Code 2.1.214 (CLI), macOS (darwin 25.5.0)
- Project
.claude/settings.json:
"permissions": {
"deny": [
"Bash(git stash)",
"Bash(git stash:*)",
"Bash(rtk git stash)",
"Bash(rtk git stash:*)"
]
}
What happened
- Main loop: a Bash call containing
rtk git stash listwas denied as expected ("Permission to use Bash with command … has been denied."). Enforcement works there — including the compound-command case. - A background subagent (Agent tool,
subagent_type: general-purpose, modelsonnet) working in the same project ran:
````
git stash push -u -m "wip: issue-1505 checked-op fix + tests"
The command executed (the stash was created and later consumed; work was recoverable, no data loss in this instance).
- The parent session's task-completion notification carried:
> SECURITY WARNING: This subagent performed actions that may violate security policy. Reason: [User Deny Rules] The subagent ran git stash push -u -m "wip: …", which matches the user's configured deny rule Bash(git stash:*) — this command should not have executed…
So a detection layer recognizes the violation post-hoc, but the PreToolUse permission gate that blocks the command in the main loop did not fire (or did not deny) in the subagent context.
Expected
Deny rules are a security boundary; they should bind every Bash execution in the session, including subagent (Task/Agent) contexts — same as the main loop. Post-hoc warning is detection, not prevention: for destructive commands (git reset --hard, rm, …) the damage is done by the time the parent is told.
Repro sketch
- Add
"Bash(git stash:*)"topermissions.denyin project.claude/settings.json. - Confirm the main loop refuses
git stash list. - Spawn a Task/Agent subagent whose work plausibly leads it to stash (e.g. "commit your work; keep the tree clean"), or that is directly told to run
git stash push. - Observe the subagent executes it; the parent later receives the SECURITY WARNING in the task notification.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗