Skip to content

Bash permission allow-rules: literal '*' inside quoted regex/glob arguments is silently reinterpreted as a positional wildcard on replay #95614

Description

@nicandr0

Summary
When a Bash command containing a literal * inside a quoted argument (e.g. a grep regex like "onclick.*Tab", or a glob like "*.py") is approved via "always allow", the resulting settings.local.json rule stores the * as-is. On replay, Claude Code's permission matcher treats every * in the stored string as its own positional wildcard — regardless of whether it originated inside quotes as a literal/regex character — allowing arbitrary content to be inserted at that position on future matches, as long as the literal suffix after it still occurs later in the command.

Why this matters
* is the universal wildcard/glob character across shells, OSes, and regex engines. Any user approving a one-off command that happens to contain a regex .* or a glob *.ext in a quoted argument unknowingly creates an allow-rule with an unintended insertion point — with no warning, no escaping mechanism, and no distinction between "wildcard meant as permission scope" vs. "literal character that happened to appear in quoted content."

This is a real-world instance of the same class of problem as #51057 (skill-level command normalization not matching the actual matcher semantics) — here at the core permission-matching layer itself.

Repro

  1. Run a Bash command with a quoted regex containing .*, e.g.:
    grep -n "onclick.*Tab" somefile.html
  2. Approve it with "always allow".
  3. Inspect the resulting rule in .claude/settings.local.json — the * from the regex is stored raw, at a position that is not at the end of the pattern and not preceded by a space (contradicting the project's own documented safe-wildcard guidance: wildcards should only appear at the end, preceded by a space, per the fewer-permission-prompts skill instructions).
  4. Any future command that reproduces the literal prefix/suffix around that * — with arbitrary content (including shell metacharacters ;, &&, $(), backticks) spliced into the wildcard's position — matches this rule and is auto-approved without a permission prompt.

Suggested fixes (any of these would resolve it)

  • Warn the user at approval time if the literal command contains a * inside a quoted argument, and offer to store the rule with that character escaped/treated as literal.
  • Use a non-colliding placeholder syntax for the permission engine's own wildcard (e.g. a dedicated token) instead of overloading *, which already carries context-dependent meaning in shells and regexes, and provides no logic to distinguish "wildcard meant as a stand-in for a specific word/pattern" from "the engine's own approval-scope wildcard."
  • At minimum, restrict stored allow-rules to only support wildcards in the documented safe position (end of pattern, preceded by a space) and reject/flag any other * occurrence as literal by default.

Environment: Claude Code CLI, discovered on Linux (WSL2), reproduced via the standard "always allow" approval flow — not linked to any specific plugin.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions