{"id":"CVE-2026-50021","title":"pnpm Has an Integrity Check Bypass via Missing Lockfile Integrity Field","summary":"pnpm Has an Integrity Check Bypass via Missing Lockfile Integrity Field","severity":"medium","cvss":6.8,"cwe":["CWE-354"],"vendor":"pnpm","product":"pnpm","ecosystem":"npm","affected":["pnpm >= 11.0.0, < 11.4.0","pnpm < 10.34.1"],"patched":["pnpm 11.4.0","pnpm 10.34.1"],"published":"2026-06-26","updated":"2026-06-26","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-q6j5-fjx5-2mc3","references":[{"url":"https://github.com/pnpm/pnpm/security/advisories/GHSA-q6j5-fjx5-2mc3"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-50021"},{"url":"https://github.com/advisories/GHSA-q6j5-fjx5-2mc3"}],"tags":["ghsa","npm"],"epss":0.00181,"epssPercentile":0.06837,"ingestedAt":"2026-06-29T13:24:35.088Z","slug":"CVE-2026-50021","body":"## Overview\n\n## 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)\n\n## Affected packages\n\n- `pnpm >= 11.0.0, < 11.4.0`\n- `pnpm < 10.34.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `pnpm 11.4.0`\n- `pnpm 10.34.1`","depth":"sunlit","depthScore":37,"depthScoreParts":{"impact":37.4,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}