Repository navigation
Description
Summary
permission.bash deny rules are matched against the command string. Recursive file-reading commands (grep -r <pattern> <dir>, rg <pattern> <dir>, ls -R <dir>, find <dir> -type f) only name the directory in their command text, so they read and return the full contents of files whose names match deny patterns (e.g. *.env*, *.pem*, *.key*) without any rule matching.
A direct cat .env is correctly denied. grep -r "API_KEY" . returns the contents of .env — allowed, because no deny pattern matches the command string.
This is the bash-tool sibling of the grep/glob tool gap tracked (and auto-closed as stale) in #17073, with duplicates #35503 / #35684, and related to the wrapper-matching gap in #54008.
Reproduction (synthetic files, no real secrets)
- Configure:
"permission": { "bash": { "*": "allow", "*.env*": "deny", "*.key*": "deny" } }
- Create
secrets/fake-credentials.envandsecrets/server.key(synthetic content). cat secrets/fake-credentials.env→ denied ✔ (command string matches*.env*).grep -r "SYNTHETIC" secrets/→ allowed, returns the full contents of both files ✘ — the command string (grep -r "SYNTHETIC" secrets/) matches no deny pattern, yet the operation reads deny-matched files.
Verified 2026-10-17 on a live session (files created out-of-band via the file-write tool, then read by an allowed recursive grep).
Expected behavior
Either:
- (a) deny rules are also evaluated against the files a command will actually read (requires intercepting recursive reads — heavy), or
- (b) a documented, supported post-execution redaction/filter hook exists so configurations can strip deny-matched files' content from tool output before the model sees it (the approach proven by the community plugin
opencode-env-protectand requested in [FEATURE]: Protect .env files in grep/glob results, not just direct read #17073), or - (c) the limitation is documented explicitly next to the bash permission docs: filename-pattern denies are command-string matches only and do NOT protect file contents reached via recursive reads.
Actual behavior
Recursive reads silently bypass filename-pattern deny rules; denied-file contents enter the model context.
Security impact
Configurations that rely on *.env* / *.key* / *.pem* bash denies as a secret-exfiltration guard have a false sense of protection: an agent (or a prompt-injected agent) can exfiltrate secret file contents with a single allowed recursive grep, never naming the file.
Suggested fix direction
Post-execution output filtering keyed on the same permission patterns (option b) is the smallest change that closes the class: after a bash command completes, scan its output for content sourced from files matching deny patterns and redact/filter. This also covers #17073's grep/glob tool variant with one mechanism.
Environment
- OpenCode: current release channel (verified 2026-10-17)
- OS: Linux (Arch, bash)
Activity
opencode-agent commentedon Oct 17, 2026
Thanks for the clear write-up and the safe repro with fake files.
To restate the request: filename-based deny rules (
*.env*,*.key*, …) should also protect a file's contents when a shell command reads it without naming it, for examplegrep -r,rgorfind. That could be done by checking the files a command actually reads, by a supported way to redact them from output after the command finishes, or at least by documenting the limit next to the shell permission docs.Some context: shell permission rules match the parsed command text. They decide which commands can run, not which files those commands touch. A recursive grep is one of many ways around a filename deny (
python -c,xargs cat, globs, a script the model writes), so these rules are a guardrail, not a sandbox, and shouldn't be the only thing protecting secrets. We agree the docs could say this much more plainly. Maintainers will consider the request; I can't promise anything about whether or when it happens.In the meantime, on v2 (
@opencode/cli) a plugin can rewrite tool output through theexecute.aftertool hook, which is how plugins likeopencode-env-protectwork. One known limit is that shell output streamed while the command runs is saved before that hook runs (#51937).Related issues:
bash -c,env, …) getting past bash deny rules (v1)opencode-agent commentedon Oct 18, 2026
Closing as working as intended. Shell permission rules match the command text the scanner produces, not the files a command ends up reading. A filename deny like
*.env*was never a sandbox for the shell tool:grep -r,rg,find -exec cat,python -cor a generated script can all read those files. The permissions docs note that shell runs with the host user's filesystem, process and network authority, and recommend a narrow shell allowlist.If you need content-level protection, use a narrow
shellallowlist instead of*: allowplus filename denies, or run OpenCode in a sandbox or container that cannot see the secrets.