Skip to content

fix: accept files under Windows drive-root projects (Z:\, C:\) - #415

Closed
Kamilslaoui93 wants to merge 1 commit into
colbymchenry:mainfrom
Kamilslaoui93:fix/windows-drive-root-path-validation
Closed

Kamilslaoui93 wants to merge 1 commit into
colbymchenry:mainfrom
Kamilslaoui93:fix/windows-drive-root-path-validation

Conversation

@Kamilslaoui93

Copy link
Copy Markdown

Summary

codegraph index fails on any project located at a Windows drive root (e.g. C:\, Z:\, or a mapped network drive). Every file is rejected by the path-traversal check, indexing produces zero results, and the log fills with Path traversal blocked in batch reader for every file in the project.

Root cause

path.resolve('Z:\') returns Z:\ — a path that already ends with path.sep. The existing check appended another separator:

resolved.startsWith(normalizedRoot + path.sep)

…producing Z:\ for drive roots. No real file path under the drive root starts with Z:\, so the check fails for every file. For non-root projects (C:\Users\foo\proj) the appended separator is correct and the check works; the bug is specific to roots whose resolved form already terminates with path.sep.

Same bug existed in isPathWithinRoot and isPathWithinRootReal.

Fix

Extract a small ensureTrailingSep() helper that appends path.sep only when missing, and use it at all three call sites in src/utils.ts. Behaviour is unchanged for every non-drive-root project.

Reproduction

PS Z:\> npx @colbymchenry/codegraph index
# ...
[CodeGraph] Path traversal blocked in batch reader { filePath: 'configuration.yaml' }
[CodeGraph] Path traversal blocked in batch reader { filePath: 'automations.yaml' }
# ... repeated for every file, "Indexed 0 files"

With this patch applied locally I get:

Indexed 768 files
20,783 nodes, 20,015 edges in 2m 51s

Tests

Added 4 cases to __tests__/security.test.ts under a new Path-within-root helpers — Windows drive-root regression describe block:

  • Two Windows-gated cases that exercise the drive-root path (Z:\), skipped on POSIX and on Windows runners where Z:\ isn't mounted.
  • Two cross-platform cases asserting that legitimate traversal attempts (../escape.txt) are still rejected — guarding against the obvious failure mode of an over-eager fix.

Full __tests__/security.test.ts passes locally on Windows (41 passed, 2 POSIX-gated skipped).

Test plan

  • npx vitest run __tests__/security.test.ts — 41 passed
  • npx tsc --noEmit — clean
  • End-to-end: npx codegraph index on a project at Z:\ (mapped network drive) — 768 files indexed, 0 path-traversal warnings

`path.resolve('Z:\')` returns `Z:\` — a path already terminated with
the OS separator. The existing path-within-root check appended another
`path.sep`, producing `Z:\`, which no file under the root could ever
start with. Every file got rejected as a path-traversal attempt, so
`codegraph index` on a project at a drive root (or a mapped network
drive) emitted "Path traversal blocked in batch reader" for every file
and indexed nothing.

Same bug in `isPathWithinRoot` and `isPathWithinRootReal`. Fixed by
extracting an `ensureTrailingSep()` helper that only appends a
separator if one isn't already present.

Regression tests in __tests__/security.test.ts:
- Windows-gated, run only if Z:\ exists on the runner
- Cross-platform traversal-rejection tests still pass (existing
  behavior preserved)
@ZyphrZero

Copy link
Copy Markdown

PR describes all three functions fixed, but only two were actually changed:

  • validatePathWithinRoot ✓
  • isPathWithinRoot ✓
  • isPathWithinRootReal(src/utils.ts:142) not changed — still realRoot + path.sep

isPathWithinRootReal internally calls isPathWithinRoot (fixed), so the logical path check for the drive root will pass; However, the subsequent realpath secondary verification (realPath.startsWith(realRoot + path.sep)) will still reproduce the same bug on the drive root and does not close the project scenario with symlink.

colbymchenry added a commit that referenced this pull request Oct 27, 2026
A project at the root of a drive, such as a mapped network drive Z:\, a
subst drive or a UNC share root, indexed nothing on Windows: every file
was refused as "Path traversal blocked", and code reads under the root
were refused too. The containment check appended a separator to the root
before comparing, and a root such as C:\ already ends with one, so the
prefix became C:\\ and no path under the root matched. The dead code
report's default file reader had its own copy of the same check, so it
treated every file of such a project as unreadable.

The check now adds a separator only when the root doesn't already end
with one. The dead code reader uses the shared lexical containment check
instead of its own copy.

Validation on Windows 11:
- subst R: onto a two-file fixture, then codegraph init -y --force R:\:
  main indexes 0 files ("all 2 files had errors", 2 "Path traversal
  blocked"), and validatePathWithinRoot(R:\, a.ts) is null. The fix
  indexes both files (6 nodes, 6 edges) and getCode returns both
  functions' source. The mapping was removed after each run.
- Dead code report against one R:\ index: main's reader drops the unused
  function as unreadable; the fix reports it.
- Tests: 3 new tests fail on main's source and pass with the fix: a file
  under the filesystem root (this drive's root here, / on POSIX), path
  math under Q:\ and \\server\share\, and read-only realpaths under C:\.

Reported in PR #415 by @Kamilslaoui93; this reimplements the fix
independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry

Copy link
Copy Markdown
Owner

Thanks @Kamilslaoui93, you pinned down exactly why a project at a drive root indexed nothing: the root already ends with a separator, so appending another one matched no file. We landed the fix in #2931, reimplemented independently and credited to you in the changelog. It also fixes the dead-code reader, which had its own copy of the same check, and its tests run without a mapped drive. Closing this in favor of it; it ships in the next release.

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.

3 participants