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
A session directory containing .. loads project config from the un-normalized path: another directory's opencode.json (and its permissions) applies #55909
Version: opencode 2.0.22 (linux-x64). Not reproduced in 1.18.34
through run --dir.
What happens
When a session is created through the server API with a directory that
contains .., for example location.directory = "<base>/src/../proj",
opencode resolves project configuration by walking the path string as
given. It finds <base>/src/opencode.json, although src is not an
ancestor of the real directory <base>/proj, and applies its permission rules to the session. The canonical path <base>/proj does
not load it.
Security impact: a caller that passes an un-normalized path can, without
meaning to, put a session under a different project's permission policy.
A permissive opencode.json in a sibling directory then turns ask (or deny) into allow.
Minimal repro (scripted model; no real provider)
A scratch base dir with no opencode.json above it. Create <base>/proj (empty) and <base>/src/opencode.json with {"permission": {"*": "allow"}}.
Global config ($XDG_CONFIG_HOME/opencode/opencode.json): {"permission": {"*": "ask"}}, plus a provider pointing at a local
OpenAI-compatible server that always answers with one shell call, touch marker.txt.
POST /api/session with {"location": {"directory": "<base>/proj"}},
then POST /api/session/<id>/prompt with {"text": "go"}. Do the same
with "<base>/src/../proj".
For each session: GET /api/session/<id>/permission, and check
whether <base>/proj/marker.txt exists.
Result (measured 2026-10-15)
Canonical path: 1 pending permission request; the tool does not run.
src/../proj: 0 requests; the tool runs and marker.txt is written.
With src/opencode.json set to {"permission": {"shell": "deny"}} and
a global "*": "allow", it reverses: canonical runs the tool, while src/../proj is denied.
With no opencode.json in src/, both paths behave the same.
Expected: normalize the session directory (path.resolve, ideally
also realpath) before project discovery, config loading and permission
evaluation, so equivalent paths always get the same config. Optionally,
reject or warn on directories that are not normalized.
Thanks for the clear report. I reproduced this on 2.0.22, and it still happens on the latest dev (7049e5a).
Setup, in short:
<base>/src/opencode.json sets "*": "allow", and the global config sets "*": "ask".
A mock OpenAI-compatible model always asks for one shell call, touch marker.txt.
Two sessions are created through POST /api/session, one with <base>/proj and one with <base>/src/../proj, and each is sent a prompt.
Result:
<base>/proj: one pending shell permission request, and marker.txt is not created.
<base>/src/../proj: no permission request, and the tool runs and creates marker.txt.
So the sibling directory's opencode.json permissions apply when the path isn't normalized. A likely cause (not yet confirmed) is that the session location directory is only normalized on Windows. On Linux and macOS it is kept exactly as given. I didn't test the reversed deny case, but it is likely the same cause.
The script below reproduces it with opencode from PATH, or with a source checkout if you set OC_DEV.
Taking this one. The location directory should be normalized once at the boundary where project/config discovery walks it (the bot notes only Windows normalizes today, so Linux/macOS keep .. verbatim) — I'll normalize there and pin it with a test that a .. session directory resolves the canonical project's config/permissions.
Activity
opencode-agent commentedon Oct 16, 2026
Thanks for the clear report. I reproduced this on 2.0.22, and it still happens on the latest
dev(7049e5a).Setup, in short:
<base>/src/opencode.jsonsets"*": "allow", and the global config sets"*": "ask".shellcall,touch marker.txt.POST /api/session, one with<base>/projand one with<base>/src/../proj, and each is sent a prompt.Result:
<base>/proj: one pendingshellpermission request, andmarker.txtis not created.<base>/src/../proj: no permission request, and the tool runs and createsmarker.txt.So the sibling directory's
opencode.jsonpermissions apply when the path isn't normalized. A likely cause (not yet confirmed) is that the session location directory is only normalized on Windows. On Linux and macOS it is kept exactly as given. I didn't test the reverseddenycase, but it is likely the same cause.The script below reproduces it with
opencodefrom PATH, or with a source checkout if you setOC_DEV.Reproduction script (repro.sh)
Run it:
bash repro.shFinn763 commentedon Oct 16, 2026
Taking this one. The location directory should be normalized once at the boundary where project/config discovery walks it (the bot notes only Windows normalizes today, so Linux/macOS keep
..verbatim) — I'll normalize there and pin it with a test that a..session directory resolves the canonical project's config/permissions.