You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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)"
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\"".
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.
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.
Confirming this also bypasses deny rules — not just ask
The report focuses on ask rules being skipped, but the same bypass applies to deny rules, which is more severe — it silently executes commands that are explicitly blocked.
Expected: blocked — matches "git push --force*". Actual: executed silently — the GITHUB_TOKEN=... prefix caused no match, fell through to "*": "allow".
The force push completed and rewrote remote history on a branch that was part of a live stacked PR chain. The user only noticed afterward when reviewing what happened during the session.
The impact of this variant is higher than the ask bypass: with ask, the user at least gets a chance to reject. With deny, the expectation is unconditional blocking — there's no fallback.
Activity
test: add test capturing env var prefix permission bypass (anomalyco#…
fazzledev commentedon Mar 8, 2026
@thdxr Please look into this. The current situation is very dangerous as none of the permissions are respected.
prullanferragut commentedon May 20, 2026
Confirming this also bypasses
denyrules — not justaskThe report focuses on
askrules being skipped, but the same bypass applies todenyrules, which is more severe — it silently executes commands that are explicitly blocked.Reproduction with
deny:Config:
{ "permission": { "bash": { "*": "allow", "git push --force*": "deny", "git push * -f*": "deny", "git push -f*": "deny" } } }Command executed by the agent:
Expected: blocked — matches
"git push --force*".Actual: executed silently — the
GITHUB_TOKEN=...prefix caused no match, fell through to"*": "allow".The force push completed and rewrote remote history on a branch that was part of a live stacked PR chain. The user only noticed afterward when reviewing what happened during the session.
The impact of this variant is higher than the
askbypass: withask, the user at least gets a chance to reject. Withdeny, the expectation is unconditional blocking — there's no fallback.fix: strip inline env var prefixes from bash permission patterns
github-actions commentedon Jul 20, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.