anomalyco / anomalyco/opencode
[SECURITY] Shell permission: CWD exception + stdout/write gadgets (echo/python3/cargo) defeat permission prompts and deny rules
@jlongster is already working on this.
Since Aug 13, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
[SECURITY] Shell permission: CWD exception + stdout/write gadgets (echo/python3/cargo) defeat permission prompts and deny rules
Description
Description
- Severity: High (local agentic security boundary bypass; makes
permission: denyadvisory) - Affected:
tool/shell.ts(verified atbaef5cd43b; redirection gap still present at743f6410f2) - Related: #39931 (
--parse-error bypass, open), #32628 (redirect targets, open), #18396 (subprocess side-effects, auto-closed), #38822 (exec-path parser refactor, auto-closed unmerged), #38807
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; see References below)
Steps to reproduce
- Set
permission: { bash: "deny" }. - Prompt the agent with (a)
env git status, (b)echo > ~/.ssh/authorized_keysstyle args, (c)python3 -c "<code that writes outside worktree>", (d) a project-local./script.sh(CWD exception). - Observe prompts/denials are skipped or inconsistent: CWD-resolved executables are implicitly permitted, and argv-code gadgets (
python3 -c,cargo install,node -e) are not path-scored by the FILES list (packages/opencode/src/tool/shell.ts:30-53).
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin)
Details
Two adjacent gaps in the shell permission scanner are unreported on their own merits (they currently only exist as fragments inside other issues):
-
CWD exception defeats deny-all. The scanner treats any command whose executable resolves inside the working directory (project-local binaries,
./scripts/*) as implicitly permitted —shell.ts:30sets CWD, and the path check exempts project-relative executables. A malicious repo can drop./script.sh(checked in or fetched during install), and the deny-all user gets no prompt and no denial: the agent runs it unchecked. A repo-controlled file executing with the user's session privileges is the worst-case shape for agentic malware — and it does not require a bypass "trick" at all, it falls out of the documented permission model. -
FILES-list validation is keyed on argc position, not on what the binary does. The tool's FILES list (
shell.ts:31-53) path-validates arguments it scores as file paths (e.g. positional args toecho,python3,cargo), but the plumbing covers neither:- stdout gadgets:
echo/printf/catwriting in-band is harmless, butpython3 -c "..."andcargo run/cargo install/node -etake code in arguments that are not path-scored, so they are checked against no path rules at all — yet they can write anywhere (devices,/etc,$HOME/.ssh) and read anything into stdout. - env-var execution:
env git status(and friends:VAR=1 cmd) execute a command that the scanner never parses from the argument list;VERSION=1 npm run xetc. all re-exec with the env prefix. Whether the inner command is then checked depends on the parser's handling of the leading tokens — observed behavior is inconsistent and depends on the argument shape, i.e. the same logical action is permitted or denied based on cosmetic differences.
- stdout gadgets:
Net effect: permission: { bash: deny } (or any rule set) is effectively bypassable by trivial rewording, as independently reproduced 11/11 ways in #39931-related discussions — the parser never sees the actual executed program for these shapes.
Suggested fix
- Parse the command with the same grammar the shell uses: strip env-prefix assignments and expansions (
env,VAR=x), resolvePATH-style lookups, then evaluate the effective command — not the raw argv. - Treat CWD-resolved executables as permission-checked like any external binary, or explicitly require a capability flag for project-binary execution.
- Path-check code that executes code:
python3,node,cargo,ruby,perlinvocations should be evaluated for what they can touch (heredocs,-c, install targets), matching the existing redirection-target direction of travel (#32628).
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; file:line references in Details).
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin).
Steps to reproduce
- Set
permission: { bash: "deny" }. - Prompt the agent with (a)
env git status, (b) stdout/write gadgets such asecho > ~/.ssh/authorized_keysorpython3 -c "<code that writes outside the worktree>", (c) a project-local./script.sh(CWD exception). - Observe prompts/denials are skipped or inconsistent: CWD-resolved executables are implicitly permitted, and argv-code gadgets (
python3 -c,cargo install,node -e) are not path-scored by the FILES list (packages/opencode/src/tool/shell.ts:30-53).
Details
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.