Feature: deny rules should evaluate each segment of compound bash commands

Resolved 💬 5 comments Opened Mar 23, 2026 by Pro777 Closed Apr 26, 2026

Summary

\permissions.deny\ rules don't fire when a denied command appears as a non-first segment of a compound bash command. This is a usability/trust-consistency issue: a user who writes a deny rule reasonably expects it to fire regardless of how the command is assembled.

The threat model here is not adversarial input. It's Claude itself, operating normally.

Current behaviour

Given this deny rule:

\\\json
"permissions": {
"deny": ["Bash(git clean:*)"]
}
\
\\

| Command | Result |
|---|---|
| \git clean -fd\ | ✅ Blocked |
| \git fetch && git pull && git clean -fd\ | ❌ Passes through |
| \npm run build; git clean -fd\ | ❌ Passes through |

Claude chains commands with \&&\ and \;\ constantly during normal operation. \git fetch && git pull && git clean -fd\ is a completely routine agent action — not an attack, not an edge case. The deny rule the user wrote to protect their worktree simply doesn't fire.

Why this matters

The deny rules are the primary mechanism most Claude Code operators and users rely on to bound Claude's actions — "never push to main", "never clean the worktree", "never drop the database", etc. Most deployments don't have OS-level sandboxing; the deny rules are the boundary.

When a user writes \deny: Bash(git clean:*)\, they believe that's enforced. If it only fires when \git clean\ is the first token of the full string, the rule silently fails on the most common real-world pattern: chained commands that Claude itself generates during normal agentic operation.

No adversary required. The agent wipes your worktree doing its job.

This is a trust-consistency gap: the rules appear to mean one thing but enforce a narrower version of it.

Requested behaviour

Each segment of a compound command (\&&\, \||\, \;\, \|\) should be evaluated independently against deny rules. If any segment matches a deny rule, the entire command is blocked.

Reference implementation

PR #36645 implements this as a \PreToolUse\ hook plugin (\bash-guard\) that can be used today as a workaround.

Related

  • #36637 — original bug report
  • #31523 — allow-side of the same root cause

Acknowledgement

I understand from a prior response that deny rules are not designed as a hard security barrier — that's fine. This request is about making the rules' actual behaviour match what the configuration UI implies, for the normal case of an agent chaining commands during routine work.

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗