Thanks to visit codestin.com
Credit goes to github.com

Skip to content

pdf-server: robust path validation + folder/file root support#497

Merged
ochafik merged 15 commits intomainfrom
ochafik/pdf-files
Feb 24, 2026
Merged

pdf-server: robust path validation + folder/file root support#497
ochafik merged 15 commits intomainfrom
ochafik/pdf-files

Conversation

@ochafik
Copy link
Contributor

@ochafik ochafik commented Feb 24, 2026

Summary

Robust path validation and file access control for pdf-server.

Changes

  1. computer:// URL scheme support: Some clients use computer:// instead of file:// for local file references. Both schemes are now handled in isFileUrl, fileUrlToPath, and root processing.

  2. Robust ancestor directory matching: Replaced String.startsWith(dir + path.sep) with path.relative-based isAncestorDir(). The old approach failed on trailing slashes and path normalization edge cases. The new check also tries realpathSync for both file and directory to handle symlinks/bind-mounts.

  3. Folders in CLI + files in roots: CLI args can now be directories (added to allowedLocalDirs). MCP roots that are files get added to allowedLocalFiles.

  4. Bare path support: Accept raw filesystem paths (e.g. /Users/.../file.pdf) in addition to file:// URLs. Bare paths are percent-decoded in case the client sends %20 for spaces.

  5. Removed temporary VM path workaround: The resolveVmPath function was a temporary workaround for clients that send untranslated internal paths. This has been removed — clients are expected to send valid host paths or file URIs.

Net change

5 files changed, 265 insertions, 30 deletions (mostly tests).

Test Plan

27 pdf-server tests pass, including new tests for computer://, trailing slashes, grandparent dirs, bare paths, percent-encoding, and isAncestorDir edge cases.

@ochafik ochafik marked this pull request as ready for review February 24, 2026 13:25
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 24, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/ext-apps@497

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-preact@497

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-react@497

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-solid@497

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-svelte@497

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-vanillajs@497

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-vue@497

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-budget-allocator@497

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-cohort-heatmap@497

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-customer-segmentation@497

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-debug@497

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-map@497

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-pdf@497

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-scenario-modeler@497

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-shadertoy@497

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-sheet-music@497

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-system-monitor@497

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-threejs@497

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-transcript@497

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-video-resource@497

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-wiki-explorer@497

commit: f93d879

- Replace string prefix matching (startsWith) with path.relative-based
  isAncestorDir() which correctly handles trailing slashes, path
  normalization, and prevents both .. traversal and prefix attacks.
- Use path.resolve consistently when adding paths to allowedLocalFiles
  and allowedLocalDirs (main.ts was missing this).
- Use resolved path for exact file match check in validateUrl.
- Improve error message formatting for easier debugging.
- Add tests for trailing slashes, grandparent dirs, and isAncestorDir.
Remove the withLogging transport wrapper, logInteraction helper,
InteractionLogger type, and all log parameter threading that were
added for debugging.
@ochafik ochafik changed the title pdf-server: support folders in cli + files in roots pdf-server: robust path validation + folder/file root support Feb 24, 2026
Some clients (e.g. Claude Code) use computer:// instead of file://
for local file references. Handle both schemes in isFileUrl and
fileUrlToPath.
Clients like Claude Desktop may pass raw absolute paths (e.g.
/Users/.../file.pdf) instead of file:// URLs. Handle these in
validateUrl and readPdfRange.
Use fs.realpathSync to resolve symlinks/bind mounts when comparing
file paths against allowed directories. This fixes the path namespace
mismatch where Claude Desktop sends container paths (e.g. /sessions/...)
while MCP roots use host paths (e.g. /Users/...).

Both the file path and directory paths are resolved through symlinks
before comparison, so either side can be a symlink.

Also stores realpath of roots in allowedLocalDirs/Files so matching
works in both directions.

Removes verbose hex diagnostics (no longer needed).
Clients may send bare file paths with percent-encoded characters
(e.g., Application%20Support). Apply decodeURIComponent to bare paths
before resolving, matching the behavior already used for file:// URLs.
Include url, resolved path, realpath, allowedFiles, and per-directory
path.relative results in the error returned to the client, so we can
diagnose why isAncestorDir fails when paths look identical.
path.relative returns 10 levels of .. despite paths looking identical,
meaning they differ at the byte level. Add hex dump of first 30 chars
of both paths to catch invisible Unicode or encoding differences.
The cowork VM remaps paths via bind mounts: the tool input uses
VM-internal paths (/sessions/...) while MCP roots use host paths
(/Users/...). Since path.relative can't match across bind mounts,
compare directory inodes instead (dev+ino from fs.statSync).

Walks up the file's parent directories checking each against allowed
dirs by inode, which works regardless of path namespace differences.
…ching

When the MCP server runs on the host but receives unrewritten VM paths
(e.g., /sessions/name/mnt/uploads/file.pdf), the tool call url argument
isn't rewritten by the cowork VM proxy because it doesn't know which
arguments are file paths.

Fix: when path validation fails, try to match the file by finding the
directory basename (e.g., 'uploads') in the VM path and looking for
the relative path suffix under each allowed directory on the host.

Also returns the resolved host path so readPdfRange reads the correct
file on the host filesystem.
The VM path mismatch (VM-internal paths not rewritten in tool call
arguments) should be fixed in the cowork VM proxy layer, not worked
around in the MCP server.

Removes:
- resolveVmPath() function
- resolvedPath field from validateUrl return type
- effectiveUrl remapping in tool handlers
- VM path resolution test
@ochafik ochafik requested a review from antonpk1 February 24, 2026 18:18
@ochafik ochafik merged commit 281a26b into main Feb 24, 2026
19 checks passed
ochafik added a commit that referenced this pull request Feb 24, 2026
Changes since 1.1.1:
- feat: add ui/download-file method for host-mediated file downloads (#475)
- pdf-server: robust path validation + folder/file root support (#497)
- ci: auto-fix prettier formatting in pre-commit hook and CI (#498)
ochafik added a commit that referenced this pull request Feb 25, 2026
* chore: bump ext-apps to 1.1.2

Changes since 1.1.1:
- feat: add ui/download-file method for host-mediated file downloads (#475)
- pdf-server: robust path validation + folder/file root support (#497)
- ci: auto-fix prettier formatting in pre-commit hook and CI (#498)

* fix: sync local SDK build into node_modules for examples

npm workspaces hoists example dependencies to the root node_modules,
but installs the published registry copy of @modelcontextprotocol/ext-apps
instead of linking to the local source. This causes type-check failures
when examples use features not yet published to npm.

Add scripts/link-self.mjs which copies the freshly-built dist/ and
package.json into the hoisted node_modules copy after each SDK build,
ensuring examples always type-check against the latest local types.

See: npm/feedback#774

* Revert "fix: sync local SDK build into node_modules for examples"

This reverts commit e3387e8.
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