Skip to content

external_directory permission bypassed for cross-drive paths on Windows #27596

Closed as not planned

Description

@Jia-Yuhanoob

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

  1. Start OpenCode in workspace D:\opencode\test0 (no .git)
  2. Configuration: defaults (no permission field in opencode.jsonc)
  3. Try to read or write a file on C:\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

  1. github-actions commented on May 14, 2026

    @github-actions
    Contributor

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

    The root cause you identified (path.relative() returning an absolute path across drives) may be the underlying cause for several of these reports.

  2. bhill298 commented on Jun 8, 2026

    @bhill298

    I have also had this issue. I would appreciate this being fixed.

  3. github-actions commented on Aug 8, 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