CVE-2026-82392
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
pnpm is a package manager. Prior to 10.34.5 and from 11.0.0 until 11.11.0, pnpm parses the package name from attacker-controlled pnpm-lock.yaml packages keys with dp.parse(depPath).name and uses it without validation in deps/graph-builder/src/lockfileToDepGraph.ts and pnpm11/deps/graph-builder/src/lockfileToDepGraph.ts. The name reaches path.join(modules, pkgName), storeController.importPackage, and pnpm11/lockfile/to-pnp/src/index.ts, allowing package contents to be written outside node_modules when a user runs pnpm install. When dangerouslyAllowAllBuilds or a matching allowBuilds entry permits lifecycle scripts, the escaped package can execute code with the user's privileges. This issue is fixed in versions 10.34.5 and 11.11.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
7.1
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
pnpm is a package manager. Prior to 10.34.5 and from 11.0.0 until 11.11.0, pnpm parses the package name from attacker-controlled pnpm-lock.yaml packages keys with dp.parse(depPath).name and uses it without validation in deps/graph-builder/src/lockfileToDepGraph.ts and pnpm11/deps/graph-builder/src/lockfileToDepGraph.ts. The name reaches path.join(modules, pkgName), storeController.importPackage, and pnpm11/lockfile/to-pnp/src/index.ts, allowing package contents to be written outside node_modules when a user runs pnpm install. When dangerouslyAllowAllBuilds or a matching allowBuilds entry permits lifecycle scripts, the escaped package can execute code with the user's privileges. This issue is fixed in versions 10.34.5 and 11.11.0.
- Record
CVE-2026-82392- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-68816
7.1
pnpm is a package manager. Prior to 10.34.5 and from 11.0.0 until 11.11.0, pnpm parses the package name from attacker-controlled pnpm-lock.yaml packages keys with dp.parse(depPath).name and uses it without validation in deps/graph-builder/src/lockfileToDepGraph.ts and pnpm11/deps/graph-builder/src/lockfileToDepGraph.ts. The name reaches path.join(modules, pkgName), storeController.importPackage, and pnpm11/lockfile/to-pnp/src/index.ts, allowing package contents to be written outside node_modules when a user runs pnpm install. When dangerouslyAllowAllBuilds or a matching allowBuilds entry permits lifecycle scripts, the escaped package can execute code with the user's privileges. This issue is fixed in versions 10.34.5 and 11.11.0.
- Record
EUVD-2026-68816- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-c59q-g84q-2gj5
7.1
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
Summary
The virtual store linker constructs package installation directories using path.join(modules, pkgName) where pkgName is extracted from lockfile packages keys via dp.parse(depPath).name without validation. A crafted pnpm-lock.yaml with traversal sequences in depPath keys (e.g., ../../../tmp/[email protected]) causes package content to be written to arbitrary filesystem paths during pnpm install.
This is an incomplete fix of GHSA-fr4h-3cph-29xv — the safeJoinModulesDir containment helper was applied to the hoisted linker and symlinkDependency but NOT to the virtual store linker's lockfileToDepGraph.ts:233.
Details
Root Cause
dp.parse() at pnpm11/deps/path/src/index.ts:135 extracts the package name as:
const name = dependencyPath.substring(0, sepIndex)
This is a raw substring operation with zero validation that name is a valid npm package name. A depPath of ../../../tmp/[email protected] yields name = '../../../tmp/pwned'.
Vulnerable Code Path
pnpm-lock.yaml→lockfile.packages['../../../../../../../tmp/[email protected]'](attacker-controlled lockfile key)nameVerFromPkgSnapshot(depPath, pkgSnapshot)atlockfile/utils/src/nameVerFromPkgSnapshot.ts:16→ callsdp.parse(depPath)→ returns{ name: '../../../../../../../tmp/pwned' }lockfileToDepGraph.ts:232→modules = path.join(dirInVirtualStore, 'node_modules')lockfileToDepGraph.ts:233→dir = path.join(modules, pkgName)→ resolves to/tmp/pwned(ESCAPES virtual store)storeController.importPackage(depNode.dir, ...)→ writes package content to the traversed path
Why Existing Defenses Don't Catch It
depPathToFilename()— replaces/with+for thedirInVirtualStorepath, butpkgNamecomes SEPARATELY fromdp.parse()and is NOT passed through this functionverifyLockfileResolutions()— validates dependency map keys (aliases) viaisValidDependencyAlias(), but never validates the depPath keys themselves- Lockfile parser —
yaml.load(lockfileRawContent)with no schema validation onpackageskeys importPackage()— acceptstargetDirand passes it directly tocafsStore.importPackage(targetDir, ...)with zero containment check- Integrity verification — requires a real fetchable package but does not validate the destination path
Escalation to RCE (non-default config)
When dangerouslyAllowAllBuilds: true is configured (or the traversal package name is in the explicit allowBuilds list), the same traversed path is used in the rebuild phase at after-install/src/index.ts:402,470. The attacker's postinstall script then executes with the victim's shell access. Under default config, allowBuild returns false for unknown packages, limiting impact to arbitrary file write.
Also Affected (PnP linker)
When nodeLinker: pnp is configured, lockfileToPackageRegistry() at lockfile/to-pnp/src/index.ts:105-110 uses the same unvalidated dp.parse().name in packageLocation construction, allowing the .pnp.cjs resolver map to point outside the virtual store. This is a lower-impact variant (PnP is not the default linker).
Impact
An attacker who can commit a crafted pnpm-lock.yaml to a repository (or supply one via a malicious package) can cause arbitrary file writes on the machine of any user who runs pnpm install. Written content is the actual package files from a real npm package (attacker controls which package and which destination).
Targets for arbitrary file write include:
.git/hooks/pre-commit— code execution on next git operation~/.local/bin/— binary hijacking- Project source files — supply chain injection
Reproduction
Craft a pnpm-lock.yaml:
lockfileVersion: '9.0'
packages:
../../../../../../../tmp/[email protected]:
resolution: {integrity: sha512-<real-package-integrity>}
engines: {node: '>=14'}
snapshots:
../../../../../../../tmp/[email protected]:…
- Record
GHSA-c59q-g84q-2gj5- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
7.1
pnpm is a package manager. Prior to 10.34.5 and from 11.0.0 until 11.11.0, pnpm parses the package name from attacker-controlled pnpm-lock.yaml packages keys with dp.parse(depPath).name and uses it without validation in deps/graph-builder/src/lockfileToDepGraph.ts and pnpm11/deps/graph-builder/src/lockfileToDepGraph.ts. The name reaches path.join(modules, pkgName), storeController.importPackage, and pnpm11/lockfile/to-pnp/src/index.ts, allowing package contents to be written outside node_modules when a user runs pnpm install. When dangerouslyAllowAllBuilds or a matching allowBuilds entry permits lifecycle scripts, the escaped package can execute code with the user's privileges. This issue is fixed in versions 10.34.5 and 11.11.0.
- Record
CVE-2026-82392- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
7.1
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
pnpm is a package manager. Prior to 10.34.5 and from 11.0.0 until 11.11.0, pnpm parses the package name from attacker-controlled pnpm-lock.yaml packages keys with dp.parse(depPath).name and uses it without validation in deps/graph-builder/src/lockfileToDepGraph.ts and pnpm11/deps/graph-builder/src/lockfileToDepGraph.ts. The name reaches path.join(modules, pkgName), storeController.importPackage, and pnpm11/lockfile/to-pnp/src/index.ts, allowing package contents to be written outside node_modules when a user runs pnpm install. When dangerouslyAllowAllBuilds or a matching allowBuilds entry permits lifecycle scripts, the escaped package can execute code with the user's privileges. This issue is fixed in versions 10.34.5 and 11.11.0.
- Record
CVE-2026-82392- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-c59q-g84q-2gj5
7.1
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
Summary
The virtual store linker constructs package installation directories using path.join(modules, pkgName) where pkgName is extracted from lockfile packages keys via dp.parse(depPath).name without validation. A crafted pnpm-lock.yaml with traversal sequences in depPath keys (e.g., ../../../tmp/[email protected]) causes package content to be written to arbitrary filesystem paths during pnpm install.
This is an incomplete fix of GHSA-fr4h-3cph-29xv — the safeJoinModulesDir containment helper was applied to the hoisted linker and symlinkDependency but NOT to the virtual store linker's lockfileToDepGraph.ts:233.
Details
Root Cause
dp.parse() at pnpm11/deps/path/src/index.ts:135 extracts the package name as:
const name = dependencyPath.substring(0, sepIndex)
This is a raw substring operation with zero validation that name is a valid npm package name. A depPath of ../../../tmp/[email protected] yields name = '../../../tmp/pwned'.
Vulnerable Code Path
pnpm-lock.yaml→lockfile.packages['../../../../../../../tmp/[email protected]'](attacker-controlled lockfile key)nameVerFromPkgSnapshot(depPath, pkgSnapshot)atlockfile/utils/src/nameVerFromPkgSnapshot.ts:16→ callsdp.parse(depPath)→ returns{ name: '../../../../../../../tmp/pwned' }lockfileToDepGraph.ts:232→modules = path.join(dirInVirtualStore, 'node_modules')lockfileToDepGraph.ts:233→dir = path.join(modules, pkgName)→ resolves to/tmp/pwned(ESCAPES virtual store)storeController.importPackage(depNode.dir, ...)→ writes package content to the traversed path
Why Existing Defenses Don't Catch It
depPathToFilename()— replaces/with+for thedirInVirtualStorepath, butpkgNamecomes SEPARATELY fromdp.parse()and is NOT passed through this functionverifyLockfileResolutions()— validates dependency map keys (aliases) viaisValidDependencyAlias(), but never validates the depPath keys themselves- Lockfile parser —
yaml.load(lockfileRawContent)with no schema validation onpackageskeys importPackage()— acceptstargetDirand passes it directly tocafsStore.importPackage(targetDir, ...)with zero containment check- Integrity verification — requires a real fetchable package but does not validate the destination path
Escalation to RCE (non-default config)
When dangerouslyAllowAllBuilds: true is configured (or the traversal package name is in the explicit allowBuilds list), the same traversed path is used in the rebuild phase at after-install/src/index.ts:402,470. The attacker's postinstall script then executes with the victim's shell access. Under default config, allowBuild returns false for unknown packages, limiting impact to arbitrary file write.
Also Affected (PnP linker)
When nodeLinker: pnp is configured, lockfileToPackageRegistry() at lockfile/to-pnp/src/index.ts:105-110 uses the same unvalidated dp.parse().name in packageLocation construction, allowing the .pnp.cjs resolver map to point outside the virtual store. This is a lower-impact variant (PnP is not the default linker).
Impact
An attacker who can commit a crafted pnpm-lock.yaml to a repository (or supply one via a malicious package) can cause arbitrary file writes on the machine of any user who runs pnpm install. Written content is the actual package files from a real npm package (attacker controls which package and which destination).
Targets for arbitrary file write include:
.git/hooks/pre-commit— code execution on next git operation~/.local/bin/— binary hijacking- Project source files — supply chain injection
Reproduction
Craft a pnpm-lock.yaml:
lockfileVersion: '9.0'
packages:
../../../../../../../tmp/[email protected]:
resolution: {integrity: sha512-<real-package-integrity>}
engines: {node: '>=14'}
snapshots:
../../../../../../../tmp/[email protected]:…
- Record
GHSA-c59q-g84q-2gj5- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L - Weakness
- CWE-22
- Updated
- 2026-11-02
Affected products
Affected packages
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-82392 is a high vulnerability (CVSS 7.1) in Pnpm, published August 31, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.61%, the 49th percentile. Fixed in pnpm 10.34.5.
References
github.com/pnpm/pnpm/security/advisories/GHSA-c59q-g84q-2gj5
github.com/pnpm/pnpm/pull/12872
github.com/pnpm/pnpm/pull/12890
github.com/pnpm/pnpm/commit/51300fd41c5e4c8f47635108e373cc3d1f324fa7
github.com/pnpm/pnpm/commit/78e29fe5583a1e5d69ea05e414eff310f78d5ed9
github.com/pnpm/pnpm/releases/tag/v10.34.5
Left out here: the rest of 2 source descriptions. The JSON record has them.