fix(server): validate file paths in createTempFiles#40754
fix(server): validate file paths in createTempFiles#40754pavelfeldman merged 3 commits intomicrosoft:mainfrom
Conversation
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)) |
There was a problem hiding this comment.
I think you want resolveWithinRoot here and in other places.
There was a problem hiding this comment.
We can even add throwingResolveWithinRoot
There was a problem hiding this comment.
Switched to resolveWithinRoot which handles both resolution and validation. This matches the pattern used in harUnzip, download, traceParser, and traceUtils.
There was a problem hiding this comment.
Added throwingResolveWithinRoot to @utils/fileUtils next to resolveWithinRoot. The dispatcher now uses it for a one-liner path validation.
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>
Test results for "MCP"1 failed 7043 passed, 1068 skipped Merge workflow run. |
Test results for "tests 1"5 flaky41701 passed, 850 skipped Merge workflow run. |
Summary
rootDirNameis sanitized withpath.basename()butitem.nameis used raw inpath.join(), allowing path components like../to escape the temp directoryisPathInsidecheck consistent with the existingrootDirNamevalidation and other path checks in the codebaseFailure scenario: When using
browserType.connect()to a shared Playwright server, a remote client sends RPC messages includingcreateTempFileswith crafteditem.namevalues containing../../. TherootDirNameparameter is correctly sanitized withpath.basename()on line 229, butitem.namepasses throughpath.join()unsanitized, resolving../components and allowing writes outside the temp directory.Origin: Introduced in #31165 (2024-06-12).
Related fixes in this repo: