Skip to content

Potential path traversal in file read/write tools #11703

Closed

Description

@ryan-mt

Summary

The file read and write tools use path.resolve/path.join with user input, which could potentially allow path traversal attacks in certain scenarios, particularly when symlinks are involved.

Locations

  • packages/opencode/src/tool/read.ts:26
  • packages/opencode/src/tool/write.ts:26

Issue

// read.ts:26
let filepath = params.filePath
if (!path.isAbsolute(filepath)) {
  filepath = path.resolve(Instance.directory, filepath)
}
await assertExternalDirectory(ctx, filepath, { bypass: Boolean(ctx.extra?.["bypassCwdCheck"]) })

While assertExternalDirectory provides protection, the check uses Filesystem.contains() which relies on string-based path comparison via path.relative(). This approach has known weaknesses:

  1. Symlink bypass: If a directory within the project contains a symlink pointing outside, path.resolve will follow it but Filesystem.contains() won't detect it
  2. Race condition (TOCTOU): The path is validated but could change between validation and use

Impact

  • Severity: Medium (mitigated by existing checks)
  • Type: Path Traversal
  • Conditions: Requires symlinks or specific timing

Suggested Fix

  1. Use fs.realpath() to resolve symlinks before checking containment:
let filepath = params.filePath
if (!path.isAbsolute(filepath)) {
  filepath = path.resolve(Instance.directory, filepath)
}
// Resolve symlinks to get actual path
const realFilepath = await fs.realpath(filepath).catch(() => filepath)
await assertExternalDirectory(ctx, realFilepath, {...})
  1. Consider using fs.openSync with O_NOFOLLOW flag for symlink-sensitive operations

Activity

  1. github-actions commented on Apr 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 90 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