{"id":"CVE-2026-50021","aliases":["GHSA-q6j5-fjx5-2mc3"],"url":"https://o3.security/vulnerability/CVE-2026-50021","summary":"pnpm: Integrity Check Bypass via Missing Lockfile Integrity Field","details":"## Summary\n\npnpm's tarball extraction worker skips integrity verification when the `integrity` field is absent from the lockfile resolution. If an attacker can both modify `pnpm-lock.yaml` to remove the `integrity:` field and cause the referenced registry URL to serve altered package content, `pnpm install --frozen-lockfile` can install the altered package without an integrity error. npm's `npm ci` enforces integrity by default; pnpm's behavior of silently skipping verification is a pnpm-specific fail-open gap.\n\n## Vulnerability Details\n\nThe `addTarballToStore` function in `worker/src/start.ts` (lines 189-204) checks `if (integrity)` before verifying the tarball hash. The `TarballResolution` type declares `integrity` as optional (`integrity?: string`). When the lockfile omits the `integrity` field, the guard evaluates to `false`, skipping hash verification entirely. The worker then computes a new hash from the unverified content and stores it as legitimate.\n\n```typescript\n// worker/src/start.ts:189-204\nfunction addTarballToStore ({ buffer, storeDir, integrity, ... }: TarballExtractMessage) {\n  if (integrity) {           // false when integrity is undefined\n    const { algorithm, hexDigest } = parseIntegrity(integrity)\n    const calculatedHash = crypto.hash(algorithm, buffer, 'hex')\n    if (calculatedHash !== hexDigest) {\n      return { status: 'error', error: { type: 'integrity_validation_failed', ... } }\n    }\n  }\n  return {\n    status: 'success',\n    value: { integrity: integrity ?? calcIntegrity(buffer) },\n  }\n}\n```\n\n## Proof of Concept\n\n```bash\nbash autofyn_audit/exploits/vuln1_integrity_bypass/exploit.sh\n# Publishes a package, generates lockfile, republishes tampered version,\n# strips integrity field, re-runs install --frozen-lockfile.\n# Result: PASS -- tampered package installed without integrity error.\n```\n\n## Impact\n\nSupply chain compromise in environments where an attacker can both alter the lockfile and cause the referenced registry URL to serve altered package content. The `--frozen-lockfile` flag does not fail closed when the integrity field is missing.\n\n## Suggested Remediation\n\nRequire an `integrity` field for remote tarball resolutions. Change the `if (integrity)` guard to fail when integrity is absent for non-local packages. When `--frozen-lockfile` is active, reject lockfile entries that lack integrity for remote packages.\n\n---\n\n> Discovered by [AutoFyn](https://github.com/SignalPilot-Labs/AutoFyn)\n> Full audit report: [audit_report.md](https://github.com/tempcollab/pnpm/blob/main/autofyn_audit/audit_report.md)\n> Exploit script: [exploit.sh](https://github.com/tempcollab/pnpm/blob/main/autofyn_audit/exploits/vuln1_integrity_bypass/exploit.sh)","published":"2026-06-25T16:48:27.901Z","modified":"2026-09-12T11:46:14.596032879Z","cvss":{"score":6.8,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N"},"epss":{"score":0.00181,"percentile":0.07944,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"pnpm","fixedVersion":"11.4.0"},{"ecosystem":"npm","name":"pnpm","fixedVersion":"10.34.1"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/50xxx/CVE-2026-50021.json"},{"type":"ADVISORY","url":"https://github.com/pnpm/pnpm/security/advisories/GHSA-q6j5-fjx5-2mc3"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-50021"},{"type":"PACKAGE","url":"https://github.com/pnpm/pnpm"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-12T11:46:14.596032879Z"}}