high · 8.8CVE-2026-55698Jun 26, 2026

CVE-2026-55698: pnpm Env Lockfile Package-Manager Integrity Bypass

Rohit Hatagale
AI Security Researcher, SecureLayer7

A malicious repository can commit a crafted pnpm-lock.yaml that tricks pnpm into downloading and executing an attacker-chosen pnpm binary instead of fetching a fresh, verified one from the registry.

Packagepnpm
Ecosystemnpm
Affected< 10.34.2
Fixed in10.34.2

The problem

When pnpm's automatic version-switching kicks in (the `packageManager` field differs from the running version), `switchCliVersion()` reads `packageManagerDependencies` from the committed env lockfile.

Before the patch, if the committed lockfile already contained matching `pnpm` and `@pnpm/exe` version entries, `resolvePackageManagerIntegrities()` treated them as fully resolved and skipped the registry re-fetch. That committed metadata, including any attacker-supplied `integrity` hash, was passed directly to `installPnpmToStore()` and the resulting binary was executed with `spawn.sync()` under the victim's privileges.

Proof of concept

bash
# Commit this as the first YAML document in pnpm-lock.yaml
# (the env-lockfile section pnpm reads for package-manager bootstrap)

importers:
  .:
    configDependencies: {}
    packageManagerDependencies:
      "@pnpm/exe":
        specifier: "9.3.0"
        version: "9.3.0"
      pnpm:
        specifier: "9.3.0"
        version: "9.3.0"
lockfileVersion: "9.0"
packages:
  /pnpm@9.3.0:
    resolution:
      integrity: sha512-poisoned   # replace with integrity of attacker-controlled tarball
snapshots:
  /pnpm@9.3.0: {}

# Trigger: victim runs any pnpm command in this repo while a different
# pnpm version is active (onFail=download path). pnpm enters
# switchCliVersion(), finds the version match, skips re-resolution,
# installs from the poisoned integrity field, and spawns the result.

The root cause is in `installing/env-installer/src/resolvePackageManagerIntegrities.ts`. Its version-only fast path returned immediately when both `pnpm` and `@pnpm/exe` version strings already matched the wanted version, without re-fetching from a trusted registry.

This is CWE-345 (insufficient authenticity verification) compounded by CWE-494 (download without integrity check) and CWE-829 (inclusion of functionality from untrusted control sphere).

The patch adds a `force` parameter to `resolvePackageManagerIntegrities()`. `switchCliVersion()` now always passes `force: true` for already-present package-manager entries, bypassing the version-only fast path, and reassigns the resolver's return value back to `envLockfile`.

The installer receives freshly resolved, registry-verified metadata instead of whatever the committed lockfile contains.

The fix

Upgrade pnpm to 10.34.2 (or 11.x equivalent containing patch commit a93449314f398cf4bdf2e28d033c02d37395ad22). The patched `switchCliVersion()` force-re-resolves package-manager bootstrap metadata through trusted registries before any install or execution, regardless of whether a matching version entry already exists in the committed env lockfile.

Reporter not attributed.

References: [1][2][3]