GHSA-qrv3-253h-g69c pnpm: Path traversal in configDependencies env lockfile allows symlink creation outside node_modules/.pnpm-config
Summary`pnpm` accepts package names from the env lockfile `configDependencies` section and uses those names directly when creating config dependency symlinks under `node_modules/.pnpm-config`.A malicious repository can commit a crafted `pnpm-lock.yaml` whose env-lockfile document contains a traversal-shaped config dependency name such as `../../PWNED_CFGDEP`. During `pnpm install`, pnpm installs the config dependency and creates a symlink at a path derived from that name.In local testing against pnpm `v11.5.1`, this caused pnpm to create a symlink outside the intended config dependency directory:This works with `--ignore-scripts`, so it does not rely on lifecycle script execution.Vulnerable behaviorThe vulnerable behavior appears to be that `configDependencies` keys from the env lockfile are trusted as package names and used in filesystem paths without rejecting traversal components.The relevant pattern is:If `pkgName` is attacker-controlled and contains `..`, then `path.join(configModulesDir, pkgName)` can resolve outside `node_modules/.pnpm-config`.ImpactA malicious project can cause pnpm to create symlinks outside the intended `node_modules/.pnpm-config` directory during install.This gives an attacker a filesystem write primitive in the victim project directory, and potentially outside it with deeper traversal payloads, depending on path permissions and platform behavior.The issue is especially relevant because:The malicious input is committed in `pnpm-lock.yaml`.The issue is triggered during `pnpm install`.It works with `--ignore-scripts`.It occurs in the config dependency installation path, before ordinary dependency installation.The user only needs to install a malicious or compromised repository.Local proof of conceptThe following local-only PoC creates a temporary project, starts a local fake registry on `127.0.0.1`, writes a malicious env-lockfile entry, runs pnpm, and checks whether pnpm created a symlink outside `node_modules/.pnpm-config`.Command used:Observed output:pnpm output:The PoC then detected the escaped symlink:Malicious lockfile structureThe malicious input is an env-lockfile `configDependencies` key containing traversal components:pnpm accepts the traversal-shaped name and reports it as installed:Security boundary violationThe intended config dependency root was:But pnpm created:This demonstrates that a config dependency name from the lockfile can escape the directory where config dependencies should be linked.Suggested remediationValidate every `configDependencies` key loaded from the env lockfile before using it as a package name or path component.Recommended fixes:Reject env-lockfile `configDependencies` names that are not valid npm package names.Reject names containing absolute paths, `.` components, `..` components, backslashes, or platform-specific path separators.Use containment-checked path joining before creating symlinks:resolve the final destination path,verify it remains inside `node_modules/.pnpm-config`,reject if it escapes.Apply the same validation to config dependency subdependencies and optional dependency names read from the env lockfile.Intersect env-lockfile `configDependencies` with the effective `pnpm-workspace.yaml` `configDependencies` before installing, so extra lockfile-only entries are rejected.A safe destination check should enforce behavior equivalent to:Name validation should happen before this check, not instead of it.
CVSS v3.1 score: 8.2