Repository navigation
Description
Description
Environment
- opencode 1.18.29
- macOS (Apple Silicon), zsh
- Config delivered via MDM managed preferences (Jamf Pro),
ai.opencode.manageddomain - Resolved plist on device:
/Library/Managed Preferences/<user>/ai.opencode.managed.plist(root:wheel, 644) ~/.config/opencode/opencode.jsoncontains only a provider block, nopermissionkey- Session started from
$HOME(worktree =/Users/<user>), also reproduced from a project directory
Summary
We are piloting the documented managed-settings path (Config > Managed settings > macOS managed preferences) to enforce an org-wide deny list for secrets locations. The profile installs and merges correctly, opencode debug config shows every rule, and the rules are correctly marked non-overridable. But every permission.read and permission.edit rule written with a ~/ or absolute path silently never matches, so the agent can read ~/.ssh, ~/.aws/credentials, etc. without a prompt. Only the **/-prefixed rules in the same block are enforced.
This is fail-open for exactly the rules an enterprise would deploy.
Managed config (relevant portion)
<key>permission</key>
<dict>
<key>*</key>
<string>ask</string>
<key>read</key>
<dict>
<key>**/.env</key> <string>deny</string>
<key>**/.env.*</key> <string>deny</string>
<key>**/secrets/**</key> <string>deny</string>
<key>**/credentials.json</key> <string>deny</string>
<key>~/.ssh/**</key> <string>deny</string>
<key>~/.aws/credentials</key> <string>deny</string>
<key>~/.aws/config</key> <string>deny</string>
<key>~/.kube/config</key> <string>deny</string>
<key>~/.netrc</key> <string>deny</string>
<key>~/.docker/config.json</key><string>deny</string>
<key>~/.npmrc</key> <string>deny</string>
</dict>
<key>edit</key>
<dict>
<key>~/.ssh/**</key> <string>deny</string>
<key>~/.zshrc</key> <string>deny</string>
<key>/etc/hosts</key> <string>deny</string>
<key>/etc/sudoers</key> <string>deny</string>
<!-- more ~/ and /Library/... entries omitted -->
</dict>
</dict>Expected
Steps 4 and 5 are both denied. Per the Permissions docs ("Home Directory Expansion": ~/projects/* -> /Users/username/projects/*), ~/ patterns are supported for permission rules, and the managed-settings docs say these rules "are enforced automatically."
Actual
- Step 4: read of
~/.ssh/known_hostsand directory listing of~/.sshboth succeed with no prompt. - Step 5:
.envandcredentials.jsonreads are correctly blocked. The deny message returned to the model shows the ruleset it evaluated, which includes the expanded absolute rule that should have matched in step 4:
{"permission":"read","pattern":"/Users/<user>/.ssh/**","action":"deny"}
{"permission":"read","pattern":"/Users/<user>/.aws/credentials","action":"deny"}
{"permission":"read","pattern":"**/.env","action":"deny"}
{"permission":"read","pattern":"**/credentials.json","action":"deny"}
So the rule is loaded and expanded, it just never matches the subject.
Root cause (from dev)
packages/opencode/src/permission/index.tsexpand()rewrites~/.ssh/**to/Users/<user>/.ssh/**(absolute).packages/opencode/src/tool/read.tsandtool/edit.tscall
ctx.ask({ permission: "read", patterns: [path.relative(instance.worktree, filepath)] }),
so the subject is worktree-relative:.ssh/known_hostswhen started from$HOME,../../.ssh/known_hostsfrom a project.packages/core/src/util/wildcard.tsmatch()anchors the regex with^...$.
An anchored absolute pattern can never match a relative subject. **/-prefixed patterns work only because ** happens to absorb the leading ../ segments.
Note this is not limited to out-of-worktree files: with the worktree set to $HOME the subject is .ssh/known_hosts with no ../, and it still does not match.
Impact
- Every
~/and absolute-path deny rule underreadandeditis silently ignored:~/.ssh,~/.aws,~/.kube,~/.netrc,~/.npmrc,~/.docker,~/.zshrc,/etc/hosts,/etc/sudoers,/Library/LaunchDaemons, etc. - Admins get no signal.
opencode debug configshows the rule present and enforced, and there is no warning that the pattern shape can never match. - Makes the MDM managed-settings feature unsafe to rely on for secrets protection until fixed.
Plugins
n/a
OpenCode version
1.18.29
Steps to reproduce
Steps to reproduce
- Deploy the plist above via MDM (or drop the equivalent
permissionblock into~/.config/opencode/opencode.json). - Confirm it loaded:
opencode debug configshowspermission.read["~/.ssh/**"] = "deny". - Start opencode in
$HOME(or any directory). - Ask the agent to read
~/.ssh/known_hostsand to list~/.ssh. - In the same session, ask it to read a
.envfile and acredentials.jsonin a temp directory.
Screenshot and/or share link
No response
Operating System
macOS 26.6.2 (25G83)
Terminal
Apple Terminal
Activity
github-actions commentedon Sep 9, 2026
This issue might be a duplicate of existing issues. Please check:
These issues describe the same root cause (tools submitting worktree-relative paths to the permission matcher, causing absolute/~ patterns to never match) and the same fail-open security impact. Your report adds useful context around MDM-managed rules and the
readtool specifically, but the underlying bug is already tracked in the issues above.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊