{"id":"CVE-2026-50014","title":"pnpm: Git Fetch Argument Injection via Lockfile resolution.commit","summary":"pnpm: Git Fetch Argument Injection via Lockfile resolution.commit","severity":"medium","cvss":6.4,"cwe":["CWE-88"],"vendor":"pnpm","product":"pnpm","ecosystem":"npm","affected":["pnpm < 10.34.0","pnpm >= 11.0.0, < 11.4.0"],"patched":["pnpm 10.34.0","pnpm 11.4.0"],"published":"2026-06-26","updated":"2026-06-26","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-p4xf-rf54-rj3x","references":[{"url":"https://github.com/pnpm/pnpm/security/advisories/GHSA-p4xf-rf54-rj3x"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-50014"},{"url":"https://github.com/advisories/GHSA-p4xf-rf54-rj3x"}],"tags":["ghsa","npm"],"epss":0.00318,"epssPercentile":0.25,"ingestedAt":"2026-06-29T13:24:35.087Z","slug":"CVE-2026-50014","body":"## Overview\n\n## Summary\n\npnpm passes the lockfile-controlled git `resolution.commit` value to `git fetch` without a `--` separator or commit-format validation. For git dependencies fetched through the shallow-fetch path, a malicious lockfile can replace the expected 40-character commit hash with a Git option such as `--upload-pack=<command>`. For SSH and local transports, `--upload-pack` can execute the supplied command. HTTPS transports ignore `--upload-pack`, so the practical attack surface is primarily SSH or local git dependencies.\n\n## Vulnerability Details\n\nThe vulnerable path is in `fetching/git-fetcher/src/index.ts`. When a git dependency host is configured for shallow fetching, pnpm calls:\n\n```typescript\nawait execGit(['fetch', '--depth', '1', 'origin', resolution.commit], { cwd: tempLocation })\n```\n\nBecause `resolution.commit` is appended before a `--` separator, Git can parse a commit value beginning with `-` as an option. The same file later passes the value to `git checkout` without a separator:\n\n```typescript\nawait execGit(['checkout', resolution.commit], { cwd: tempLocation })\n```\n\n`resolution.commit` comes from the lockfile and is typed as a plain `string`; pnpm does not validate it as a 40-character hexadecimal commit before passing it to Git.\n\n## Proof of Concept\n\n```bash\nbash autofyn_audit/exploits/vuln11_git_upload_pack_rce/exploit.sh\n# Creates a local bare git repo and triggers the shallow-fetch path.\n# Replaces the lockfile commit hash with '--upload-pack=touch /tmp/vuln11_pwned'.\n# Result: PASS -- /tmp/vuln11_pwned created by injected touch command.\n```\n\nThe PoC uses a local `file://githost/...` repository because the injection requires a local or SSH transport. HTTPS transport ignores `--upload-pack`.\n\n## Impact\n\nCode execution as the user running `pnpm install`, under specific transport conditions. The attacker must modify `pnpm-lock.yaml`, and the affected dependency must use SSH or local git transport. HTTPS transport (the common case) is immune.\n\n## Suggested Remediation\n\nAdd a `--` separator before lockfile-controlled git revision values. Validate `resolution.commit` matches `/^[0-9a-f]{40}$/i` before passing to Git.\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/vuln11_git_upload_pack_rce/exploit.sh)\n\n## Affected packages\n\n- `pnpm < 10.34.0`\n- `pnpm >= 11.0.0, < 11.4.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `pnpm 10.34.0`\n- `pnpm 11.4.0`","depth":"sunlit","depthScore":35,"depthScoreParts":{"impact":35.2,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}