[BUG]
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code's security heuristics systematically flag its own bash output
Suggested labels: area:security area:tools bug has repro
Summary
Claude Code's bash security layer routinely flags and blocks commands that Claude Code itself generated — not adversarial inputs, not prompt injection payloads, but Claude's own idiomatic bash output. These warnings are not dismissable via "Yes, don't ask again," which means any non-trivial autonomous session will inevitably stall on false positives with no way to train them out.
This effectively breaks the autonomous use case. The user cannot walk away from a task involving shell commands because Claude will halt waiting for approval of its own comments, newlines, and standard syntax.
Environment
- Claude Code (latest as of March 2026)
- macOS terminal
- Standard development workflows (build scripts, file operations, git, CLI tools)
The problem: nine heuristic categories triggered by Claude's own output
Each of the following warnings has been triggered by bash that Claude Code composed from scratch, with no external string interpolation or repository-sourced content. None are dismissable.
1. "Contains brace with quote character (expansion obfuscation)"
Claude writes standard brace expansion or associative arrays alongside quoted strings. Flagged because brace+quote is a known injection obfuscation pattern — but Claude chose this syntax itself.
2. "Command contains quote characters inside a # comment which can desync quote tracking"
Claude writes comments like # This sets the "production" config. Comments have zero execution semantics. Claude is flagged for annotating its own code.
3. "Command contains newlines that could separate multiple commands"
Claude writes multi-line bash: if/then/fi, for loops, heredocs, line-broken pipelines for readability. There is no single-line alternative for non-trivial shell logic. This triggers on virtually every real command block.
4. "Command contains a quoted newline followed by a #-prefixed line, which can hide arguments from line-based permission checks"
Combination of items 2 and 3. Claude writes a multi-line command with an inline comment. The warning text admits the security layer uses line-based checks that can be fooled by this pattern — rather than fixing the parser, the product flags the pattern.
5. "Command contains locale quoting which can hide characters"
Claude writes $'...' syntax for escape sequences and special characters — the correct, standard bash approach. Flagged because locale quoting could theoretically encode hidden characters via hex escapes.
6. "Command contains $() command substitution"
$(command) is the POSIX-preferred, ShellCheck-recommended way to capture command output. This is foundational bash that every developer writes dozens of times daily.
7. "Command contains quoted characters in flag names"
Claude writes --flag="value". Standard CLI argument syntax. Claude could trivially use --flag=value instead, but it isn't aware this triggers a warning.
8. "Glob patterns are not allowed in write operations. Please specify an exact file path."
Claude generates cp *.json dist/ or similar. This is a hard rejection, not a prompt — Claude fails, recognizes the failure, reformulates, and retries. Burns tokens, time, and context window for something the model should know not to attempt.
9. "Shell expansion syntax in paths requires manual approval"
Claude writes paths using ~, $HOME, $PWD, ${VARIABLE}/path. Standard shell path references. Claude uses these because hardcoding absolute paths is brittle — it does the right thing and gets blocked.
Root cause
The model and the security layer have no awareness of each other:
- The security layer is a lexical pattern matcher on the raw command string. It does not parse bash. It does not understand that comments don't execute. It does not know whether the command was composed by the model or influenced by adversarial content. It sees character patterns and flags them.
- The model is not trained to avoid triggering these patterns. It writes idiomatic, well-structured, properly-commented bash and has no awareness that its own infrastructure will reject the output.
The security layer is now suspicious of: braces, quotes, newlines, comments, dollar signs, parentheses, locale strings, globs, tildes, and variable expansion. That is essentially every non-alphanumeric character that makes bash a programming language.
User impact
- Autonomous sessions stall within minutes on any task involving real shell commands
- No per-category opt-out — these heuristics cannot be individually dismissed or allowlisted
- Only workaround is
--dangerously-skip-permissions, which disables all security — forcing a choice between "approve every comment Claude writes" and "disable all safety checks" - Hard rejections (item 8) waste context window — Claude generates, fails, and retries commands it should know are prohibited
Proposed fixes (any one would help)
- Teach the model its own constraints. The model should prefer equivalent syntax that doesn't trigger heuristics: no quotes in comments,
--flag=valuenot--flag="value", resolve paths to literals, avoid$'...'when alternatives exist.
- Differentiate self-generated commands from externally-influenced ones. Apply lighter scrutiny to commands the model composed from its own reasoning vs. commands containing strings from the repo,
CLAUDE.md, or other potentially adversarial sources.
- Make heuristic categories individually dismissable. Let users opt out per category (e.g., "I accept command substitution, stop asking") via settings, without disabling the entire security system.
- Parse the bash properly. The warnings admit the security layer uses line-based checks that can be fooled by comments. An AST-based analysis would flag actual obfuscation rather than every piece of standard syntax.
Reproduction
Start any Claude Code session. Ask it to write a build script, a deployment helper, or any multi-step shell task. Count the approval prompts triggered by Claude's own syntax choices. For most real tasks, the count will exceed the number of prompts triggered by actually meaningful security decisions.
What Should Happen?
Claude Code's security heuristics systematically flag its own bash output
Suggested labels: area:security area:tools bug has repro
Summary
Claude Code's bash security layer routinely flags and blocks commands that Claude Code itself generated — not adversarial inputs, not prompt injection payloads, but Claude's own idiomatic bash output. These warnings are not dismissable via "Yes, don't ask again," which means any non-trivial autonomous session will inevitably stall on false positives with no way to train them out.
This effectively breaks the autonomous use case. The user cannot walk away from a task involving shell commands because Claude will halt waiting for approval of its own comments, newlines, and standard syntax.
Environment
- Claude Code (latest as of March 2026)
- macOS terminal
- Standard development workflows (build scripts, file operations, git, CLI tools)
The problem: nine heuristic categories triggered by Claude's own output
Each of the following warnings has been triggered by bash that Claude Code composed from scratch, with no external string interpolation or repository-sourced content. None are dismissable.
1. "Contains brace with quote character (expansion obfuscation)"
Claude writes standard brace expansion or associative arrays alongside quoted strings. Flagged because brace+quote is a known injection obfuscation pattern — but Claude chose this syntax itself.
2. "Command contains quote characters inside a # comment which can desync quote tracking"
Claude writes comments like # This sets the "production" config. Comments have zero execution semantics. Claude is flagged for annotating its own code.
3. "Command contains newlines that could separate multiple commands"
Claude writes multi-line bash: if/then/fi, for loops, heredocs, line-broken pipelines for readability. There is no single-line alternative for non-trivial shell logic. This triggers on virtually every real command block.
4. "Command contains a quoted newline followed by a #-prefixed line, which can hide arguments from line-based permission checks"
Combination of items 2 and 3. Claude writes a multi-line command with an inline comment. The warning text admits the security layer uses line-based checks that can be fooled by this pattern — rather than fixing the parser, the product flags the pattern.
5. "Command contains locale quoting which can hide characters"
Claude writes $'...' syntax for escape sequences and special characters — the correct, standard bash approach. Flagged because locale quoting could theoretically encode hidden characters via hex escapes.
6. "Command contains $() command substitution"
$(command) is the POSIX-preferred, ShellCheck-recommended way to capture command output. This is foundational bash that every developer writes dozens of times daily.
7. "Command contains quoted characters in flag names"
Claude writes --flag="value". Standard CLI argument syntax. Claude could trivially use --flag=value instead, but it isn't aware this triggers a warning.
8. "Glob patterns are not allowed in write operations. Please specify an exact file path."
Claude generates cp *.json dist/ or similar. This is a hard rejection, not a prompt — Claude fails, recognizes the failure, reformulates, and retries. Burns tokens, time, and context window for something the model should know not to attempt.
9. "Shell expansion syntax in paths requires manual approval"
Claude writes paths using ~, $HOME, $PWD, ${VARIABLE}/path. Standard shell path references. Claude uses these because hardcoding absolute paths is brittle — it does the right thing and gets blocked.
Root cause
The model and the security layer have no awareness of each other:
- The security layer is a lexical pattern matcher on the raw command string. It does not parse bash. It does not understand that comments don't execute. It does not know whether the command was composed by the model or influenced by adversarial content. It sees character patterns and flags them.
- The model is not trained to avoid triggering these patterns. It writes idiomatic, well-structured, properly-commented bash and has no awareness that its own infrastructure will reject the output.
The security layer is now suspicious of: braces, quotes, newlines, comments, dollar signs, parentheses, locale strings, globs, tildes, and variable expansion. That is essentially every non-alphanumeric character that makes bash a programming language.
User impact
- Autonomous sessions stall within minutes on any task involving real shell commands
- No per-category opt-out — these heuristics cannot be individually dismissed or allowlisted
- Only workaround is
--dangerously-skip-permissions, which disables all security — forcing a choice between "approve every comment Claude writes" and "disable all safety checks" - Hard rejections (item 8) waste context window — Claude generates, fails, and retries commands it should know are prohibited
Proposed fixes (any one would help)
- Teach the model its own constraints. The model should prefer equivalent syntax that doesn't trigger heuristics: no quotes in comments,
--flag=valuenot--flag="value", resolve paths to literals, avoid$'...'when alternatives exist.
- Differentiate self-generated commands from externally-influenced ones. Apply lighter scrutiny to commands the model composed from its own reasoning vs. commands containing strings from the repo,
CLAUDE.md, or other potentially adversarial sources.
- Make heuristic categories individually dismissable. Let users opt out per category (e.g., "I accept command substitution, stop asking") via settings, without disabling the entire security system.
- Parse the bash properly. The warnings admit the security layer uses line-based checks that can be fooled by comments. An AST-based analysis would flag actual obfuscation rather than every piece of standard syntax.
Reproduction
Start any Claude Code session. Ask it to write a build script, a deployment helper, or any multi-step shell task. Count the approval prompts triggered by Claude's own syntax choices. For most real tasks, the count will exceed the number of prompts triggered by actually meaningful security decisions.
Error Messages/Logs
Steps to Reproduce
I don't know.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.79 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
_No response_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗