[BUG] Bash permission patterns with wildcards don't match commands with flags (e.g., "kill *" doesn't match "kill -9 PID")
Description
Bash permission patterns using wildcards appear to not match commands that include flags or arguments in certain positions.
Configuration:
"permission": {
"bash": {
"kill *": "ask",
"pkill *": "ask"
}
}
Expected behavior: Running kill -9 44165 should prompt for permission ("ask")
Actual behavior: The command executes immediately without prompting, even though the pattern "kill *" should match kill -9 44165
Possible cause (unconfirmed): After investigating the codebase, we noticed that the bash tool in packages/opencode/src/tool/bash.ts uses a whitelist of treesitter node types when extracting command tokens for permission matching:
if (
child.type !== "command_name" &&
child.type !== "word" &&
child.type !== "string" &&
child.type !== "raw_string" &&
child.type !== "concatenation"
) {
continue;
}
It's possible that flags like -9 are parsed as argument nodes by treesitter, which would cause them to be skipped during extraction. This could result in the command string being truncated to just "kill", which wouldn't match the pattern "kill *" (due to the literal space). The system might then fall back to the default "*": "allow" rule.
Note: This is speculative based on code review - we haven't confirmed this is the definitive root cause.
OpenCode version
1.0.224
Steps to reproduce
- Add this to
opencode.json:
"permission": {
"bash": {
"kill *": "ask",
"pkill *": "ask"
}
}
- Start OpenCode and ask the agent to kill a process by PID
- Observe that
kill -9 <PID>executes without prompting for permission
Screenshot and/or share link
No response
Operating System
macOS
Terminal
Ghostty
🤖 This content was generated with AI assistance using Claude Opus 4.5 (Thinking).
This issue might be a duplicate of existing issues. Please check:
- #5330: More explicit bash command permissions - discusses wildcard permission patterns matching commands with additional arguments/flags
I've also experienced this. for me... wildcard or not, this issue persists.
"permission": {
"bash": {
"git ls-files": "allow",
"git ls-files*": "allow",
"git ls-files *": "allow",
}
},
From the export
User
run git ls-files
Assistant (Build · gpt-5-mini (Github Copilot) · 13.1s)
I'll list the repository's tracked files by running git ls-files. Running the command now.
Tool: bash
**Input:**
```json
{
"command": "git ls-files",
"description": "List tracked files in the git repository",
"timeout": 120000
}
Error:
Error: The user rejected permission to use this specific tool call.
I can revert back to v1.0.223 until this is fixed, and simply include "git ls-files": "allow",, which works as expected.
I have a feeling this much more granular syntax is going to be challenging, and perhaps increase custom permissions exponentially, if this continues as such. For example, i'm happy to allow opencode to call any git read-only operations it needs, but want it to always ask before doing any writing ops. Any chance adoption of a standard like regex is on the roadmap?
@scottrbaxter can you show your config?
opencode debug config
@scottrbaxter can you show your config?
opencode debug config
I shared the relevant part of my config above... is there something else specific to this issue that you need? i'd rather not just dump my entire config into a public forum.
"permission": {
"bash": {
"git ls-files": "allow",
"git ls-files*": "allow",
"git ls-files *": "allow",
}
},
Well ideally all your agent configs, you can like take out irrelevant stuff but ideally all the agent configs and the global permissions, if u do a debug config I can see the fully resolved permissions which helps debug.
@rekram1-node no agent configs and i can still reproduce this issue. also, think i narrowed it down to the appended global wildcard.
"permission": {
"bash": {
"git ls-files": "allow",
"git ls-files*": "allow",
"git ls-files *": "allow",
"*": "ask" // last rule
}
},
simply comment out "*": "ask" in permission.bash and the command is allowed.
here's a config (only provider is removed) which is able to reproduce this issue:
{
"$schema": "https://opencode.ai/config.json",
"theme": "palenight",
"keybinds": {
"leader": "ctrl+x",
"app_exit": "ctrl+q,q",
"editor_open": "<leader>e",
"theme_list": "<leader>t",
"sidebar_toggle": "<leader>b",
"scrollbar_toggle": "none",
"username_toggle": "none",
"status_view": "<leader>s",
"session_export": "<leader>x",
"session_new": "<leader>n",
"session_list": "<leader>l",
"session_timeline": "<leader>g",
"session_fork": "none",
"session_rename": "none",
"session_share": "none",
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"messages_page_up": "pageup",
"messages_page_down": "pagedown",
"messages_half_page_up": "ctrl+alt+u",
"messages_half_page_down": "ctrl+alt+d",
"messages_first": "ctrl+g,home",
"messages_last": "ctrl+alt+g,end",
"messages_next": "none",
"messages_previous": "none",
"messages_last_user": "none",
"messages_copy": "<leader>y",
"messages_undo": "<leader>u",
"messages_redo": "<leader>r",
"messages_toggle_conceal": "<leader>h",
"tool_details": "none",
"model_list": "<leader>m",
"model_cycle_recent": "f2",
"model_cycle_recent_reverse": "shift+f2",
"model_cycle_favorite": "none",
"model_cycle_favorite_reverse": "none",
"command_list": "ctrl+p",
"agent_list": "<leader>a",
"agent_cycle": "tab",
"agent_cycle_reverse": "shift+tab",
"variant_cycle": "ctrl+t",
"input_clear": "ctrl+c",
"input_paste": "ctrl+v",
"input_submit": "return",
"input_newline": "shift+return,ctrl+return,alt+return,ctrl+j",
"input_move_left": "left,ctrl+b",
"input_move_right": "right,ctrl+f",
"input_move_up": "up",
"input_move_down": "down",
"input_select_left": "shift+left",
"input_select_right": "shift+right",
"input_select_up": "shift+up",
"input_select_down": "shift+down",
"input_line_home": "ctrl+a",
"input_line_end": "ctrl+e",
"input_select_line_home": "ctrl+shift+a",
"input_select_line_end": "ctrl+shift+e",
"input_visual_line_home": "alt+a",
"input_visual_line_end": "alt+e",
"input_select_visual_line_home": "alt+shift+a",
"input_select_visual_line_end": "alt+shift+e",
"input_buffer_home": "home",
"input_buffer_end": "end",
"input_select_buffer_home": "shift+home",
"input_select_buffer_end": "shift+end",
"input_delete_line": "ctrl+shift+d",
"input_delete_to_line_end": "ctrl+k",
"input_delete_to_line_start": "ctrl+u",
"input_backspace": "backspace,shift+backspace",
"input_delete": "ctrl+d,delete,shift+delete",
"input_undo": "ctrl+-,super+z",
"input_redo": "ctrl+.,super+shift+z",
"input_word_forward": "alt+f,alt+right,ctrl+right",
"input_word_backward": "alt+b,alt+left,ctrl+left",
"input_select_word_forward": "alt+shift+f,alt+shift+right",
"input_select_word_backward": "alt+shift+b,alt+shift+left",
"input_delete_word_forward": "alt+d,alt+delete,ctrl+delete",
"input_delete_word_backward": "ctrl+w,ctrl+backspace,alt+backspace",
"history_previous": "shift+up",
"history_next": "shift+down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",
"terminal_suspend": "ctrl+z",
"terminal_title_toggle": "none",
"tips_toggle": "<leader>h"
},
"share": "disabled",
"autoupdate": false,
"disabled_providers": [
"opencode",
"ollama-cloud",
"github-copilot"
],
"permission": {
"bash": {
"git ls-files": "allow",
"git ls-files*": "allow",
"git ls-files *": "allow",
"*": "ask"
}
},
"agent": {},
"mode": {},
"plugin": [],
"command": {},
"username": "scott"
}
@scottrbaxter try this instead:
"permission": {
"bash": {
"*": "ask", // last rule is now first rule
"git ls-files": "allow",
"git ls-files*": "allow",
"git ls-files *": "allow",
}
},
@rekram1-node here are my findings with the wildcard set first (top of the list).
Allowed without prompt, and does not permit args without being asked first.
"bash": {
"*": "ask"
"git ls-files": "allow",
}
Allowed without prompt (this seems to match the command itself and include any args passed).
"bash": {
"*": "ask"
"git ls-files*": "allow",
}
What if i want to pass an arg to a command that begins with the same character(s) as other commands (e.g. w)? with this logic, i have to allow all executables that begin with w... or i'm stuck writing new rules foreach and every arg. not ideal.
Asks to allow.
"bash": {
"*": "ask"
"git ls-files *": "allow",
}
I believe this is the expected response, though does make this new change a lot more cumbersome and less intuitive to the user. I'll now have to reconsider a ton of commands in this list, just so that i can pass args (e.g. allow read only ops, ask for write ops) to them as intended. This list will easily become 10x longer.
Still seems like a great use case for regex patterns. Any chance that's being considered?
I also think this is very unintuitive (and dangerous). I can only auto-allow flags in commands if there's no space before the wildcard. But this is risky because multiple commands may share that prefix.
If my config has "ls*": "allow". It will allow running lstmeval (for example). I did test this.
But if I use "ls *": "allow" (with space). It now asks for permissions when passing any flags to ls like ls -la.
So I can either allow all the commands starting with ls, even if the command is lsyouarehacked; or manually approve all commands.
I found a way to get the expected behaviour, which is allowing both the bare command and the command with *. For example:
"ls": "allow",
"ls *": "allow",
"grep": "allow",
"grep *": "allow",
This change is a pretty big rate limiter for moving from 1.0.223 to 1.1+. I could refactor all of these in my config, but i wonder if this really is going to be a permanent change for the seeable future? There are clearly going to be limitations and lots of added entries with trial and error to correctly pattern match, e.g. if a user wants just a few args for a command allowed to run vs asking or denying. also the wildcard at the top seems counter-intuitive... though maybe that's on me comparing, this logic to systems like iptables chains.
Is there any reconsideration on how this feature/change is being implemented? i could hold off on 1.0.233 while it's being reconsidered/refactored, if so.. but would like to better understand what the target state will be.
ill discuss further with dax
Here is a basic SAFE test case, I think this works as expected rn?
My permissions:
"permission": {
"bash": {
"ls *": "ask",
},
},
Notice that I got prompted for ls AND ls -al
Exact test case here too
Here is a basic SAFE test case, I think this works as expected rn?
The problem is with "allow". Not "ask".
Going to mark this as completed now believe my wildcarding fix fixed this
Awesome, thanks! I checked, and now the behaviour seems correct in v1.1.18
Initial tests confirmed working as expected, thank you @rekram1-node!