high · 8.8CVE-2026-50016Jun 26, 2026

CVE-2026-50016: pnpm Transitive Dependency Alias Path Traversal via Symlink Replacement

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A malicious npm registry package can smuggle path traversal segments into a dependency alias, causing pnpm to replace project directories like .git/hooks with symlinks to attacker-controlled code duri

Packagepnpm
Ecosystemnpm
Affected< 10.34.0
Fixed in10.34.0

The problem

pnpm reads dependency alias names directly from registry package metadata and joins them onto the target node_modules path without validating for `..` segments. A transitive package can declare an alias like `@x/../../../../../.git/hooks` pointing to an attacker payload package, and pnpm will create a symlink at the traversed destination during install.

The impact bypasses the `--ignore-scripts` safety expectation. No lifecycle scripts run during install, yet the victim project's `.git/hooks`, `scripts/`, `.github/actions/`, or other directories are silently replaced. The payload then executes the next time the user runs `git commit`, `pnpm test`, or any other tool that reads those directories.

Proof of concept

bash
# bad@1.0.0 package.json (the malicious transitive package published to a registry)
{
  "name": "bad",
  "version": "1.0.0",
  "dependencies": {
    "@x/../../../../../.git/hooks": "npm:payload-hooks@1.0.0"
  }
}

# payload-hooks@1.0.0/pre-commit (the hook that runs on git commit)
#!/bin/sh
echo PWNED >&2
exit 0

# victim installs a normal-looking package that pulls in bad@1.0.0 transitively
pnpm install normal@1.0.0 --ignore-scripts

# pnpm resolves the traversal alias and creates:
# <project>/.git/hooks -> node_modules/.pnpm/payload-hooks@1.0.0/node_modules/payload-hooks

# later, without any further attacker action:
git commit -m "trigger"
# stderr: PWNED

The root cause is that pnpm passed alias keys from package metadata directly into `path.join(parentPackageNodeModulesDir, aliasKey)` with no check that the normalized result stayed inside the intended node_modules subtree. A scoped-looking alias such as `@x/../../../../../.git/hooks` normalizes cleanly past the project root and lands at `.git/hooks`, where pnpm then places a symlink to the resolved payload package directory.

The fix in 10.34.0 adds validation at both read time (when aliases are parsed from a package manifest) and at link time (when the symlink is created), rejecting any alias whose normalized path escapes the node_modules boundary. This matches CWE-23 (Relative Path Traversal): unsanitized user-controlled path components used to construct a filesystem path.

The fix

Upgrade pnpm to 10.34.0 or later (or 11.4.0+ on the v11 track). Both releases reject dependency aliases containing path-traversal segments when reading them from a package manifest or symlinking them into node_modules. No configuration change is needed; the validation is on by default.

Reporter not attributed.

References: [1][2][3]