Skip to content

fix(server): validate file paths in createTempFiles#40754

Merged
pavelfeldman merged 3 commits intomicrosoft:mainfrom
SebTardif:fix/temp-files-path-traversal
May 9, 2026
Merged

fix(server): validate file paths in createTempFiles#40754
pavelfeldman merged 3 commits intomicrosoft:mainfrom
SebTardif:fix/temp-files-path-traversal

Conversation

@SebTardif
Copy link
Copy Markdown
Contributor

@SebTardif SebTardif commented May 9, 2026

Summary

  • rootDirName is sanitized with path.basename() but item.name is used raw in path.join(), allowing path components like ../ to escape the temp directory
  • Add isPathInside check consistent with the existing rootDirName validation and other path checks in the codebase

Failure scenario: When using browserType.connect() to a shared Playwright server, a remote client sends RPC messages including createTempFiles with crafted item.name values containing ../../. The rootDirName parameter is correctly sanitized with path.basename() on line 229, but item.name passes through path.join() unsanitized, resolving ../ components and allowing writes outside the temp directory.

Origin: Introduced in #31165 (2024-06-12).

Related fixes in this repo:

  • #40715 -- path traversal in static file serving (merged)
  • #40623 -- Zip Slip path traversal in harUnzip (merged)
  • #40555 -- confine trace attachment reads to trace dir (merged)

rootDirName is sanitized with path.basename() but item.name is used
raw in path.join(), allowing path components like '../' to escape
the temp directory. Add isPathInside check consistent with the
existing rootDirName validation and other path checks in the codebase.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
await progress.race(fs.promises.mkdir(path.dirname(path.join(tempDirWithRootName, item.name)), { recursive: true }));
const file = fs.createWriteStream(path.join(tempDirWithRootName, item.name));
const itemPath = path.join(tempDirWithRootName, item.name);
if (!isPathInside(tempDirWithRootName, itemPath))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you want resolveWithinRoot here and in other places.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can even add throwingResolveWithinRoot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Switched to resolveWithinRoot which handles both resolution and validation. This matches the pattern used in harUnzip, download, traceParser, and traceUtils.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added throwingResolveWithinRoot to @utils/fileUtils next to resolveWithinRoot. The dispatcher now uses it for a one-liner path validation.

SebTardif added 2 commits May 9, 2026 11:30
Switch to resolveWithinRoot which is the established pattern for
path validation in this codebase (used in harUnzip, download,
traceParser, traceUtils). It handles both resolution and validation
in one call.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Add throwingResolveWithinRoot to @utils/fileUtils that combines
resolveWithinRoot + null check + throw in one call. Use it in
createTempFiles for cleaner path validation.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

Test results for "MCP"

1 failed
❌ [firefox] › mcp/tracing.spec.ts:54 › check that trace is saved with browser_start_tracing (no output dir) @mcp-windows-latest-firefox

7043 passed, 1068 skipped


Merge workflow run.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

Test results for "tests 1"

5 flaky ⚠️ [installation tests] › playwright-test-package-managers.spec.ts:19 › npm: @playwright/test should work `@package-installations-windows-latest`
⚠️ [installation tests] › screencast.spec.ts:18 › screencast works `@package-installations-windows-latest`
⚠️ [chromium-library] › library/popup.spec.ts:261 › should not throw when click closes popup `@chromium-ubuntu-22.04-node20`
⚠️ [chromium-library] › library/video.spec.ts:647 › screencast › should capture full viewport `@chromium-ubuntu-22.04-node20`
⚠️ [firefox-page] › page/page-emulate-media.spec.ts:144 › should keep reduced motion and color emulation after reload `@firefox-ubuntu-22.04-node20`

41701 passed, 850 skipped


Merge workflow run.

@pavelfeldman pavelfeldman merged commit 5614c60 into microsoft:main May 9, 2026
44 of 45 checks passed
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