CVE
CVE-2026-82392, CVE-2026-82393
CWE
CWE-22, CWE-73, CWE-94
Affected Surface
- The npm package `pnpm` in versions `<10.34.5` and `>=11.0.0 <11.11.0`
- Developer workstations, CI runners, and build containers that run `pnpm install` against untrusted pull requests, unreviewed lockfile changes, or attacker-controlled tarball dependencies
- Projects that rely on `--ignore-scripts` as their main protection against hostile package installation, because `CVE-2026-82393` can still overwrite files outside `node_modules` before any lifecycle script question arises
- PnP-based pnpm deployments, where `CVE-2026-82392` also let a crafted lockfile aim `.pnp.cjs` package locations outside the virtual store
The strongest new package-manager story from the last three days is not malware. It is a pair of containment failures in pnpm that turn routine dependency installation into arbitrary file write.
The two CVEs were published into NVD on 31 August 2026, and they are closely related enough that defenders should triage them together:
| CVE | Input under attacker control | Vulnerable sink | Practical result |
|---|---|---|---|
CVE-2026-82392 | pnpm-lock.yaml packages key | path.join(modules, pkgName) after dp.parse(depPath).name | package contents written outside the virtual store |
CVE-2026-82393 | tarball dependency package.json name | raw isolated-linker joins built from resolved manifest name | overwrite outside node_modules, even under --ignore-scripts |
Both bugs come from the same architectural mistake: pnpm trusted a package name too early, treated it as a safe path segment, and only later discovered that a package name can be a filesystem traversal primitive if nobody reasserts containment before writing.
Affected package and fixed versions
The affected package is the npm package pnpm.
| Package | Affected versions | Fixed versions |
|---|---|---|
pnpm | <10.34.5 and >=11.0.0 <11.11.0 | 10.34.5, 11.11.0 |
The fixed releases land both CVEs at once. If you are below those versions, you should assume the install path still contains at least one filesystem escape route.
CVE-2026-82392: a crafted lockfile key escaped the virtual store
The first bug is easier to reason about because the data flow is short. The advisory shows that pnpm reconstructed a package name from an attacker-controlled lockfile key:
pnpm-lock.yaml packages key
-> dp.parse(depPath).name
-> pkgName
-> path.join(modules, pkgName)
-> storeController.importPackage(...)
The vulnerable path started with a packages entry such as:
packages:
../../../../../../../tmp/pwned@1.0.0:
resolution: { integrity: "..." }
From there, the code path described in the advisory is enough to break containment:
const dir = path.join(modules, pkgName)
If pkgName is ../../../../../../../tmp/pwned, then dir is not an internal package slot anymore. It is an attacker-chosen write target.
The important nuance is that CVE-2026-82392 is not only about one write. The advisory also notes a second impact path for PnP, where the same unvalidated name can shape packageLocation values in the generated .pnp.cjs map. That does not make PnP the main risk, but it shows the same trust error appearing in more than one linker path.
Why earlier defenses did not save it
The advisory calls out several nearby defenses that looked relevant but did not actually cover this sink:
depPathToFilename()sanitized the virtual-store slot name, but the innerpkgNamewas derived separatelyverifyLockfileResolutions()validated dependency aliases, not thepackageskeys themselves- lockfile parsing accepted the YAML shape without schema-level name validation
- the final import path did not re-check that the resolved directory still lived under the virtual store
That last point is the real lesson. A lockfile parser can reject obvious garbage and still lose if the final write sink trusts a path fragment that originated in the lockfile.
CVE-2026-82393: tarball manifests bypassed --ignore-scripts
The second bug is the one most teams should treat as the more serious operational problem.
Here the hostile input is not the lockfile key. It is the dependency tarball’s own package.json name field. According to the advisory, pnpm accepted a scoped traversal such as:
{
"name": "@x/../../../../home/runner/.git/hooks"
}
and then reused that resolved manifest name as a raw path segment in several isolated-linker joins.
The dangerous part is not subtle:
tarball dependency
-> resolved manifest name
-> raw path join under node_modules/.pnpm/.../node_modules/<name>
-> extraction outside node_modules
-> overwrite of attacker-chosen files
The advisory is explicit that this can happen during:
pnpm install --ignore-scripts
That makes CVE-2026-82393 more than a lifecycle-script bug. Even if your pipeline blocks postinstall, preinstall, and friends, the extracted files can still overwrite shell startup files, Git hooks, or package code that will execute later through normal developer or CI activity.
What the patch changed
The upstream fix is useful because it shows exactly where pnpm needed a stronger invariant.
In the graph builder, the vulnerable join:
const dir = path.join(modules, pkgName)
became:
const dir = safeJoinModulesDir(modules, pkgName)
The same containment helper was then applied to the remaining isolated-linker sinks and PnP package-location construction. Upstream also added validation so bad names fail with ERR_PNPM_INVALID_DEPENDENCY_NAME instead of reaching fetch and filesystem operations.
That is the right repair shape for both CVEs:
- validate package names at the resolver boundary
- re-check containment again at the filesystem sink
You want both. A package manager that validates names but does not guard the write path can still lose on edge cases. A package manager that only guards the sink may still let untrusted names flow too far into later logic.
Why this matters for application security teams
pnpm sits in a trust-heavy part of the software delivery path:
pull request or dependency update
-> lockfile or tarball resolution
-> pnpm install
-> source tree, hooks, caches, or binaries on developer and CI hosts
Once a package manager writes outside its own containment boundary, the question stops being “can a malicious dependency run a script?” and becomes “what trusted file or executable path can this install step touch?”
That is why CVE-2026-82393 deserves extra attention in build systems that were relying on --ignore-scripts as a hard stop. It is not a hard stop if extraction itself can plant or replace code in places the rest of the toolchain already trusts.
What to check right now
Start with version scope:
pnpm --version
Then find places where install inputs can be attacker-controlled:
rg -n "pnpm-lock.yaml|nodeLinker|dangerouslyAllowAllBuilds|allowBuilds" \
.github .gitlab-ci.yml package.json pnpm-workspace.yaml .npmrc .
rg -n "https?://.*\\.tgz|tarball:" package.json pnpm-lock.yaml .
The question that matters is not just “do we use pnpm?” It is:
Can untrusted contributors land lockfile changes?
Do any dependencies resolve from tarball URLs or custom registries?
Do CI runners call pnpm install on pull requests before human review?
If the answer to any of those is yes and the pnpm version is still in range, the install path itself is part of your attack surface.
Response guidance
- Upgrade to
pnpm@10.34.5orpnpm@11.11.0immediately. - Rebuild CI and developer base images that bundle older
pnpmbinaries. - Review recent pull requests for suspicious
pnpm-lock.yamlpackageskeys and tarball dependencies. - Treat unexpected file changes outside
node_modulesafterpnpm installas a potential compromise, not a broken build. - Keep
--ignore-scripts, but stop treating it as the only guardrail for hostile dependencies.
The larger pattern is worth remembering. Recent package-security failures have often centered on provenance, maintainer compromise, or install hooks. These two pnpm CVEs are different. They show that the package manager’s own path-containment logic is just as important. If the installer cannot keep package bytes inside its designated directory tree, a lot of higher-level supply-chain hygiene stops mattering.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.