pnpm Path Traversal: When Windows Backslashes Break the Rules
Jan 27, 2026·5 min read·5 visits
Executive Summary (TL;DR)
pnpm versions prior to 10.28.1 contain a Windows-specific path traversal vulnerability. The extraction logic checked for Unix-style traversal attempts (`./`) but ignored Windows-style backslashes (`.\`). This allows malicious packages to write files outside their installation directory, potentially overwriting configuration files or injecting code into CI/CD pipelines.
A logic error in pnpm's tarball parsing mechanism allowed for arbitrary file writes on Windows systems. By using backslashes (`\`) instead of forward slashes (`/`) in path names, attackers could bypass sanitization checks designed to prevent directory traversal.
The Hook: Fast, Efficient, and Slightly Blind
pnpm is the darling of the JavaScript ecosystem. It saves disk space, it's fast, and it uses a clever content-addressable store to manage dependencies. But in the race for performance, handling file system nuances often gets tricky—especially when you cross the bridge from the clean, predictable world of POSIX (Linux/macOS) to the wild west of Windows.
At the heart of any package manager is the ability to take a compressed archive (a tarball) and explode it onto the disk. This is a high-risk operation. You are essentially taking untrusted instructions on where to put bytes on a user's machine. If you aren't paranoid about the file paths inside that archive, you're going to have a bad time.
The vulnerability in question, CVE-2026-23889, hides in the parseTarball.ts file. This component is responsible for reading the entries of a package and deciding where they go. Ideally, it keeps them locked inside a designated folder. But on Windows, pnpm's bouncer was checking for the wrong kind of fake ID.
The Flaw: The POSIX-Centric Blind Spot
The root cause here is a classic "It Works On My Machine" error, assuming the developer's machine was a Mac or Linux box. To prevent path traversal (the act of escaping the extraction directory using ../), pnpm attempted to sanitize filenames.
The logic relied on a simple string check: fileName.includes('./'). If the path looked like it was trying to be clever with relative paths using forward slashes, pnpm would trigger a normalization routine to flatten it safely.
Here is the fatal flaw: Windows is bilingual. It speaks both Forward Slash (/) and Backslash (\).
If an attacker crafts a tarball entry named package\..\..\malicious.exe, the string does not contain ./. Therefore, pnpm's guard logic looks at it, shrugs, and says, "Looks safe to me!" It then passes this path to the Windows file system APIs. Windows, helpful as ever, interprets the backslashes as directory separators, processes the .., and walks up the directory tree, writing the file wherever the attacker pointed it.
The Code: A Tale of Two Slashes
Let's look at the smoking gun in store/cafs/src/parseTarball.ts. The vulnerable code was checking exclusively for the Unix convention.
Vulnerable Implementation:
// If it doesn't have './', it assumes it's safe.
if (fileName.includes('./')) {
fileName = path.posix.join('/', fileName).slice(1)
}The fix, applied in commit 6ca07ffbe6fc0e8b8cdc968f228903ba0886f7c0, is a lesson in defensiveness. It explicitly checks for the Windows backslash sequence and, crucially, normalizes all backslashes to forward slashes before doing any path math.
Patched Implementation:
// Check for both ./ AND .\
if (fileName.includes('./') || fileName.includes('.\\')) {
// 1. Replace all backslashes with forward slashes
// 2. Use POSIX join to resolve the '..' safely
fileName = path.posix.join('/', fileName.replaceAll('\\', '/')).slice(1)
}By converting \ to / first, path.posix.join can correctly interpret the traversal attempts and neutralize them, regardless of the operating system the code is running on.
The Exploit: Crafting the Poisoned Package
Exploiting this requires creating a valid tarball that breaks the rules. Standard tools like npm pack or tar usually normalize paths for you, so a script kiddie might struggle to generate the payload. We need to go lower level or manually edit the archive headers.
The Attack Chain:
- Target Selection: Identify a Windows-heavy environment. CI/CD runners (GitHub Actions Windows runners) are prime targets because they often run with elevated privileges and contain sensitive secrets in the environment.
- Payload Creation: We create a tar archive where the file name field in the header is modified from
package/index.jsto something spicy likepackage\..\..\..\Users\ContainerAdministrator\.npmrc. - Delivery: Publish this package to the npm registry (or a private registry). Give it a benign name like
windows-fs-helper. - Execution: When the victim runs
pnpm install windows-fs-helper, pnpm extracts the file. Because of the missing check, the file system writes our payload to the user's home directory instead of thenode_modulesfolder.
The Impact: Why This Matters
You might ask, "So I can write a file. Big deal." In the context of a package manager, Arbitrary File Write is usually Game Over.
Supply Chain Poisoning: By overwriting .npmrc, an attacker can change the registry URL. The next time the developer (or the CI server) runs install, they are pulling packages from the attacker's server, not the official one. This grants persistent MITM capabilities.
CI/CD RCE: On build servers, overwriting a script that is about to be executed (like a post-build step or a GitHub Actions workflow file) leads directly to Remote Code Execution. Since the pnpm process already has write access to the disk, the barrier to entry is low.
This vulnerability is strictly Integrity based (CVSS 6.5), but in a development environment, integrity loss almost always leads to confidentiality loss (stealing env vars) or availability loss (breaking the build).
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
pnpm pnpm | < 10.28.1 | 10.28.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| CVSS v3.1 | 6.5 (Medium) |
| Attack Vector | Network (Malicious Package) |
| Impact | Arbitrary File Write |
| Affected OS | Windows |
| Fix Commit | 6ca07ffbe6fc0e8b8cdc968f228903ba0886f7c0 |
MITRE ATT&CK Mapping
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.