A bookstore that, when you return a book titled `../`, burns down the building next door
pnpm reads the bin field of installed packages to register command-line shims under PNPM_HOME. The manifest validator on versions <10.34.2 and 11.0.0–11.5.2 fails to reject relative path segments (..) inside scoped bin keys. When the package is later uninstalled or upgraded, pnpm joins the unsanitized bin name to the global bin directory and hands the resulting path to fs.rmSync(..., {recursive: true, force: true}) — recursively deleting whatever the traversal points at. The classic payload is a published package whose bin map contains an entry like @attacker/.., which on cleanup resolves to and obliterates the parent of PNPM_HOME (commonly ~/.local/share/pnpm or the user's home tree).
The vendor 6.5/MEDIUM rating matches reality. The CIA triangle is genuinely A-only — no code execution, no data confidentiality loss, no integrity tampering of in-place files (just destruction). The required UI:R is heavy: a developer or CI job must voluntarily pnpm add -g an attacker-published package, then later run pnpm remove -g or an upgrade that triggers cleanup. That's a typosquat / dependency-confusion social-engineering pre-req, which limits realistic exposure to dev workstations and CI runners that pull global packages from npm. Vendor severity stands.
4 steps from start to impact.
Publish a poisoned package to npm
package.json bin field maps a key containing .. (e.g. "@attacker/..": "./index.js") and publishes it to the public npm registry. The crafted manifest passes npm's own publication checks because npm is not the consumer of the bin key in the vulnerable way pnpm is. Typosquatting a popular CLI name (prettier-cli, tsc-fast, etc.) is the realistic delivery vehicle.- npm publish account
- Plausible package name (typosquat, dep-confusion, or supply-chain takeover of an existing maintainer)
- npm runs malware scanning on new packages — overtly malicious manifests get flagged
- Reputable packages have install-count signals defenders can score against
- Many shops pin to private registries (Verdaccio, Artifactory, JFrog) that gate uploads
bin entries; SBOM diffing catches new global deps.Victim globally installs the package
pnpm add -g <package> against the poisoned package. Install completes normally — the shim symlink under PNPM_HOME/bin is the only on-disk artifact at this stage. No deletion happens yet; the bomb is armed but not detonated.- Victim uses pnpm (not npm or yarn) as their global package manager
- Victim chooses
-ginstall over a project-localpnpm install - Victim does not pin to a private registry that would have rejected the package
- Most shops install dependencies project-locally, not globally
- CI containers are usually ephemeral and don't run
add -g - Corporate dev environments increasingly route npm traffic through Artifactory/Verdaccio proxies
~/.local/share/pnpm/bin/; package-allowlist policies in JFrog/Sonatype.Trigger the unsafe uninstall path
pnpm remove -g <package>, pnpm update -g, or have the package replaced by a dependency resolution. pnpm walks the manifest's bin map, resolves each entry against PNPM_HOME, and calls fs.rmSync(targetPath, {recursive: true, force: true}). Because .. was never stripped, the resolved path escapes PNPM_HOME and points at its parent (often the user's home or ~/.local/share).- Victim issues an uninstall or update
- Process runs with the same user privileges that own the target directory tree
- Many devs never explicitly uninstall — packages just sit
- On Windows,
fs.rmSynchonors NTFS ACLs; admin-owned dirs survive - Containerized CI typically discards the filesystem before any cleanup would matter
unlink/rmdir rules on home directories spike noticeably.Recursive deletion of arbitrary directory
fs.rmSync with recursive:true, force:true performs an unconditional depth-first wipe of the resolved target. With .. as the bin name, that target is one directory above PNPM_HOME — typically ~/.local/share (Linux/macOS) or %LOCALAPPDATA% (Windows). Source trees, SSH keys, browser profiles, and other tooling state in the blast radius are destroyed.- Step 3 completed
- Target directory writable by the current user
- Backups / Time Machine / OneDrive sync recover quickly
- Dev workstations are usually not the system of record — pain not data loss
- Root-owned system directories untouched unless pnpm was sudo'd (unusual)
The supporting signals.
| In-the-wild exploitation | None observed. No PoC published as weaponized typosquat; no campaign attribution. |
|---|---|
| Proof of concept | Public technical writeup by reporter (Amit Schendel) with manifest snippet; trivial to reproduce — bin field with .. key. |
| EPSS | 0.00271 (~0.27%) — bottom-percentile likelihood of exploitation in next 30 days. |
| KEV status | Not on CISA KEV. No federal patch deadline. |
| CVSS vector | AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H — network-deliverable via npm registry, but requires user interaction (install + uninstall) and impacts availability only. |
| Affected versions | pnpm < 10.34.2 and >= 11.0.0, < 11.5.3. Releases prior to 10.x not assessed in advisory. |
| Fixed versions | pnpm 10.34.2 (10.x line) and pnpm 11.5.3 (11.x line). No distro backports needed — pnpm is shipped as an npm package / standalone binary, not via OS repos. |
| Exposure data | Not internet-exposed in a scannable sense — affects developer workstations and CI runners. pnpm install base estimated at ~5M weekly downloads (npm registry stats); growing share of JS toolchains. |
| Disclosure | Patch committed 2026-06-05; GHSA-4gxm-v5v7-fqc4 + CVE published 2026-06-25. |
| Reporter | Amit Schendel, Senior Security Researcher (coordinated disclosure via GitHub Security Advisory). |
noisgate verdict.
The single decisive factor is the social-engineering prerequisite — exploitation requires a developer to voluntarily pnpm add -g an attacker-controlled package and later trigger its cleanup, collapsing the exploit population to typosquat/dep-confusion victims on dev/CI hosts. Impact is destructive but bounded to availability of user-writable directories, with no code execution or data exfiltration path.
Why this verdict
- Friction — user interaction required twice: the victim must both install AND later uninstall/update a poisoned package.
UI:Rplus an out-of-band cleanup trigger is two compounding human steps; most installed-and-forgotten globals never detonate. - Friction — narrow attack population: only pnpm users (~15% of JS package-manager mindshare per npm telemetry) who use global installs are reachable. Project-local
pnpm installdoes not touchPNPM_HOMEand is unaffected. - Friction — delivery via public npm only: orgs using private registries (Artifactory, Verdaccio, JFrog) with allowlists block the typosquat vector entirely.
- Impact ceiling — availability only:
C:N/I:N/A:H. No RCE, no token theft, no persistence. Worst case is wiped~/.local/shareon a dev workstation, recoverable from backup. - Role multiplier — developer workstations / dev sandboxes: chain succeeds, but blast radius is per-user home directory destruction. Not a fleet-scale outcome. Floor stays MEDIUM.
- Role multiplier — CI build agents: chain succeeds if the agent runs
pnpm add -goutside an ephemeral container, but most modern CI is container-per-job; persistent agents that pull global deps are an outlier (<5% per common CI patterns). Blast radius is loss of agent state, not pivot or supply-chain compromise — chain does not poison build artifacts. - Role multiplier — no canonical high-value-role deployment: pnpm is not a hypervisor, IdP, CA, PAM, backup, kernel-mode agent, or network edge. The HIGH/CRITICAL floor rule does not engage.
Why not higher?
No code execution, no credential theft, no integrity compromise — vector ends in directory deletion. Exploitation requires a victim to choose an attacker's package AND later trigger cleanup, which is a double-gated social engineering chain. EPSS sits at 0.27% with no in-the-wild activity and no KEV listing.
Why not lower?
Fix is trivially available and the destructive payload is real — a successful detonation can wipe a developer's home tree or a long-lived CI agent's state, which is meaningful operational pain. CWE-22 with A:H and AV:N is correctly above LOW. Treating this as IGNORE would leave CI fleets that don't refresh their pnpm binary stuck on a defective release.
What to do — in priority order.
- Upgrade pnpm to 10.34.2 or 11.5.3 — The clean fix. Bump
packageManagerfield inpackage.json, refresh Corepack pins (corepack prepare [email protected] --activate), and rebuild golden CI images. No mitigation SLA applies to MEDIUM under noisgate — go straight to the 365-day remediation window, but for a one-line version bump there's no reason to wait. - Pin npm registry to an internal proxy with allowlist — Artifactory / Verdaccio / Sonatype Nexus in front of the public registry blocks the typosquat vector for this AND future supply-chain bugs. Allowlist by package name + maintainer signature. Roll out across all dev and CI environments.
- Ban
pnpm add -gin CI pipelines — CI runners should neveradd -g— all dependencies belong in the project'spackage.json. Add a lint check or pre-commit hook that fails on-gflags in pipeline YAML. Forces project-local installs which are not in the vulnerable code path. - Enable Socket.dev / Snyk supply-chain scanning on new packages — Both flag suspicious
binentries with..segments and unusual scope patterns. Catches the next variant of this bug before it ships. - Snapshot dev-workstation home directories nightly — Cheap insurance against this and any other destructive supply-chain payload. Most enterprise endpoint-backup products (Code42, Druva) cover
~/.local/shareand dotfile trees by default.
- Network egress filtering — exploit travels inside legitimate npm traffic to registry.npmjs.org, which devs need open. Blocking npm breaks the toolchain.
- EDR alone — recursive deletion by a user-mode Node.js process under the user's own UID looks like normal pnpm activity. Without baselining mass-delete events specifically, behavior alerts don't fire.
- MFA / IdP hardening — irrelevant; no authentication boundary is crossed.
- WAF rules — there is no HTTP request surface to filter; the exploit is a package manifest, not network traffic to your services.
Crowdsourced verification payload.
Run on each developer workstation or CI runner that may have pnpm installed. Example: bash check-pnpm-55699.sh. No special privileges required — runs as the invoking user against the pnpm binary on $PATH.
#!/usr/bin/env bash
# noisgate verifier — CVE-2026-55699 (pnpm path traversal via bin field)
# Exits 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
if ! command -v pnpm >/dev/null 2>&1; then
echo "UNKNOWN: pnpm not installed on this host"
exit 2
fi
raw=$(pnpm --version 2>/dev/null || true)
if [[ -z "$raw" ]]; then
echo "UNKNOWN: pnpm --version returned nothing"
exit 2
fi
# Strip any pre-release suffix (e.g. 11.5.3-rc.1 -> 11.5.3)
ver=${raw%%-*}
IFS='.' read -r major minor patch <<<"$ver"
if ! [[ "$major" =~ ^[0-9]+$ && "$minor" =~ ^[0-9]+$ && "$patch" =~ ^[0-9]+$ ]]; then
echo "UNKNOWN: could not parse pnpm version '$raw'"
exit 2
fi
vuln=0
if (( major < 10 )); then
vuln=1
elif (( major == 10 )); then
if (( minor < 34 )) || { (( minor == 34 )) && (( patch < 2 )); }; then
vuln=1
fi
elif (( major == 11 )); then
if (( minor < 5 )) || { (( minor == 5 )) && (( patch < 3 )); }; then
vuln=1
fi
fi
if (( vuln == 1 )); then
echo "VULNERABLE: pnpm $raw is affected by CVE-2026-55699 — upgrade to 10.34.2 or 11.5.3"
exit 1
fi
echo "PATCHED: pnpm $raw is not affected by CVE-2026-55699"
exit 0
If you remember one thing.
[email protected] / [email protected] into your golden dev images, bump the packageManager field in all repo package.json files via a sweep PR, and refresh Corepack pins in CI. While you're in there, audit pipelines for any pnpm add -g calls and replace them with project-local installs. If you don't already proxy npm through Artifactory/Verdaccio with an allowlist, this CVE is your cheap excuse to fund that project — it kills the entire class of typosquat supply-chain bugs, not just this one. No need to panic-deploy: not KEV, EPSS 0.27%, no observed in-the-wild activity.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.