Skip to content

fix(core): a project path typed in another letter case stays inside the project - #4514

Merged
miguel-heygen merged 2 commits into
mainfrom
fix/core-safe-path-real-letter-case
Sep 26, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
fix/core-safe-path-real-letter-case

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What this fixes

isSafePath (and resolveWithinProject) compared paths resolved with realpathSync, which keeps the letter case the caller typed. On a case-insensitive disk (the macOS default, and Windows) a path inside the project typed in another case, or a project path typed in another case, compared as outside and was refused.

Now both sides resolve with realpathSync.native, which returns the case on disk, so containment is decided on the real path. Symlink resolution and the handling of not-yet-existing trailing segments are unchanged. On volumes where the native call refuses with EISDIR (some Windows RAM disks), it falls back to realpathSync, as before this change.

Tests

  • safePath.test.ts: an in-project path typed in another letter case is admitted, and a path outside stays refused. It runs only on case-insensitive disks (macOS, Windows) and skips on Linux. Checked on macOS against the file on main: main refuses both in-project paths, this change admits them, the outside path stays refused.
  • safePath.eisdir.test.ts: with the native call throwing EISDIR, paths still resolve, and the outside path is still refused.

Results: safePath.test.ts 14 (1 skipped on Linux), safePath.eisdir.test.ts 1. Removing the EISDIR fallback fails the new test.

@terencecho terencecho left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 88aa3e2f against #4514's own merge base. Both the base and target now use native realpath canonicalization, while the existing ancestor walk still handles not-yet-created descendants and rejects symlinks outside the project. The EISDIR branch falls back to the former realpath behavior instead of making affected volumes unusable. I found no diff-scoped security or correctness blocker.

The case-variant test is conditional on a case-insensitive filesystem, so it skipped on my Linux worktree. The same-head Windows CI core suite subsequently ran it without a skip (15/15 in safePath.test.ts, plus 1/1 in safePath.eisdir.test.ts); the author also reports exercising it on macOS, which I did not independently repeat. The EISDIR fallback test passes but verifies same-case paths only: on a volume where native realpath throws EISDIR, the original differently-cased-path limitation can remain. That limitation is disclosed in the PR's fallback description and is not a new regression; a mixed-case fallback fixture would make the supported boundary clearer. Focused Node-environment tests passed (15 passing, one platform skip). With an offline, --ignore-scripts install, a separate local run in the package's default jsdom environment failed both the pre-existing safePath suite and the new EISDIR suite at import with ERR_UNKNOWN_BUILTIN_MODULE: node:; same-head CI's normal core test step passed, so I cannot attribute that local setup failure to this diff. I did not exercise a Windows RAM disk or run the full package suite locally.

Verdict: APPROVE at this head.

— Review by tai (pr-review)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Sep 26, 2026
Merged via the queue into main with commit 259f65c Sep 26, 2026
70 checks passed
@miguel-heygen
miguel-heygen deleted the fix/core-safe-path-real-letter-case branch September 26, 2026 03:13
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.

2 participants