Repository navigation
fix(core): a project path typed in another letter case stays inside the project - #4514
Conversation
There was a problem hiding this comment.
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)
What this fixes
isSafePath(andresolveWithinProject) compared paths resolved withrealpathSync, 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 withEISDIR(some Windows RAM disks), it falls back torealpathSync, 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 throwingEISDIR, paths still resolve, and the outside path is still refused.Results:
safePath.test.ts14 (1 skipped on Linux),safePath.eisdir.test.ts1. Removing theEISDIRfallback fails the new test.