-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Description
Description
After #16651, Instance.directory is always the canonical (symlink-resolved) path. But the filepath argument to containsPath() comes from various tool callers (bash.ts, external-directory.ts, file/index.ts) and is not canonicalized. Similarly, Instance.worktree may not be canonical.
This means a symlinked path passed to containsPath() won't match the canonical Instance.directory, causing:
bash.ts:89,129— false negatives on the boundary check, triggering unnecessaryexternal_directorypermission prompts for paths that are actually inside the projectexternal-directory.ts:17— same false negative, prompting for permission when none is neededfile/index.ts:503,585—File.read()andFile.tree()incorrectly reject symlinked paths within the project
The existing TODO comments at file/index.ts:501 and :583 acknowledge this as a known limitation ("lexical containment check"). #16651 makes it slightly more pronounced because Instance.directory is now always canonical while inputs remain uncanonicalized.
A fix would be to canonicalize the filepath argument inside containsPath():
containsPath(filepath: string) {
const resolved = Filesystem.resolve(filepath)
if (Filesystem.contains(Instance.directory, resolved)) return true
if (Instance.worktree === "/") return false
return Filesystem.contains(Instance.worktree, resolved)
}Steps to reproduce
- Create a symlink to a project directory:
ln -s ~/myproject ~/myproject-link - Run
opencodefrom the symlinked path:cd ~/myproject-link && opencode - Use a tool that accesses a file via its symlinked path (e.g., bash tool with
cwdset to the symlink path, or file read via symlink) - Observe unnecessary
external_directorypermission prompts for paths that are actually inside the project
Operating System
macOS, Linux (anywhere symlinks are common)