Skip to content

[SECURITY] Shell permission: CWD exception + stdout/write gadgets (echo/python3/cargo) defeat permission prompts and deny rules #42436

Description

@shafqatevo

[SECURITY] Shell permission: CWD exception + stdout/write gadgets (echo/python3/cargo) defeat permission prompts and deny rules

Description

Description

Plugins

None required

OpenCode version

Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; see References below)

Steps to reproduce

  1. Set permission: { bash: "deny" }.
  2. Prompt the agent with (a) env git status, (b) echo > ~/.ssh/authorized_keys style args, (c) python3 -c "<code that writes outside worktree>", (d) a project-local ./script.sh (CWD exception).
  3. 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):

  1. 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:30 sets 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.

  2. 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 to echo, python3, cargo), but the plumbing covers neither:

    • stdout gadgets: echo/printf/cat writing in-band is harmless, but python3 -c "..." and cargo run/cargo install/node -e take 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 x etc. 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.

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), resolve PATH-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, perl invocations should be evaluated for what they can touch (heredocs, -c, install targets), matching the existing redirection-target direction of travel (Shell redirect targets bypass the external_directory permission #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

  1. Set permission: { bash: "deny" }.
  2. Prompt the agent with (a) env git status, (b) stdout/write gadgets such as echo > ~/.ssh/authorized_keys or python3 -c "<code that writes outside the worktree>", (c) a project-local ./script.sh (CWD exception).
  3. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions