Description
Commands with inline environment variable prefixes (e.g. CI=true git commit) bypass bash permission rules. A user who has configured "git *": "ask" will not be prompted — the command executes silently.
This affects any inline env var prefix, not just CI=true. FOO=bar git commit, ANYTHING=1 git push — all bypass the pattern match.
Root Cause
Introduced in commit e7ff714 (2026-01-30): "fix: handle redirected_statement treesitter node in bash permissions (#6737)"
In packages/opencode/src/tool/bash.ts, line 97:
let commandText = node.parent?.type === "redirected_statement" ? node.parent.text : node.text
node.text returns the full text of the command AST node, including any variable_assignment children. So for CI=true git commit -m "msg", commandText becomes "CI=true git commit -m \"msg\"".
Then on line 139:
patterns.add(commandText) // "CI=true git commit -m \"msg\""
The permission system then tries to match "CI=true git commit -m \"msg\"" against the configured rule "git *" — no match — and falls through to "*": "allow", so the command runs without a dialog.
Meanwhile, the command[] array (used for always) correctly strips variable_assignment children (the loop on lines 100–113 only includes command_name, word, string, raw_string, concatenation node types). So command[0] = "git" is correct — but it's not used for the patterns.add() that drives permission matching.
Before the offending commit
The previous code was:
patterns.add(command.join(" ")) // "git commit -m msg" ← correctly stripped
This correctly matched "git *" regardless of any inline env var prefix.
The change broke it
The commit changed patterns.add(command.join(" ")) to patterns.add(commandText) to fix redirect handling (so users see git commit 2>&1 in the permission dialog instead of just git commit). The redirect fix is valid, but it unintentionally included variable_assignment nodes in the pattern used for permission matching.
Steps to Reproduce
- Configure
opencode.json:
{
"permission": {
"bash": {
"git *": "ask",
"*": "allow"
}
}
}
- Have an AI agent run:
CI=true git commit --allow-empty -m "test"
- Expected: Permission dialog fires for
git commit
- Actual: Command executes silently — no dialog
Works with any inline env var: FOO=bar git commit, GIT_AUTHOR_NAME=x git commit, etc.
Note: export CI=true; git commit (separate commands) is not affected — only the inline VAR=value command syntax.
Environment
- OpenCode version: 1.1.51 (bug present, introduced in 1.1.x around 2026-01-30)
- Confirmed via binary strings extraction and source code analysis
- Affects all platforms
Description
Commands with inline environment variable prefixes (e.g.
CI=true git commit) bypass bash permission rules. A user who has configured"git *": "ask"will not be prompted — the command executes silently.This affects any inline env var prefix, not just
CI=true.FOO=bar git commit,ANYTHING=1 git push— all bypass the pattern match.Root Cause
Introduced in commit e7ff714 (2026-01-30): "fix: handle redirected_statement treesitter node in bash permissions (#6737)"
In
packages/opencode/src/tool/bash.ts, line 97:node.textreturns the full text of thecommandAST node, including anyvariable_assignmentchildren. So forCI=true git commit -m "msg",commandTextbecomes"CI=true git commit -m \"msg\"".Then on line 139:
The permission system then tries to match
"CI=true git commit -m \"msg\""against the configured rule"git *"— no match — and falls through to"*": "allow", so the command runs without a dialog.Meanwhile, the
command[]array (used foralways) correctly stripsvariable_assignmentchildren (the loop on lines 100–113 only includescommand_name,word,string,raw_string,concatenationnode types). Socommand[0]="git"is correct — but it's not used for thepatterns.add()that drives permission matching.Before the offending commit
The previous code was:
This correctly matched
"git *"regardless of any inline env var prefix.The change broke it
The commit changed
patterns.add(command.join(" "))topatterns.add(commandText)to fix redirect handling (so users seegit commit 2>&1in the permission dialog instead of justgit commit). The redirect fix is valid, but it unintentionally includedvariable_assignmentnodes in the pattern used for permission matching.Steps to Reproduce
opencode.json:{ "permission": { "bash": { "git *": "ask", "*": "allow" } } }CI=true git commit --allow-empty -m "test"git commitWorks with any inline env var:
FOO=bar git commit,GIT_AUTHOR_NAME=x git commit, etc.Note:
export CI=true; git commit(separate commands) is not affected — only the inlineVAR=value commandsyntax.Environment