Repository navigation
Description
Description
Bug: external_directory permission bypassed for cross-drive paths on Windows
Summary
On Windows, when the workspace is on a different drive (e.g., D:\), tools like read, write, edit, grep, and glob bypass the external_directory permission check for paths on other drives (e.g., C:\). The root cause is a cross-drive bug in AppFileSystem.contains().
Version
Reproduced on v1.14.49 and v1.14.50 (Desktop, Windows 11).
Root Cause
packages/core/src/filesystem.ts:241-243:
export function contains(parent: string, child: string) {
return !relative(parent, child).startsWith("..")
}On Windows, Node.js path.relative() behaves differently across drives:
| Scenario | path.relative() result |
startsWith("..") |
contains() returns |
|---|---|---|---|
Same drive: D:\opencode\test0 → D:\test.md |
..\..\test.md |
true |
false ✅ correct |
Cross drive: D:\opencode\test0 → C:\Users\...\nDoc |
C:\Users\...\nDoc (absolute) |
false |
true ❌ BUG |
When drives differ, path.relative returns the full absolute path of the target, which does NOT start with ... This causes contains() to incorrectly return true, making containsPath() (in instance-context.ts) treat cross-drive paths as "inside the project" — completely skipping the external_directory permission gate.
The issue is also present in normalizePathPattern() and potentially affects overlaps() (same path.relative pattern).
Affected Code
| File | Line | Issue |
|---|---|---|
packages/core/src/filesystem.ts |
241-243 | contains() cross-drive false positive |
packages/core/src/filesystem.ts |
235-239 | overlaps() same vulnerability |
packages/opencode/src/util/filesystem.ts |
171-173 | Duplicate contains() (identical bug) |
packages/opencode/src/project/instance-context.ts |
18-24 | containsPath() relies on contains() |
packages/opencode/src/tool/external-directory.ts |
16-45 | assertExternalDirectoryEffect() skipped when containsPath returns true |
Reproduction
- Start OpenCode in workspace
D:\opencode\test0(no.git) - Configuration: defaults (no
permissionfield inopencode.jsonc) - Try to
readorwritea file onC:\Users\<user>\Desktop\:
| Action | Target | Result |
|---|---|---|
| Write | D:\test.md (same drive, outside workspace) |
❌ Rejected (correct) |
| Write | C:\Users\...\Desktop\test.md |
✅ Allowed (bypass!) |
| Write | C:\Users\...\Desktop\nDoc\test.md |
✅ Allowed (bypass!) |
| Write | C:\ProgramData\test.md |
✅ Allowed (bypass!) |
Suggested Fix
export function contains(parent: string, child: string) {
// Windows: paths on different drives cannot be contained
if (process.platform === "win32") {
const parentParsed = path.parse(parent)
const childParsed = path.parse(child)
if (parentParsed.root.toLowerCase() !== childParsed.root.toLowerCase()) {
return false
}
}
return !relative(parent, child).startsWith("..")
}Same fix should be applied to overlaps() and the duplicate contains() in packages/opencode/src/util/filesystem.ts.
Related Issues
- #25097 — Permission glob matcher doesn't match deep absolute paths
- #25268 — glob tool ignores permission rules for external drives/paths
- #24468 — CRITICAL: external_directory bypassed on Windows
Plugins
No response
OpenCode version
v1.14.49 and v1.14.50 (Desktop, Windows 11)
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
windows11
Terminal
No response
Activity
github-actions commentedon May 14, 2026
This issue might be a duplicate of existing issues. Please check:
external_directorybypass for cross-drive paths on Windows (same-drive blocks correctly, cross-drive bypasses permission check)$env:*/*may silently expand to drive root and BYPASSexternal_directorypermission, causing entire windows drive got destroyed #24468: Related —external_directorypermission bypass on Windows causing access to entire driveThe root cause you identified (
path.relative()returning an absolute path across drives) may be the underlying cause for several of these reports.bhill298 commentedon Jun 8, 2026
I have also had this issue. I would appreciate this being fixed.
github-actions commentedon Aug 8, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.