Skip to content

Fix isPathWithinAllowedDirectories for UNC paths on Windows#3921

Open
Christian-Sidak wants to merge 2 commits intomodelcontextprotocol:mainfrom
Christian-Sidak:fix/unc-path-validation
Open

Fix isPathWithinAllowedDirectories for UNC paths on Windows#3921
Christian-Sidak wants to merge 2 commits intomodelcontextprotocol:mainfrom
Christian-Sidak:fix/unc-path-validation

Conversation

@Christian-Sidak
Copy link
Copy Markdown

Summary

Fixes #3756

On Windows, isPathWithinAllowedDirectories fails for UNC paths (e.g. \\192.168.1.1\share\) because path.normalize strips a leading backslash from UNC paths, and then path.resolve interprets the result as a drive-relative path (e.g. C:\server\share). This causes the allowed-directory check to always fail for network shares.

This PR introduces a normalizePossiblyUNCPath helper that detects UNC paths and preserves the \\ prefix through normalization, rather than passing them through path.resolve which corrupts them. Non-UNC paths continue to use the existing path.resolve(path.normalize(...)) logic.

Changes

  • Added isUNCPath() helper to detect UNC paths by their \\ prefix
  • Added normalizePossiblyUNCPath() that preserves the UNC prefix through path.normalize, restoring the stripped backslash when needed
  • Applied normalizePossiblyUNCPath() to both the input path and allowed directory normalization in isPathWithinAllowedDirectories

Test plan

  • All 53 existing tests pass (including the existing UNC path test case)
  • The existing UNC test at line 432 of path-validation.test.ts covers the core scenario: matching \\server\share\project against allowed directories, subdirectory access, and cross-server rejection
  • To reproduce the original bug: configure \\192.168.x.x\share\ as an allowed directory on Windows and attempt file access -- previously always denied, now correctly allowed

On Windows, path.normalize can strip a leading backslash from UNC paths
(\\server\share becomes \server\share). When path.resolve is then
called on this single-backslash path, it gets interpreted as drive-relative
(e.g. C:\server\share), causing the allowed-directory check to always
fail for UNC network paths.

This introduces normalizePossiblyUNCPath which detects UNC paths and
preserves the \\\\ prefix through normalization, rather than passing
them through path.resolve which corrupts them.

Fixes modelcontextprotocol#3756
Copy link
Copy Markdown
Member

@olaservo olaservo left a comment

Choose a reason for hiding this comment

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

Good approach — skipping path.resolve entirely for UNC paths is the right call since that's what causes the corruption. The normalizePossiblyUNCPath helper is clean and well-documented. Non-UNC paths are unaffected.

One request before we can merge: could you add test cases for UNC paths? PR #3920 has some good ones you could reference. They should be gated behind path.sep === '\' (like the existing UNC test at line 432) since UNC paths are meaningless on Linux. Something like:

  • UNC path within allowed UNC directory → true
  • UNC path outside allowed UNC directory → false
  • Exact UNC path match → true

This review was assisted by Claude Code.

@Christian-Sidak
Copy link
Copy Markdown
Author

Added the three UNC path test cases (within allowed directory, outside allowed directory, exact match) all gated behind path.sep === '\\'. CI is re-running now.

quocnam1 pushed a commit to quocnam1/MCPServers that referenced this pull request Apr 18, 2026
PR modelcontextprotocol#3921 fixes isPathWithinAllowedDirectories for UNC paths, but three
other sites in the filesystem server also corrupt UNC paths via
path.resolve or preserve them incorrectly through path.normalize:

1. path-validation.ts::normalizePossiblyUNCPath
   path.normalize preserves trailing separators on UNC paths on Windows
   (unlike path.resolve for drive paths), so 'normalizedDir + path.sep'
   produces a double separator that startsWith() never matches. Strip
   the trailing separator to harmonize.

2. lib.ts::validatePath
   Calls path.resolve(expandedPath) before the allowed-directory check,
   which on Windows corrupts UNC paths into drive-relative paths
   (\\\\server\\share -> C:\\server\\share). Skip path.resolve for UNC
   paths and let normalizePath handle them.

3. index.ts bootstrap
   Same path.resolve corruption when normalizing allowed directories
   at server startup, before they are stored via setAllowedDirectories.
   Same guard applied.

All non-UNC paths retain existing behavior (path.resolve + normalize).
Tests added:
- 4 tests in path-validation.test.ts for the trailing-separator case
- 6 tests in lib.test.ts for validatePath end-to-end on UNC, gated
  behind process.platform === 'win32' and using the existing fs mock
  pattern
- Bug modelcontextprotocol#3 (bootstrap) has no direct unit test since UNC access in CI is
  not available, but the fix applies the same principle as validatePath
  and is logically covered.

Fixes modelcontextprotocol#3756
Supersedes modelcontextprotocol#3921
@quocnam1
Copy link
Copy Markdown

FYI I opened #3981 which addresses the same root issue plus three other corruption sites (validatePath, bootstrap init, trailing-separator normalization) that also cause UNC failures on Windows. Happy to close this one if #3981 is preferred, or to rebase on top of it if you'd rather land yours first.

@Christian-Sidak
Copy link
Copy Markdown
Author

Thanks for the heads up, @quocnam1! Happy to have this landed alongside or after #3981 -- whatever the maintainers prefer. If #3981's broader scope is more useful, I'm fine closing this one. Either way works for me.

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.

isPathWithinAllowedDirectories fails for UNC paths on Windows (network drives)

3 participants