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
{{ message }}
Repository navigation
Shell redirect targets bypass the external_directory permission #32628
When the shell tool scans a command for out-of-project paths, it gates them through the external_directory permission (default ask) — the same boundary the native write/edit/read tools enforce. But the scan skips redirection nodes (packages/opencode/src/tool/shell.ts: … || item.type === "redirection") continue), and there is no redirect-target handling. As a result a shell redirect target is never checked against external_directory:
cp foo /tmp/out → external_directory is asked ✅
cat foo > /tmp/out, env > /tmp/out, echo k >> ~/.ssh/authorized_keys → external_directory is never consulted; only the bash permission fires, and the redirect silently writes outside the project.
So a one-token shell redirect bypasses the project boundary that every other write path in opencode respects.
Reproduce (on current dev): with external_directory set to ask/deny, cp src /external/x prompts but env > /external/x does not, and the write to /external/x goes through.
Impact: an out-of-project write/exfiltration via redirection is not gated by external_directory, inconsistent with cp/mv/native tools.
A fix is proposed in the linked PR (feed file_redirect destinations through the existing argPath → containsPath → scan.dirs pipeline; skip fd-dups / here-docs / std-stream pseudo-devices).
Related to #31485, which proposes replacing tree-sitter-bash with a structural parser to fix this whole class of permission bypasses. This issue is one concrete instance of that class — shell redirect targets aren't checked against external_directory — and the linked PR is a small, targeted fix using the existing arg-scan pipeline. It stands on its own and doesn't depend on the larger parser rewrite, so I kept it separate rather than folding it into #31485.
Re #18396: that issue is the broader case where a bash subprocess writes outside permitted dirs as a side effect (e.g. python3 -m venv /home/user/venv). A forked process does its own I/O, so that can't be caught by parsing the command and is out of scope for this fix.
This issue is the narrower, parseable slice: a shell redirect target (env > /outside, cat foo >> ~/.ssh/...) that opencode's own command scanner already inspects for path arguments but skips for redirections. The linked PR closes exactly that gap by routing redirect targets through the existing external_directory scan — it does not (and can't, at the parse layer) resolve the general subprocess-side-effect case in #18396. Keeping it separate so the PR's scope stays honest; happy to re-link to #18396 if a maintainer prefers tracking it there.
Activity
github-actions commentedon Jun 17, 2026
This issue might be a duplicate of existing issues. Please check:
external_directorypermission — same class of bypass via shell executionwarmjademe commentedon Jun 17, 2026
Related to #31485, which proposes replacing tree-sitter-bash with a structural parser to fix this whole class of permission bypasses. This issue is one concrete instance of that class — shell redirect targets aren't checked against
external_directory— and the linked PR is a small, targeted fix using the existing arg-scan pipeline. It stands on its own and doesn't depend on the larger parser rewrite, so I kept it separate rather than folding it into #31485.warmjademe commentedon Jun 17, 2026
Re #18396: that issue is the broader case where a bash subprocess writes outside permitted dirs as a side effect (e.g.
python3 -m venv /home/user/venv). A forked process does its own I/O, so that can't be caught by parsing the command and is out of scope for this fix.This issue is the narrower, parseable slice: a shell redirect target (
env > /outside,cat foo >> ~/.ssh/...) that opencode's own command scanner already inspects for path arguments but skips for redirections. The linked PR closes exactly that gap by routing redirect targets through the existingexternal_directoryscan — it does not (and can't, at the parse layer) resolve the general subprocess-side-effect case in #18396. Keeping it separate so the PR's scope stays honest; happy to re-link to #18396 if a maintainer prefers tracking it there.github-actions commentedon Aug 17, 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.