CVE-2026-82392: pnpm Virtual Store Linker Path Traversal via Crafted Lockfile
A crafted pnpm-lock.yaml with path traversal sequences in package keys can cause pnpm to write arbitrary files anywhere on the filesystem when a developer runs pnpm install.

The problem
pnpm's virtual store linker extracts a package name from lockfile packages keys using dp.parse(depPath).name, then passes it directly to path.join(modules, pkgName) at lockfileToDepGraph.ts:233. No containment check exists at that call site.
Because pnpm-lock.yaml is a plain YAML file with no schema validation on packages keys, an attacker who controls the lockfile (via a malicious repo, compromised dependency, or supply chain commit) can supply a key like ../../../../../../../tmp/pwned@1.0.0.
The raw substring parse yields pkgName = '../../../../../../../tmp/pwned', and path.join resolves the install destination to /tmp/pwned instead of the virtual store. Under default config the impact is arbitrary file write. When dangerouslyAllowAllBuilds: true is set, the same escaped path is used in the rebuild phase and the attacker's postinstall script runs, escalating to RCE.
Proof of concept
A working proof-of-concept for CVE-2026-82392 in pnpm, with the exact payload below.
# pnpm-lock.yaml (place in project root, then run: pnpm install)
lockfileVersion: '9.0'
packages:
../../../../../../../tmp/pwned@1.0.0:
resolution: {integrity: sha512-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFURBOSnceZQjgXvuKJcS/pqt5n5ex5k+7OVODkTWGkng==}
engines: {node: '>=14'}
snapshots:
../../../../../../../tmp/pwned@1.0.0: {}
importers:
.:
dependencies:
legitimate-name:
specifier: ^1.0.0
version: ../../../../../../../tmp/pwned@1.0.0The root cause is a missing path-containment check at the virtual store graph-builder sink. dp.parse() does a raw substring on the depPath up to the version separator, so ../../../../../../../tmp/pwned@1.0.0 becomes name = '../../../../../../../tmp/pwned' with no validation.
That name feeds directly into path.join(modules, pkgName), which Node.js resolves by collapsing the traversal segments, landing outside the virtual store entirely.
Existing defenses do not catch this: depPathToFilename() only sanitizes the dirInVirtualStore component, verifyLockfileResolutions() validates dependency map aliases but never the packages keys themselves, and the YAML parser applies no schema to those keys.
The prior fix for GHSA-fr4h-3cph-29xv added safeJoinModulesDir to the hoisted linker (lockfileToHoistedDepGraph.ts) but left the identical pattern in lockfileToDepGraph.ts:233 unguarded. The fix in 10.34.5 applies the same safeJoinModulesDir containment helper at all remaining sinks, throwing ERR_PNPM_INVALID_DEPENDENCY_NAME for any name that does not pass npm package-name validation.
The fix
Upgrade pnpm to 10.34.5 (v10 line) or 11.11.0 (v11 line). The patch applies safeJoinModulesDir at lockfileToDepGraph.ts:233, after-install/src/index.ts:402, and lockfile/to-pnp/src/index.ts:105-110, and adds early lockfile-key validation so traversal-shaped depPath keys are rejected before any fetch or filesystem work begins.
See commits 51300fd and 78e29fe on the pnpm/pnpm repo.
Related research
- high · 7.5CVE-2026-82393CVE-2026-82393: pnpm Scoped-Name Path Traversal Arbitrary File Write
- high · 7.1pnpm pacquet: Trust-Lockfile Dependency Alias Path Traversal
- high · 8.2pnpm Path Traversal via configDependencies Lockfile Entry Allows Symlink Escape
- high · 7.1pnpm Hoisted Lockfile Alias Path Traversal ()