Skip to content

Permission glob matcher doesn't match deep absolute paths; only **/<basename>/** works #25097

Closed as not planned

Description

@bmeindl

Summary

The permission-glob matcher in opencode 1.14.30 doesn't match patterns of the form **/<segment>/<segment>/<dir>/** against absolute resolved paths like /private/tmp/<segment>/<segment>/<dir>/<file>. Only single-segment patterns like **/<basename>/** match reliably. Patterns starting with a literal / (e.g. /tmp/scope/**) also don't match.

This makes it impossible to express "allow exactly this absolute path subtree" precisely — you have to fall back to **/<basename>/** which over-matches if the basename appears elsewhere in the filesystem.

Environment

  • opencode 1.14.30 (curl install, macOS 25.3.0)
  • Permission engine in OPENCODE_CONFIG_CONTENT JSON

Reproduce

mkdir -p /tmp/agent-out
rm -f /tmp/agent-out/x.txt

CFG_FAILS='{"permission":{"edit":{"*":"deny","**/private/tmp/agent-out/**":"allow"},"write":{"*":"deny","**/private/tmp/agent-out/**":"allow"},"read":"deny","bash":"deny","external_directory":"allow"},"tools":{"bash":false,"edit":true,"write":true,"web_fetch":true,"web_search":true}}'

echo "Use the write tool to create /tmp/agent-out/x.txt with content X. Reply DONE." | \
  OPENCODE_CONFIG_CONTENT="$CFG_FAILS" opencode run --model <any-model> --dangerously-skip-permissions
ls /tmp/agent-out/x.txt   # ❌ does not exist

Same setup but with **/agent-out/** instead of **/private/tmp/agent-out/**:

CFG_WORKS='{"permission":{"edit":{"*":"deny","**/agent-out/**":"allow"},"write":{"*":"deny","**/agent-out/**":"allow"},...}}'
# ✅ writes the file

Tested patterns (all targeting /private/tmp/agent-out/x.txt):

Pattern Result
/private/tmp/agent-out/** ❌ blocked
private/tmp/agent-out/** ❌ blocked
**/private/tmp/agent-out/** ❌ blocked
**/tmp/agent-out/** ❌ blocked (mostly; sometimes flaky)
**/agent-out/** ✅ matches reliably
**/agent-out/* ✅ matches reliably

The agent-side error message confirms the rule IS in opencode's evaluated rules list, just doesn't match:

"relevant rules [{...},{"permission":"edit","pattern":"*","action":"deny"},
{"permission":"edit","pattern":"**/private/tmp/agent-out/**","action":"allow"}]"

Expected

Per the docs (https://opencode.ai/docs/permissions/), ** should match any number of path segments and last-match-wins. So **/private/tmp/agent-out/** should match /private/tmp/agent-out/x.txt and the allow should override the catch-all deny.

Suggested investigation

  • The matcher likely doesn't anchor ** correctly when the path starts with /. Maybe it's stripping the leading / from the input path but then the pattern's leading **/ requires non-empty segments, and private/tmp/... has zero segments before private from the matcher's POV.
  • Or the matcher treats multiple ** differently from one — **/dir/** works, **/seg/dir/** doesn't.
  • Test against Bun's built-in Glob.match if that's what's being used; or wherever the rule resolution happens in src/permission/.

Workaround

Use **/<basename>/** in the config and enforce the actual path scope before spawning opencode (we validate against an ALLOWED_ROOTS list at the spawn boundary, then trust opencode for defense-in-depth).

Impact

For folks building MCP wrappers / sandboxes around opencode (e.g. our sub-agent-mcp), this means opencode's permission engine can't be the primary security gate for path scoping — you have to validate paths in your own code first. That's a reasonable defense-in-depth posture but probably not what the docs imply.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 30, 2026
  2. github-actions commented on Apr 30, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your issue is specifically about the **/seg1/seg2/dir/** multi-segment globstar not matching even relative paths, that would be a distinct bug worth keeping open separately.

  3. bmeindl commented on May 1, 2026

    @bmeindl
    Author

    Re-ran the original repro today on opencode 1.14.30 (same version as in the OP) on macOS 26.3 — and the multi-segment globstar patterns I had reported as failing now match correctly. Only the leading-slash absolute patterns still fail, which lines up exactly with #20045's diagnosis (edit receives paths relative to Instance.worktree, so a pattern starting with / can't match the un-slashed input).

    Fresh table, target = /tmp/agent-out/x.txt, same OPENCODE_CONFIG_CONTENT shape as the OP, only the pattern varies:

    Pattern Result
    **/agent-out/** ✅ matches
    **/tmp/agent-out/** ✅ matches (was reported failing in OP — now works)
    **/private/tmp/agent-out/** ✅ matches (was reported failing in OP — now works)
    /tmp/agent-out/** ❌ blocked
    /private/tmp/agent-out/** ❌ blocked

    So my original report of a distinct multi-segment-globstar bug doesn't reproduce anymore. The remaining failures are the same root cause as #20045 (and the broader unification ask in #22465).

    Happy for this to be closed as duplicate of #20045 — feel free to close, or leave it linked from there if useful as another data point. Thanks for the bot's triage hint, that pointed me at the right cluster.

  4. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  5. github-actions commented on Jul 14, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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