Security heuristics silently discard permissions.allow Bash rules — no warning, rule never appears in /permissions
Summary
Certain permissions.allow Bash rules are discarded at load. They remain in settings.local.json, never appear under /permissions → Allow, and are never honored at execution time.
There is no warning, no log line, and no UI indication that a rule was rejected.
Version: 2.1.233
Platform: macOS (Darwin 25.6.0)
Minimal reproduction
.claude/settings.local.json:
{
"permissions": {
"allow": [
"Bash(python3:*)",
"Bash(echo:*)",
"Bash(npm run:*)",
"Bash(npx tsc:*)",
"Bash(realpath:*)"
]
}
}
Start a fresh session (not --continue/--resume, so settings are re-read), then open /permissions → Allow.
Expected: all 5 rules listed.
Actual: 3 listed —
Bash(echo:*)
Bash(npx tsc:*)
Bash(realpath:*)
Bash(python3:*) and Bash(npm run:*) are absent.
Because all five rules use identical syntax and live in the same file, this isolates the behavior to the rule content rather than a syntax or file-loading problem.
Second, independent signal
In the same session, the per-call approval annotation distinguishes the two paths directly:
⏺ Bash(realpath)
⎿ /path/to/cwd <- no annotation: an allow rule matched
⏺ Bash(python3 -c "print(1)")
⎿ 1
⎿ Allowed by auto mode classifier <- no rule matched; the classifier approved it
With auto mode disabled, that second path prompts instead.
This annotation is a reliable per-command discriminator: a silent ⎿ means a permission rule matched, while Allowed by auto mode classifier means none did.
Impact
Observed in a repo whose entire allowlist lives in settings.local.json. Rules that appear configured are silently inert, so agent and subagent runs prompt for commands the user believes are already approved.
Why this is still a bug if the stripping is intentional
Refusing to blanket-allow patterns like python3:* is defensible on its own terms. The problem is that it happens silently:
- Claude Code itself writes these rules when the user selects "Yes, and don't ask again" on such a command.
- The rule is then discarded on the next load with no feedback.
- Users end up believing they have an allowlist that does not exist.
At minimum, a rejected rule should emit a warning at load and be visibly marked in /permissions. The CLI already surfaces validator warnings for other rule problems, e.g.:
Permission deny rule (.claude/settings.json): Write(<path>) is not matched by file
permission checks — only Edit(path) rules are. Use Edit(<path>) instead.
so the reporting channel already exists.
Related issues
- #34106 — "Security heuristics override explicit permission allowlist". Same mechanism, independent repros (
stat,python3). Closed as inactive and locked, not resolved; its closing message invites a new issue. - #18160 — open, same general class.
- #48762 — auto-closed as duplicate of #34106.
- #20449, #58652 — related allowlist-not-honored reports.
Filing fresh per the guidance on #34106 rather than commenting on a locked thread. The minimal repro and the classifier-annotation signal above are the parts I did not find in the earlier reports.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗