Skip to content

Any read or edit permission rule written as an absolute path (~/.ssh, ~/.aws, /etc/hosts, etc.) silently never matches, including MDM managed rules #48049

Closed as not planned

Description

@nlscott

Description

Environment

  • opencode 1.18.29
  • macOS (Apple Silicon), zsh
  • Config delivered via MDM managed preferences (Jamf Pro), ai.opencode.managed domain
  • Resolved plist on device: /Library/Managed Preferences/<user>/ai.opencode.managed.plist (root:wheel, 644)
  • ~/.config/opencode/opencode.json contains only a provider block, no permission key
  • 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_hosts and directory listing of ~/.ssh both succeed with no prompt.
  • Step 5: .env and credentials.json reads 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.ts expand() rewrites ~/.ssh/** to /Users/<user>/.ssh/** (absolute).
  • packages/opencode/src/tool/read.ts and tool/edit.ts call
    ctx.ask({ permission: "read", patterns: [path.relative(instance.worktree, filepath)] }),
    so the subject is worktree-relative: .ssh/known_hosts when started from $HOME, ../../.ssh/known_hosts from a project.
  • packages/core/src/util/wildcard.ts match() 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 under read and edit is silently ignored: ~/.ssh, ~/.aws, ~/.kube, ~/.netrc, ~/.npmrc, ~/.docker, ~/.zshrc, /etc/hosts, /etc/sudoers, /Library/LaunchDaemons, etc.
  • Admins get no signal. opencode debug config shows 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

  1. Deploy the plist above via MDM (or drop the equivalent permission block into ~/.config/opencode/opencode.json).
  2. Confirm it loaded: opencode debug config shows permission.read["~/.ssh/**"] = "deny".
  3. Start opencode in $HOME (or any directory).
  4. Ask the agent to read ~/.ssh/known_hosts and to list ~/.ssh.
  5. In the same session, ask it to read a .env file and a credentials.json in a temp directory.

Image Image Image

Screenshot and/or share link

No response

Operating System

macOS 26.6.2 (25G83)

Terminal

Apple Terminal

Activity

  1. github-actions commented on Sep 9, 2026

    @github-actions
    Contributor

    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 read tool specifically, but the underlying bug is already tracked in the issues above.

  2. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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