[FEATURE] Allow suppressing bash safety heuristic prompts via settings

Open 💬 17 comments Opened Mar 3, 2026 by chrispmcgee

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Claude Code's built-in bash safety heuristics trigger interactive "Do you want to proceed?" prompts on common, legitimate shell patterns — and unlike allowlist-based permission prompts, these cannot be permanently suppressed through permissions.allow rules, acceptEdits mode, or "don't ask again."

Patterns that trigger these prompts include:

  • $() command substitution (e.g., git commit -m "$(cat <<'EOF'...)" — Claude's own recommended commit format)
  • Backtick command substitution (e.g., in gh pr create bodies)
  • Newlines separating multiple commands (e.g., for loops, multi-step scripts)
  • Empty quotes before dashes ("potential bypass")
  • Quote characters inside # comments ("can desync quote tracking")
  • ANSI-C quoting ("can hide characters")
  • Compound commands with cd and output redirection

For power users running Claude Code interactively on their own machines, these prompts fire dozens of times per session on routine development commands. The prompts have no "don't ask again" option, so each one requires manual intervention — even when the command prefix is explicitly in the allowlist.

Proposed Solution

A setting to configure the behavior of individual safety heuristic categories. For example:

{
  "bashSafety": {
    "commandSubstitution": "allow",
    "newlines": "allow",
    "ansiQuoting": "allow",
    "ambiguousSyntax": "prompt"
  }
}

Or a simpler blanket setting:

{
  "bashSafety": "allow"
}

When set to "allow", the heuristic check would still run but would auto-approve instead of prompting. This preserves the detection logic (it could still log warnings) while removing the interactive friction.

This is the complement to #28993, which proposes auto-deny so Claude reformulates. Both approaches want to eliminate the interactive prompt — this one for users who trust their local shell environment and want flow, #28993 for users who want stricter automatic enforcement.

Alternative Solutions

  • --dangerously-skip-permissions: Too broad — disables all permission checks, not just heuristics. Intended for containers, not interactive use.
  • PreToolUse hooks: As noted in #28993, users can write hooks to intercept these, but this requires reimplementing detection logic that Claude Code already has. A configuration toggle would be much simpler.
  • Avoiding trigger patterns: Claude could use simpler commands (e.g., no heredocs in commits), but this sacrifices formatting quality and forces workarounds for standard shell patterns.

Priority

High - Significant impact on productivity

Feature Category

Configuration and settings

Use Case Example

  1. User has Bash(git:*) in their allowlist and acceptEdits mode enabled
  2. User asks Claude to commit changes
  3. Claude runs git add file.php && git commit -m "$(cat <<'EOF' ... EOF)"
  4. Despite the allowlist match, user gets prompted: "Command contains $() command substitution — Do you want to proceed?"
  5. User approves. Next commit, same prompt. And the next. Every single commit.
  6. With this feature, user sets "bashSafety": "allow" and commits flow without interruption

Additional Context

  • Related: #28993 (auto-deny direction of same problem)
  • The heuristics appear to be a relatively recent addition — users who configured extensive allowlists previously experienced far fewer prompts
  • The current behavior creates "ask fatigue" where users reflexively approve every prompt, undermining the security purpose

View original on GitHub ↗

17 Comments

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/28993
  2. https://github.com/anthropics/claude-code/issues/30345
  3. https://github.com/anthropics/claude-code/issues/30213

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

chrispmcgee · 4 months ago

Not a duplicate. #28993 proposes auto-deny (opposite direction — we want auto-allow). #30213 is specific to cd && git in worktrees. #30345 is the closest but is scoped to a single heuristic type and filed as a bug — this issue is a broader feature request for a setting to configure the behavior of all bash safety heuristics, encompassing #30345 and #30213 as specific cases.

mrballcb · 4 months ago

Also #27957 is related in that any tool which you configure claude to use that utilizes quoted cli commands (ie beans for me) will prompt every single change it wants to make. I too want the ability to disable or bypass enforcement of this check either globally or per permitted command. Claude has become unwieldy for me because I have to repeatedly hit Enter for every step of every agent.

connorfee-texwahoo · 4 months ago

Same issue here. allowBashCommandSubstitution: true is set in both global (~/.claude/settings.json) and project-level settings.local.json — still prompts every time. This is especially painful for git commits since Claude's own system prompt hardcodes the heredoc $() pattern.

ThinkOffApp · 4 months ago

We run Claude Code headless as a persistent service with dangerouslySkipPermissions: true and layer safety on top via hooks instead of interactive prompts. IDE Agent Kit generates this config with init --ide claude-code.

The bash safety heuristics are the main blocker for unattended operation. Heredoc commits, command substitution in git messages, and similar patterns trigger prompts that can't be suppressed through permissions.allow. For automated workflows, hook-based validation (pre-tool scripts that check the command before execution) is more flexible and doesn't require human presence.

A settings flag to suppress specific heuristic categories while keeping the hook system for custom safety rules would make headless operation much more practical.

skypher · 4 months ago

Same here. Good idea especially for casual users but we gotta have a way to disable these.

ryanscovill · 4 months ago

Worth noting that the first-party /code-review plugin hits this hard. It spawns parallel subagents that generate bash commands with natural-language # comments (e.g. # Check "make docker build-all"), triggering the quote-tracking prompt dozens of times per run with no way to suppress it — permissions.allow doesn't help and there's no "don't ask again." It makes /code-review essentially unusable in practice, which seems like a good signal for prioritizing a fix here.

RobbyMon81 · 4 months ago

Adding another voice here. I'm hitting all five of these heuristic checks constantly during normal development on a Python/FastAPI project:

  1. $() command substitution — polling a local dev server for status
  2. Newlines separating commands — multi-step shell loops
  3. Quote chars inside # comments — inline Python scripts with comments
  4. Quoted newline + #-prefixed line — same, python3 -c with comments
  5. Output redirection (>) — suppressing stderr with 2>/dev/null

These fire even when the command prefix (curl, python3, while, etc.) is explicitly in my permissions.allow list. The permission system and the safety heuristics are completely orthogonal, which means there's no way to reduce friction for trusted local-only workflows.

A bashSafety config (granular or blanket) would be hugely appreciated. Even just being able to allow specific categories individually would cover most use cases.

skypher · 4 months ago

+1 this is super annoying. I understand it might have benefits but we need control over it.

yurukusa · 3 months ago

Workaround — PreToolUse hook to auto-approve commands that trigger safety heuristics:

The safety heuristic prompts (quoted characters, pipes, redirects) can be bypassed with a PreToolUse hook that approves the command before the heuristic fires:

#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
[ "$TOOL" != "Bash" ] && exit 0

# Auto-approve common patterns that trigger false safety prompts
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Approve known-safe patterns with pipes, redirects, quotes
if echo "$CMD" | grep -qE '^\s*(cat|grep|find|ls|echo|git|npm|yarn|python|node|cargo|go|make)'; then
    echo '{"decision":"approve"}'
    exit 0
fi
exit 0

This runs before the built-in safety heuristic and returns {"decision":"approve"}, which skips the interactive prompt entirely.

npx cc-safe-setup includes hooks that handle the most common false-positive patterns (cd+git compounds, comment stripping) while still blocking actually dangerous commands.

yurukusa · 3 months ago

A PreToolUse hook can override the safety heuristic by returning a permission decision:

CMD=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$CMD" ] && exit 0
if echo "$CMD" | grep -qE '^\s*(npm\s+test|cargo\s+test|go\s+test|pytest|make\s+test)'; then
    jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"test command auto-approved"}}'
fi
exit 0

The hook's permissionDecision: "allow" bypasses the safety heuristic prompt. You control which commands skip it.
For common patterns:

npx cc-safe-setup --install-example auto-approve-build
npx cc-safe-setup --install-example auto-approve-python
yurukusa · 3 months ago

/tmp/gh-comment-30435.md

yurukusa · 3 months ago

You can suppress these safety heuristic prompts with a PreToolUse hook that auto-approves commands matching the patterns you've described:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
FIRST_LINE=$(echo "$COMMAND" | head -1 | sed 's/^\s*//')
BASE=$(echo "$FIRST_LINE" | awk '{print $1}')
APPROVE=false
case "$BASE" in
    git)
        APPROVE=true
        ;;
    gh)
        APPROVE=true
        ;;
    for|while|if|case)
        BODY_CMDS=$(echo "$COMMAND" | grep -oE '^\s*\w+' | awk '{print $1}' | sort -u)
        APPROVE=true
        for cmd in $BODY_CMDS; do
            case "$cmd" in
                for|while|if|case|do|done|then|else|fi|in|esac) ;;
                echo|printf|cat|head|tail|grep|find|ls|git|npm|node|test|true|false) ;;
                cd|mkdir|touch|sort|uniq|cut|tr|awk|sed|jq) ;;
                *) APPROVE=false; break ;;
            esac
        done
        ;;
    echo|printf|cat|tee)
        APPROVE=true
        ;;
esac
if [ "$APPROVE" = true ]; then
    jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Safe command — safety heuristic false positive suppressed"}}'
fi
exit 0
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/suppress-safety-heuristics.sh" }]
    }]
  }
}

This specifically targets the patterns you listed:

  • $() command substitution in git commits → approved (git is the base command)
  • Backtick substitution in gh pr create → approved (gh is safe)
  • Multiline for loops → approved if body commands are all safe
  • Empty quotes before dashes → approved (echo/printf are safe base commands)
  • Quote chars in # comments → stripped during normalization

The hook fires before the safety heuristic, so permissionDecision: "allow" bypasses the interactive prompt entirely.

frastlin · 3 months ago

I am getting very tired of all these silly approves. I don't even know what they mean, and the command looks fine. Claude could run a python script to do any of these and as long as I have python allowed, it could perform these. I don't understand why I am getting warned about these commands. It did not used to be this way. Here is a list of all the commands I have found:

  • Command contains newlines that could separate multiple commands
  • Command contains backslash-escaped whitespace that could alter command parsing format
  • Command contains ANSI-C quoting which can hide characters
  • Command contains ${} parameter substitution
  • Command contains ambiguous syntax with command separators that could be misinterpreted
  • Command contains quoted characters in flag names
  • Command contains output redirection (>) which could write to arbitrary files
  • Shell expansion syntax in paths requires manual approval

I have in my claude.md file imperatives to not use these commands, but claude ignores them. I would very much like to turn these off.

extemporalgenome · 2 months ago

While I agree with the OP's proposal (do allow users to opt-into blanket overrides/disabling of specific safety heuristics), ideally we'd also just have the permission evaluation be smarter about command substitution and such:

  • Command substitution should generally be treated as only escalating the danger of the overall bash tool invocation if the substituted command is dangerous, or could cause dangerous argument-splitting.
  • "$(...)" should evaluate the substituted command for safety, but should otherwise treat the substitution the same as any string literal. "$(cat ...)" is not risky (readonly) unless it's something like "$(cat /etc/shadow)" (sensitive information exposure).
  • $(...), i.e. _without_ surrounding quotes, should be evaluated in terms of argument danger in the command it's passed to: echo has no real danger if given one or many args, so echo $(cat words) is going to be fine, but rm $(cat words) is dangerous due to argument splitting.
  • A simple initial good-enough heuristic might be to ask for permission on unquoted substitutions, while using the recursive permission evaluation on quoted substitutions, while guiding the model in the system prompt to always quote substitutions unless unquoted is strictly necessary to achieve a required effect.
frastlin · 2 months ago

Hello, the new "Auto Mode" does this evaluation. Since starting to use Auto Mode, I have not been asked to review any silly requests like this. I have a feeling Auto Mode may be doing more than just skipping these silly instances where permission is requested though.

T-Bone-Haff · 2 months ago

Adding empirical data to support this feature request from systematic testing on Claude Code 2.1.143 (macOS, zsh).

Three-loop characterization of --permission-mode against the "Contains brace with quote character (expansion obfuscation)" heuristic:

| Permission mode | Dialog firing rate (n=invocations) |
|---|---|
| default | 21/21 = 100% |
| acceptEdits | 6/6 = 100% |
| bypassPermissions | 0/6 = 0% |

Test invocations included trivial blocks like { echo "Hello from T1"; } >> file — pure literal-string echoes with no shell expansion, no command substitution, no risk surface. These trigger the dialog at 100% rate under both default and acceptEdits.

Confirming the OP's framing: the 200+ Bash(...) allow-patterns accumulated in my settings.local.json do not suppress this class. The allow-list operates on exact-command-pattern matches; the heuristic fires on syntactic shape (literal { + "), a different layer.

Operator-side mitigation is empirically binary: live with the dialogs (scaling to hundreds per multi-phase scripted cycle in my use case) or run with bypassPermissions and accept the platform's intentional sandbox-only trust gate. The intermediate acceptEdits mode does not help with this heuristic class.

Why this matters for the proposed "bashSafety": "allow" setting: The current binary surface forces users with bounded-blast-radius repos (single-user dev environments) to choose between "rubber-stamp 100+ dialogs per session" — which produces ask-fatigue and undermines the heuristic's safety purpose — and "disable all permission checks entirely." The proposed setting would allow heuristic-aware users to preserve allow-list discipline while suppressing the over-broad syntactic class.

Strongly support this feature request. Happy to share full diagnostic transcripts if useful for engineering investigation.