Wildcard Permission Matching Failure for Curl Commands in Claude Code
Bug Description
Subject: Claude Code Permission System Not Working - Curl
Commands Still Being Blocked Despite Comprehensive
Wildcards
Bug Description:
The Claude Code permission system in
.claude/settings.local.json is not properly recognizing
wildcard patterns for curl commands. Despite having
extensive wildcard permissions including "", "Bash()",
"curl ", "curl", and "Bash(curl*)", curl commands are
still intermittently blocked and require user approval.
Expected Behavior:
All curl commands should execute without stopping when
comprehensive wildcard permissions are configured in the
settings file.
Actual Behavior:
Curl commands randomly stop execution and ask for
permission despite being explicitly whitelisted with
multiple wildcard patterns.
Configuration Details:
- File: /Users/greg/codebase/.claude/settings.local.json
- Platform: macOS (Darwin 24.5.0)
- Claude Code Model: claude-sonnet-4-20250514
Permissions Configured:
{
"permissions": {
"allow": [
"*",
"Bash(*)",
"curl(*)",
"curl *",
"curl",
"Bash(curl)",
"Bash(curl*)",
"Bash(curl:*)",
"Bash(curl -*)",
"Bash(curl )",
"Bash(curl *)",
// ... plus 100+ specific curl patterns
],
"deny": []
}
}
Specific Examples That Still Get Blocked:
- curl -X PUT "http://localhost:3351/api/..." -H
"Content-Type: application/json" -d '{"key":"value"}'
- curl -s "http://localhost:3351/api/tasks" | jq
- Various curl commands with different flag combinations
Impact:
This breaks automated workflows and forces manual
intervention for every curl command, making Claude Code
unusable for API-heavy development tasks.
Reproduction Steps:
- Configure comprehensive curl wildcards in
.claude/settings.local.json
- Attempt to run various curl commands through Claude
Code
- Observe that some commands still trigger permission
requests
Request:
Please fix the permission pattern matching system to
properly respect wildcard patterns, especially the global
"" and "Bash()" patterns which should theoretically
allow any command.
Environment Info
- Platform: darwin
- Terminal: vscode
- Version: 1.0.35
- Feedback ID: 8bd43805-6977-48a4-bd3a-01766423c25a
Errors
[{"error":"Error: Command failed: security find-generic-password -a $USER -w -s \"Claude Code\"\nsecurity: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.\n\n at genericNodeError (node:internal/errors:983:15)\n at wrappedFn (node:internal/errors:537:14)\n at checkExecSyncError (node:child_process:892:11)\n at execSync (node:child_process:964:15)\n at TZ (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:700:3921)\n at file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:622:9014\n at Q (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:526:17199)\n at VX (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:622:8160)\n at nS (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:622:7241)\n at f9 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:622:11408)","timestamp":"2025-06-29T08:18:41.487Z"}]
21 Comments
Yes, please, urgently, this slows down progress immensely - espcially in testing.
Related, I added this to
"allow":in settings:But claude doesn't recognize it; still prompts whenever needing to write to the file.
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
sigh
Running into the same issues too. Across multiple commands - on MacOS.
This comment claims that claude does not support wildcards. But claude code seems to allow rules with wildcards itself when you select the option to allow things in the future. For example, claude code wrote this in my settings:
"Bash(git commit:*)",Is that comment just wrong?
@fwends – have you tried
"Bash(curl:*)"(asterisk/wildcard after the colon)? That seems to work for me and follows the pattern that Claude Code uses in the settings.local.json if you select the "Yes, and don't ask again..." option for a given command prefix.@nbrustein – it definitely supports wildcards... I think he just worded his comment poorly. He's basically saying that to allow all Bash commands (which for the record, I have no clue why you would want to do that), you would use
"Bash"instead of"Bash:*". But for a specific command (e.g., if you want to allow alllsofcommands), you can use wildcards to do that ("Bash(lsof:*)").That's the same syntax I have for jq:
"Bash(jq:*)"
That particular one doesn't work but others seem to work perfectly fine:
"Bash(mv:*)"
"Bash(mkdir:*)"
@monkeyden
@nbrustein
@sbs44
you know the issue is from 4 months ago right, now i just use bypass it's easier than messing about with claude options
i don't know if it got fixed, but it was not working no matter what i did and yes everything was tried to make it work, it was a claude issue not a problem between keyboard and chair!
I don’t have a concrete reproducible example yet, but I ran into a problem where if there’s even a slight syntax or command issue in the settings file, Claude silently ignores the entire file. This makes it look like it’s ignoring your allow permissions.
Through trial and error, I found that certain allow permission lines would cause Claude to fail to parse the file. If I removed just that line, the rest of the permissions worked as expected. The commands looked valid at first glance but still triggered the issue.
To debug, I used the /permissions command to check what had been granted. By progressively tweaking the file and removing lines, I was able to isolate the problematic entry.
Summary: a single invalid allow line causes Claude to silently discard all permissions from the settings file, with no error message or warning.
I couldn't care less that it's from 4 mos ago. It's apparently still an issue in the latest release.
I tried:
in project and user settings. Nothing worked.
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
Was this fixed? Why autoclose then?
Still seems broken or at least buggy for me
I think this might be related to the requirement for both
curlaccess and a whitelist of allowed domains.Tested on Claude Code v2.0.76. This
.claude/settings.local.jsonconfiguration works well for me.<img width="2758" height="1041" alt="Image" src="https://github.com/user-attachments/assets/5cb85ee7-46da-442a-a93d-d7c87dfb0c75" />
It does not ask any permissions again when I ask it to get some info from GitHub.
Hopefully it helps. ☺️
This does _not_ work in v2.1.9.
Having this in the permissions:
still prompts for curl usage.
It does appear to _sometimes_ work, which makes me scared about the security layer of the tool. The behaviour of the permissioning system should not depend on a dice roll.
Concur. Some people (earlier CC versions?) it works for but this plagues me to this day. I have it configured to approve and I am never given the option to approve for the session. Neither for curl nor jq, and I have a lot of JSON.
I really went deep on this one. Well, Claude did. I just told it what to do. I believe this is the same issue mentioned in this other GH Issue.
For Anthropic staff or their helper bots:
For Claude Users:
The issue:
Even in skip permission mode, no matter how many times one presses "Yes, and don't ask again", it _will_ ask again, and again, and again. So I had Claude debug itself. It resorted to extracting the code from the npm package, beatifying it, then tracing through it to find the issue. Below is what it wrote up for me. The "fix" ... "worked" .. but of course blanket-allow-listing everything is not ideal.
Why
--dangerously-skip-permissionsDoesn't Work (and How to Fix It)Claude Code v2.1.42 — February 2026
TL;DR
--dangerously-skip-permissionsdoesn't reliably bypass permission prompts. The flag sets an internal mode calledbypassPermissions, but Bash commands go through a command injection security scanner that runs before the bypass mode is consulted. Certain command patterns trigger this scanner, causing a permission prompt regardless of the flag.The fix: add explicit allow rules to
~/.claude/settings.json. These are checked at a different point in the permission flow — one that actually takes priority over the security scanner.Add
"mcp__<server-name>__*"entries for any MCP servers you use.The Bug: How Permission Checks Actually Work
When Claude wants to run a tool, the permission system evaluates it through a chain of checks. The order matters, because the first definitive answer wins.
The Chain for Bash Commands
The bypass mode check is at step 5. The command injection scanner is at step 3c. When the scanner triggers, it returns
{behavior: "ask"}fromcheckPermissions(). This bubbles up through step 3, and the flow reaches step 5 — but only if step 3 returned a "passthrough." When step 3 returns "ask," it's returned as the final result fromcheckPermissions(), and step 4 evaluates it. Since Bash doesn't setrequiresUserInteraction, it falls through to step 5... except the result already has suggestions attached, and the permission UI kicks in.The exact mechanics of why it short-circuits are buried in minified code, but the observable behavior is clear: complex Bash commands trigger prompts even with
--dangerously-skip-permissions.Why
settings.jsonAllow Rules Fix ItThe allow rules from
settings.jsonare checked at step 3a (exact match) and step 3b (prefix match) — before the injection scanner at step 3c.Bash(*)matches as a wildcard prefix rule, so the check returns "allow" beforeob()ever runs.What Triggers the Command Injection Scanner
The scanner (
ob()) runs 14 security checks on every Bash command. Here's what triggers a prompt:Tier 1: Early Checks (run first, can allow or block)
| Check | Triggers On | Example |
|-------|-------------|---------|
| Empty command | Empty string | (auto-allowed) |
| Heredoc in substitution |
$(cat <<EOF ...)|echo "$(cat <<'EOF' ...)"|| Plain heredoc |
<<-'DELIM'with quoted delimiter |cat <<'EOF'(auto-allowed) || Git commit |
git commitwith safe flags |git commit -m "msg"|Tier 2: Security Checks (the ones that cause prompts)
| Check ID | Triggers On | Example That Would Prompt |
|----------|-------------|--------------------------|
| INCOMPLETE_COMMANDS | Starts with tab, flags, or operators |
&& echo hello,-rf /|| JQ_SYSTEM_FUNCTION |
jqwithsystemkeyword |jq 'system("rm -rf /")' file|| JQ_FILE_ARGUMENTS |
jqreading files in unsafe ways | variousjqpatterns || OBFUSCATED_FLAGS | Flags that don't match known-safe sets |
sedwith unusual flags || SHELL_METACHARACTERS |
;,\|,&in arguments |find . -name "*.tmp" -exec rm {} \;|| DANGEROUS_VARIABLES |
$VARnear pipes or redirects |echo $HOME \| grep user|| NEWLINES | Newlines in unquoted content | Multi-line commands |
| COMMAND_SUBSTITUTION |
$(),${}, backticks | `echowhoami|<| INPUT_REDIRECTION |
with suspicious patterns |< /etc/passwd|>| OUTPUT_REDIRECTION |
with suspicious patterns |> /etc/crontab|$IFS| IFS_INJECTION |
or${..IFS..}|$IFSin any command |git commit| GIT_COMMIT_SUBSTITUTION |
with$()in message |git commit -m "$(date)"|/proc/*/environ| PROC_ENVIRON_ACCESS |
|cat /proc/self/environ|echo "hello` (missing close quote) || MALFORMED_TOKEN_INJECTION | Unbalanced quotes, braces, brackets |
The Pattern
Simple commands pass:
ls,echo hello,git status,cat file.txt.Commands that trigger prompts tend to involve:
command1 | command2(shell metacharacter check)$(...)or backtickscmd1 && cmd2,cmd1; cmd2$VAR | grep,> $FILE< $INPUT,> $OUTPUTfind/sed/jqinvocations: Various heuristicsThis is why the behavior seems random — simple
echoorlsworks, but a pipedgrepcommand prompts. The trigger is the content of the command, not anything about the tool or session configuration.The
--dangerously-skip-permissionsFlag Isn't Entirely BrokenFor non-Bash tools (Read, Write, Edit, Glob, Grep, MCP tools), the flag works correctly. These tools'
checkPermissions()methods return "passthrough" or "allow," so the flow reaches step 5 wherebypassPermissionstakes effect.It's specifically the Bash tool's command injection scanner that creates the gap. The scanner was clearly designed as a security-critical check that should override even bypass mode — but this was never documented, and the flag's description says "Bypass all permission checks."
Additional Issues
Sub-agents never inherit bypass mode
When Claude spawns sub-agents via the Task tool, they run with
permissionMode: "default"or"acceptEdits". The parent'sbypassPermissionsmode is not propagated. This is a known architectural issue (GitHub #25526, #18950).The
settings.jsonfix partially helps here because the allow rules are loaded from disk by the sub-agent process, but thedefaultModesetting may not be respected in all sub-agent contexts.Plan mode and bypass mode are mutually exclusive
Shift+Tab cycles between modes as parallel states. Activating plan mode deactivates bypass (GitHub #5466). Worse,
ExitPlanModecan be denied under bypass mode, creating a trap (GitHub #25627).Statsig feature gate
A server-side feature gate (
tengu_disable_bypass_permissions_mode) can silently disable bypass mode for your account. Check~/.claude.jsonforcachedGrowthBookFeatures.tengu_disable_bypass_permissions_mode. If it'strue, bypass mode is disabled regardless of any flags or settings.Methodology
This analysis was performed by:
ps~/.claude/debug/~/.local/share/claude/versions/2.1.42anthropics/claude-codeKey functions in the binary (v2.1.42, names are minified):
aHB()— resolves CLI flags + settings into final permission modevS1()— per-tool-call permission evaluatorhHB()— Bash-specific permission checkerob()— command injection scanner (14 checks)cF()— main permission evaluation entry pointJust adding onto this to say that it's a bit maddening when explore subagents are spawned and rack up dozens of permission requests despite my allow list. This singular issue means long-running tasks are impossible, given that you need to babysit the CLI to press enter almost all the time now.
Wildcard permission matching fails for curl and other commands with complex arguments. A hook handles this reliably:
The hook extracts the URL from the curl command and checks it against a domain allowlist. No wildcard pattern matching issues — the logic is explicit bash code.