# pnpm v10+ Bypass "Dependency lifecycle scripts execution disabled by default"

pnpm v10's default lifecycle-script blocking only guards the build phase, so git-hosted dependencies still run prepare/prepublish/prepack scripts during fetch, yielding RCE on pnpm install.

- Payout: Research
- Severity: high
- Program: pnpm
- Found by: orenyomtov
- Disclosed: 2026-01-07
- CVE: CVE-2025-69264
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-379q-355j-w6rj
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-379q-355j-w6rj
- Tags: area:oss domain:oss impact:integrity impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:misconfiguration vuln:rce

**Target:** pnpm package manager install/fetch pipeline (git-hosted dependencies)
**Root cause:** The v10 default sets onlyBuiltDependencies=[] which is only consulted in the BUILD phase (exec/build-modules); git dependencies are prepared in the FETCH phase via preparePackage(), which checks only ignoreScripts (default false) and even strips ignore-scripts from rawConfig, then runs prepare/prepublish/prepack hooks.
**Steps:**
1. Create a package with `"scripts": {"prepare": "node -e \"require('fs').writeFileSync('/tmp/pwned.txt', 'RCE!')\""}`
2. Init it as a git repo and commit
3. In a victim project add dependency `"malicious-pkg": "git+file:///path/to/malicious-pkg"` (must git clone, not tarball)
4. Run `pnpm install` on pnpm >=10.0.0, <10.26.0
5. Observe prepare script executes with no approval prompt; check `/tmp/pwned.txt`
**How found:** Compared the v10 script-blocking implementation: onlyBuiltDependencies=[] is enforced in the BUILD phase (exec/build-modules), but git deps go through a separate FETCH-phase preparePackage() that only checks ignoreScripts and explicitly omits ignore-scripts from rawConfig — so the allowlist is never consulted.
**Exploitation:** Attacker publishes or compromises a dependency that references a malicious git dependency (git+https, github:, gitlab:, bitbucket:, git+ssh, git+file). On victim `pnpm install`, preparePackage() runs the git package's prepare/prepublish/prepack scripts during fetch, executing arbitrary code with the victim's privileges — enabling secret exfiltration, backdoors, persistence, reverse shells in CI/CD.
**Escalation:** Supply-chain: compromise a transitive dependency to inject the malicious git dependency, so any consumer running pnpm install is hit without consent.
**Insight:** When a package manager adds a 'scripts disabled by default' control, check every code path that runs lifecycle hooks — fetch/prepare paths for git, tarball, and local deps are often separate from the build path and may not consult the same allowlist.

## Proof of concept (copied exactly from the original report)

**Victim package.json with git dependency**

```
{
     "dependencies": {
       "malicious-pkg": "git+file:///path/to/malicious-pkg"
     }
   }
```

