GitHub Advisoryadvisory · npm

GHSA-vq4v-j7r6-jq4m pnpm: A tarball dependency's manifest `name` escapes node_modules → arbitrary file write/overwrite on install

HIGH Published 2026-09-02 Modified 2026-09-02 Official page ↗
Aliases
CVE-2026-82393
Ecosystem
npm
Packages
pnpm
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weaknesses
CWE-22, CWE-73, CWE-94
Reviewed
2026-09-02

Affected packages

• npm pnpm: < 10.34.5 → fixed in 10.34.5 • npm pnpm: >= 11.0.0, < 11.11.0 → fixed in 11.11.0

Description

Summary When resolving a package, pnpm uses the resolved manifest `name` as a raw path segment for the isolated-linker import target. A tarball dependency whose `package.json` `name` is a scoped path traversal (`@x/../../…/`) is therefore extracted outside `node_modules`, to an attacker-chosen absolute path, and can overwrite existing files there. Attacker controls the destination, filenames, and contents → arbitrary file write → code execution (e.g. `~/.zshrc`, `.git/hooks/pre-commit`, another package's code). Occurs during `pnpm install` even with `--ignore-scripts` (no lifecycle scripts run), defeating that safety. Same class as the just-patched GHSA-hwx4 (transitive-dependency *alias* traversal) and GHSA-v23m (`stage download` manifest name/version traversal), in a sink their fixes did not cover: the isolated-linker import target keyed by the resolved name. Root cause • The isolated-linker import target is built with a raw `path.join(modules, )` in `installing/deps-resolver/src/resolvePeers.ts:706`, `installing/deps-resolver/src/index.ts:614`, and `deps/graph-builder/src/lockfileToDepGraph.ts:233` — without the `safeJoinModulesDir` guard used on the symlink/hoisted/bin paths (`installing/deps-restorer/src/lockfileToHoistedDepGraph.ts:222`). The store location is `node_modules/.pnpm//node_modules/`, so a traversal `` escapes. • The only resolve-time name gate (`resolving/npm-resolver/src/pickPackage.ts:753`) rejects only *unscoped* names containing `/`, so a scoped `@x/../..` passes. Steps to reproduce Self-contained PoC (real `[email protected]`; loopback tarball server; escape target is a throwaway temp dir): ``` npm i [email protected] host a tarball whose package.json name = "@x/"+"../".repeat(25)+"/OUTSIDE"; victim depends on the http URL pnpm install --ignore-scripts ``` Confirmed output (`repro/poc.mjs`, exit 0): ``` escape dir is outside the project : true new file implanted outside node_modules : true pre-existing file OVERWRITTEN : true • CONFIRMED: a tarball dependency wrote & overwrote files OUTSIDE the project during `pnpm install --ignore-scripts` * ``` Remediation Route the isolated-linker import-target joins (`resolvePeers.ts:706`, `deps-resolver/index.ts:614`, `lockfileToDepGraph.ts:233`) through `safeJoinModulesDir` (as the hoisted linker already does), and/or enforce `validate-npm-package-name` on the resolved manifest name (close the scoped-name gap at `pickPackage.ts:753`) so the import target rejects a traversal name and re-asserts containment before any write.

Related

Alias · CVE (1)

Weakness · CWE (3)

  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory weakness
  • CWE-73 External Control of File Name or Path weakness
  • CWE-94 Improper Control of Generation of Code weakness

References