← Back to Feed CACHED · 2026-06-30 20:33:47 · CACHE_KEY CVE-2026-55487
CVE-2026-55487 · CWE-346 · Disclosed 2026-06-25

pnpm is a package manager

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A package quietly changes its name tag after the bouncer checked it

pnpm gates lifecycle script execution (postinstall, preinstall, install) behind an allowBuilds allowlist that defenders curate to limit which dependencies can run arbitrary code at install time. In versions prior to 10.34.2 and 11.5.3, the identity used to evaluate that allowlist was derived from manifest fields the package itself controls rather than the resolved registry identity. A crafted package — directly or via a transitive dependency — can present a manifest whose name matches an allowlisted entry, satisfy the gate, and execute attacker-controlled lifecycle scripts during pnpm install. CWE-346 (Origin Validation Error) captures the class precisely.

Vendor's HIGH (7.5) with AC:H and UI:R is defensible but slightly understates the real story. The 'high attack complexity' assumes the attacker still has to land a malicious package in the dependency graph — which the 2025–2026 npm supply-chain wave (Shai-Hulud, TanStack, lifecycle-token exfil) has shown is *not* a high bar. Once the package is in the graph, the allowBuilds bypass turns a 'we hardened install scripts' control into a paper one. We keep it at HIGH rather than upgrading to CRITICAL because the trigger still requires a developer or CI to run pnpm install against a contaminated lockfile, not a network-reachable service.

"Manifest identity spoofing bypasses pnpm's allowBuilds gate — supply-chain class bug in a developer/CI-tier package manager."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Plant malicious package in dependency graph

Attacker publishes a typosquat, hijacks an abandoned maintainer account, or compromises a transitive dependency upstream (npm, internal Verdaccio, Nexus, Artifactory). Tooling commonly used in real campaigns includes npm-package-clone-script and the Shai-Hulud worm payload that mass-publishes hijacked versions.
Conditions required:
  • Attacker can publish to a registry the victim's lockfile resolves against
  • Victim consumes the package directly or transitively
Where this breaks in practice:
  • Internal registries with proxy-only mode and strict scope policies block public-package shadowing
  • Lockfile pinning + integrity hashes detect substitution but NOT first-introduction
Detection/coverage: Socket, Snyk, Phylum, and GitHub's dependency review surface suspicious new publishers and recently-changed maintainers — but coverage on transitive deps is uneven.
STEP 02

Craft a manifest that impersonates an allowBuilds-approved name

The attacker authors a package.json whose name field matches an entry the victim has whitelisted in pnpm.onlyBuiltDependencies / allowBuilds. Because pnpm pre-10.34.2 / pre-11.5.3 evaluates the gate against manifest-supplied identity rather than the registry-resolved tuple, the spoofed name is accepted as trusted.
Conditions required:
  • Victim project uses pnpm.onlyBuiltDependencies or equivalent allowBuilds policy (this control is *itself* the target — projects without it are arguably less impacted by this specific bug, more by lifecycle scripts generally)
  • Attacker knows or guesses an allowlisted name (often public — esbuild, sharp, node-sass, better-sqlite3)
Where this breaks in practice:
  • Projects that disable lifecycle scripts entirely (ignore-scripts=true) are immune
  • Projects with no allowBuilds policy never relied on this gate in the first place
Detection/coverage: No mainstream SCA tool currently diffs manifest-name vs resolved-name for allowBuilds policy evaluation.
STEP 03

Trigger install — lifecycle script executes

Developer runs pnpm install locally or CI runs it on PR/build. pnpm resolves the malicious package, evaluates allowBuilds against the spoofed manifest name, and executes the attacker's postinstall / preinstall script with the privileges of the install context.
Conditions required:
  • pnpm install is invoked
  • Network egress is available for second-stage payload
Where this breaks in practice:
  • Hermetic CI runners with egress allowlists limit C2
  • Developer workstations rarely have egress restriction
Detection/coverage: EDR on dev/CI hosts can catch suspicious node child-processes spawning shells, curl, or token reads — Wazuh / CrowdStrike / SentinelOne have rules for this pattern post-Shai-Hulud.
STEP 04

Credential exfil and lateral movement

Lifecycle script reads ~/.npmrc, ~/.aws/credentials, ~/.ssh/, GITHUB_TOKEN, NPM_TOKEN, and CI-injected secrets, then exfiltrates via DNS or HTTPS to an attacker endpoint. On CI, the same tokens are typically scoped to publish back to the registry — closing the worm loop (Shai-Hulud pattern).
Conditions required:
  • Tokens present in environment or filesystem
  • Egress to attacker infrastructure
Where this breaks in practice:
  • Short-lived OIDC tokens (GitHub Actions, GitLab JWT) limit blast radius vs long-lived PATs
  • Secret scanners on outbound traffic (Nightfall, GitGuardian inline) can catch token shapes
Detection/coverage: CrowdStrike, SentinelOne, Wazuh, and GitHub's secret-scanning post-leak alerts cover the exfil tail; pre-exfil JS behavior is harder.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed exploitation of *this specific CVE* as of 2026-07-01, but the class (lifecycle-script abuse via supply-chain) is under active mass exploitation — Shai-Hulud worm, TanStack compromise (@tanstack/* GHSA-g7cv-rxg3-hmpx), and the Microsoft-reported 33-package dependency-confusion campaign all chain on this same primitive.
Proof-of-conceptConceptual PoC implied by the GHSA writeup; no public weaponized repo named as of disclosure. Trivial to reproduce — name field in package.json is the entire bypass.
EPSS0.0011 (~11th percentile) — low predicted exploitation in next 30 days, but EPSS lags supply-chain class bugs because telemetry is endpoint-centric, not registry-centric.
KEV statusNot listed. CISA KEV historically lags npm/pnpm ecosystem bugs by months.
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H — AV:N is generous (the attack vector is the registry, not the network), UI:R is correct (developer must run install), AC:H reflects needing a poisoned package in the graph.
Affected versionspnpm < 10.34.2 on the 10.x line; pnpm < 11.5.3 on the 11.x line. pacquet (Rust port) affected in the same window.
Fixed versionspnpm 10.34.2 and pnpm 11.5.3. No distro backports — this is a Node tooling package, upgrade via npm i -g pnpm@latest or Corepack pin.
Exposure datapnpm self-reports ~30% of the JavaScript build-tool market by late 2025 surveys; Shodan/Censys not applicable (no network listener). Internal CI fleets are the real exposure surface.
Disclosure2026-06-25 via GitHub Security Advisory database, coordinated with the pnpm maintainers.
ReporterCredited to the pnpm security team and external GHSA submitters — see advisory for full credits.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to HIGH (7.2/10)

Verdict held at HIGH because the decisive factor is role multiplier — the affected component runs in developer and CI build contexts where a single successful chain harvests source-tree-wide credentials and publish tokens, which is the canonical supply-chain pivot pattern. We do not upgrade to CRITICAL because exploitation still requires landing a poisoned package in the victim's dependency graph AND a developer or CI invoking pnpm install — not a network-reachable trigger.

HIGH Root cause classification (origin validation / CWE-346)
HIGH Patched versions (10.34.2 / 11.5.3)
MEDIUM In-the-wild exploitation status for this specific CVE
MEDIUM Installed-base share in CI vs developer workstations

Why this verdict

  • Friction audit — entry requires supply-chain landing: attacker must first publish a malicious package the victim resolves. This is no longer a high bar in 2026 (Shai-Hulud, TanStack), but it is still not internet-reachable on its own — supports HIGH, not CRITICAL.
  • Friction audit — UI:R is real: pnpm install must be invoked. On CI this is automatic on every PR; on workstations it's frequent. Not a wormable network primitive.
  • Friction audit — defense-in-depth bypass, not RCE primitive: the bug specifically defeats allowBuilds, a *hardening control*. Projects with ignore-scripts=true are unaffected; projects with no allowBuilds policy never benefited from the gate — narrowing the at-risk population to teams who deliberately tried to harden.
  • Role multiplier — CI/CD build server (canonical high-value role): chain succeeds end-to-end. Blast radius: every secret in the CI environment, every artifact-registry publish token, every cloud OIDC role assumable from that runner. This is the supply-chain pivot pattern — verdict floor is HIGH.
  • Role multiplier — developer workstation: chain succeeds. Blast radius: local SSH keys, gh tokens, AWS keys, browser sessions on macOS keychain. Worm potential via published packages from compromised maintainer credentials.
  • Role multiplier — production runtime: rarely runs pnpm install in production. Limited.

Why not higher?

Not CRITICAL because there is no unauthenticated network trigger and no auto-execution without a developer or CI initiating pnpm install. EPSS is low and there is no published weaponized exploit chain naming this specific CVE. A CRITICAL bucket implies hours-to-days mass exploitation pressure; the evidence here is class-level not CVE-specific.

Why not lower?

Not MEDIUM because the bug defeats a hardening control specifically designed to stop the supply-chain class that *is* being mass-exploited in 2026. The role multiplier on CI runners (token egress, publish-back worm potential) holds the verdict floor at HIGH regardless of EPSS.

05 · Compensating Control

What to do — in priority order.

  1. Set ignore-scripts=true globally in CI — Configure ~/.npmrc and project-level .npmrc with ignore-scripts=true on every CI runner. This bypasses the broken allowBuilds gate entirely by refusing to run *any* lifecycle script. Apply within the noisgate HIGH mitigation SLA — 30 days. For packages that legitimately need a build step (esbuild, sharp), pre-build artifacts in a sandboxed pipeline and consume the prebuilt tarballs.
  2. Pin pnpm via Corepack and block drift — In package.json set "packageManager": "[email protected]" (or 10.34.2 for the LTS line) and enable Corepack in CI. Fail the build if the resolved pnpm version is below the fix. Deploy within 30 days alongside the upgrade rollout.
  3. Enforce internal proxy registry with publisher allowlist — Verdaccio / Nexus / Artifactory in proxy-only mode, blocking shadowing of internal scopes by public packages, and requiring approval for any new transitive dependency publisher. Closes the upstream-landing precondition.
  4. Scope CI tokens to short-lived OIDC — Replace long-lived NPM_TOKEN and PATs with OIDC federation (GitHub Actions → cloud, GitHub Actions → npm publish via trusted publishers). Reduces the value of any single install-time exfil.
  5. EDR rules for node child processes — On dev workstations and CI runners, alert on node spawning sh, bash, cmd.exe, curl, wget, or reading ~/.aws/, ~/.ssh/, ~/.npmrc during an install window. CrowdStrike, SentinelOne, Wazuh all support this.
What doesn't work
  • Lockfile pinning + integrity hashes — only protects against *substitution* of an already-resolved package; does NOT protect against the first introduction of a poisoned package or a hijacked publisher.
  • SBOM generation alone — visibility without an allowlist policy does not block install-time script execution.
  • Web Application Firewall — there is no HTTP request path to filter; the attack vector is the package registry resolved by the developer's machine.
  • pnpm audit — reports known CVEs in dependencies but does not validate manifest-vs-resolved-name identity.
06 · Verification

Crowdsourced verification payload.

Run on every developer workstation and CI runner where pnpm is installed. Invoke as bash check-pnpm-cve-2026-55487.sh from any shell — no root required. Detects pnpm version, compares against the 10.34.2 / 11.5.3 fix lines, and flags whether ignore-scripts is set as a compensating control.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verification: CVE-2026-55487 (pnpm manifest identity spoofing)
# Output: VULNERABLE / PATCHED / UNKNOWN
set -u

if ! command -v pnpm >/dev/null 2>&1; then
  echo "UNKNOWN: pnpm not on PATH"
  exit 2
fi

VER=$(pnpm --version 2>/dev/null | tr -d '[:space:]')
if [ -z "$VER" ]; then
  echo "UNKNOWN: could not read pnpm --version"
  exit 2
fi

MAJOR=${VER%%.*}
REST=${VER#*.}
MINOR=${REST%%.*}
PATCH=${REST#*.}

ver_ge() {
  # ver_ge A.B.C X.Y.Z -> 0 if A.B.C >= X.Y.Z
  printf '%s\n%s\n' "$2" "$1" | sort -V -C
}

IGNORE_SCRIPTS=$(pnpm config get ignore-scripts 2>/dev/null | tr -d '[:space:]')

FIXED=1
case "$MAJOR" in
  10)
    if ver_ge "$VER" "10.34.2"; then FIXED=0; fi
    ;;
  11)
    if ver_ge "$VER" "11.5.3"; then FIXED=0; fi
    ;;
  *)
    if [ "$MAJOR" -gt 11 ] 2>/dev/null; then FIXED=0; fi
    ;;
esac

if [ "$FIXED" -eq 0 ]; then
  echo "PATCHED: pnpm $VER (ignore-scripts=$IGNORE_SCRIPTS)"
  exit 0
fi

if [ "$IGNORE_SCRIPTS" = "true" ]; then
  echo "VULNERABLE: pnpm $VER (mitigated by ignore-scripts=true, but upgrade still required)"
  exit 1
fi

echo "VULNERABLE: pnpm $VER (no fix, ignore-scripts not enforced)"
exit 1
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: inventory pnpm versions across every CI runner and developer laptop (the verification script does this), then pin "packageManager": "[email protected]" (or 10.34.2 for teams on the LTS line) in every repo. Per the noisgate mitigation SLA for HIGH, you have ≤ 30 days to roll out a compensating control — set ignore-scripts=true on all CI runners this week as the fast win, since it neutralizes the broken allowBuilds gate entirely. Per the noisgate remediation SLA, full pnpm upgrade across the org must complete within ≤ 180 days, but CI fleets should be done within 30 because that is where token blast-radius is largest. There is no KEV-driven hours-deadline override here, but the 2026 supply-chain wave (Shai-Hulud, TanStack) means treating this as backlog is the wrong call — prioritize it above generic dev-tool CVEs.

Sources

  1. GitHub Advisory Database
  2. pnpm 11 release discussion
  3. TanStack npm packages Shai-Hulud writeup (Snyk)
  4. TanStack GHSA-g7cv-rxg3-hmpx
  5. Microsoft — 33 malicious npm packages, dependency confusion
  6. npm Supply Chain Security in 2026 (Mondoo)
  7. pnpm CVE history (cvedetails)
  8. npm manifest confusion class (CSO)
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.