Skip to content

fix: canonicalize filepath in Instance.containsPath() for symlink support#16665

Open
jmylchreest wants to merge 2 commits intoanomalyco:devfrom
jmylchreest:fix/containsPath-symlink-canonicalization
Open

fix: canonicalize filepath in Instance.containsPath() for symlink support#16665
jmylchreest wants to merge 2 commits intoanomalyco:devfrom
jmylchreest:fix/containsPath-symlink-canonicalization

Conversation

@jmylchreest
Copy link

@jmylchreest jmylchreest commented Mar 9, 2026

Issue for this PR

Closes #16660

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Instance.containsPath() does a lexical prefix check comparing Instance.directory against a filepath argument. After #16651, Instance.directory is always canonical (symlink-resolved), but the filepath passed by callers (bash.ts, external-directory.ts, file/index.ts) is not canonicalized.

This mismatch causes symlinked paths to fail the containment check — triggering unnecessary external_directory permission prompts or rejecting valid file reads/trees via symlinked paths. The issue pre-existed #16651 but became more pronounced since Instance.directory is now always canonical while inputs remained raw.

Fix: call Filesystem.resolve(filepath) inside containsPath() before comparing, so both sides use canonical paths.

Depends on #16651 for Filesystem.resolve() symlink resolution.

How did you verify your code works?

  • bun test test/file/path-traversal.test.ts — 20 pass (15 existing + 5 new)
  • bun test test/util/filesystem.test.ts — 55 pass
  • New tests cover: symlinks inside project, external symlinks to project, symlinks escaping project (security), dangling symlinks, and symlink cycles (ELOOP)

Screenshots / recordings

N/A — no UI changes.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions bot added the needs:compliance This means the issue will auto-close after 2 hours. label Mar 9, 2026
Instance.containsPath() compared Instance.directory (always canonical
after anomalyco#16651) against an uncanonicalized filepath argument. When callers
passed a symlinked path, the lexical comparison failed even though the
path resolved to a location inside the project.

This caused false negatives in bash.ts, external-directory.ts, and
file/index.ts — triggering unnecessary external_directory permission
prompts or rejecting valid file reads via symlinked paths.

Fix: resolve the filepath through Filesystem.resolve() before comparing,
so both sides use canonical paths.

Adds tests for: symlinks inside project, external symlinks to project,
symlinks escaping project, dangling symlinks, and symlink cycles.

Fixes anomalyco#16660
@jmylchreest jmylchreest force-pushed the fix/containsPath-symlink-canonicalization branch from 728e91a to 540f3d3 Compare March 9, 2026 01:37
@github-actions github-actions bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Mar 9, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@jmylchreest
Copy link
Author

The failing tests should resolve once #16651 is also applied

…omalyco#16651

Tests for external symlinks, symlinks escaping project, and ELOOP
propagation probe at runtime whether Filesystem.resolve() follows
symlinks. They skip gracefully on dev (before anomalyco#16651) and activate
once realpathSync lands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Instance.containsPath() compares mixed canonical/non-canonical paths

1 participant