Jun 27, 2026·7 min read·4 visits
A path traversal vulnerability in pnpm's configDependencies handling allows malicious lockfiles to create arbitrary directories and symbolic links outside of node_modules, bypassing the execution boundaries of --ignore-scripts during package installation.
A high-severity path traversal vulnerability exists in the pnpm package manager. By crafting a malicious lockfile (pnpm-lock.yaml) with path traversal characters in the configDependencies block, an attacker can create arbitrary directories and symlinks outside the project's node_modules/.pnpm-config directory. This exploitation happens automatically during pnpm installation, even when executing with scripts disabled via the --ignore-scripts flag.
The pnpm package manager utilizes a unique virtual store layout inside node_modules/.pnpm and manages global packages using hard links to minimize disk space consumption. To support environment-specific properties and isolated configuration setups, the pnpm lockfile format (pnpm-lock.yaml) implements a configDependencies section. Under standard operational conditions, these configurations are isolated under a dedicated subdirectory: node_modules/.pnpm-config.
Because the installer processes dependencies declared in local workspace lockfiles under the assumption that they represent validated states, the keys and version strings within this configuration section are parsed directly to build directory trees. When users clone and inspect open-source or third-party repositories, they run installation commands to configure local packages. The installation stage represents a significant security boundary because it executes code before manual inspection can occur.
To limit risks associated with third-party software, security frameworks and automated testing pipelines often enforce the --ignore-scripts flag during installation to block life-cycle script executions (e.g., preinstall, postinstall). However, this path traversal bypasses those blocks. Because the installation code path itself handles the generation of these directory hierarchies, directory creation and symbolic link mapping occur regardless of whether script execution is restricted or disabled.
The root cause of this vulnerability lies in the lack of validation and sanitization of user-controlled parameters extracted from configDependencies during workspace setup. In vulnerable versions, pnpm reads the package names and version keys declared inside pnpm-lock.yaml and directly utilizes them to construct output filesystem paths using Node.js's standard utility function path.join.
The application constructs the output configuration workspace using a base path defined as const configModulesDir = path.join(opts.rootDir, 'node_modules/.pnpm-config'). When iterating over the keys in the dependency structure, pnpm evaluates path.join(configModulesDir, pkgName). Because Node.js's path.join resolves relative directory segments natively, any package name containing upward path traversal sequences (such as ../../) will escape the restricted base directory.
This behavior means that instead of creating directories and mapping links inside the protected node_modules/.pnpm-config directory, pnpm evaluates the destination path relative to the workspace root or the wider host filesystem depending on the number of traversal sequences used. The application then proceeds to execute the directory creation and symlink linkage (symlinkDir) using these resolved out-of-bounds target destinations, allowing arbitrary system structures to be modified or created based solely on the contents of the lockfile.
The vulnerability was resolved by introducing explicit strict validations within the package installer's parsing flow. The fix, implemented in commit 352ae489f1b14ffdc19d2c6eacb1b06b098c2ddc, adds verification checks that validate both the configuration names and versions before any path processing or folder creation functions are invoked.
Specifically, the patch enforces validation in normalizeConfigDeps.ts using two new assertion files: assertValidConfigDepName.ts and assertValidConfigDepVersion.ts. Below is the logical implementation introduced to validate names:
// config/deps-installer/src/assertValidConfigDepName.ts
import { PnpmError } from '@pnpm/error'
import validateNpmPackageName from 'validate-npm-package-name'
export function assertValidConfigDepName (name: string): void {
// Verify that the dependency name conforms to legitimate npm package name standards
if (!validateNpmPackageName(name).validForOldPackages) {
throw new PnpmError(
'INVALID_DEPENDENCY_NAME',
`The configDependencies in pnpm-workspace.yaml contains a dependency with an invalid name: ${JSON.stringify(name)}`,
{
hint: 'A dependency name must be a valid npm package name — a single `name` or `@scope/name` consisting of URL-friendly characters, with no leading `.` or `_`, and not equal to reserved names such as `node_modules`.',
}
)
}
}Additionally, the patch enforces that the configuration versions match exact semantic version formats (semver) to prevent an attacker from nesting directory traversal sequences in the version fields. This validation prevents attackers from exploiting variables used to organize the virtual store folders:
// config/deps-installer/src/assertValidConfigDepVersion.ts
import { PnpmError } from '@pnpm/error'
import semver from 'semver'
export function assertValidConfigDepVersion (name: string, version: string): void {
// Block non-semver compliant characters (preventing traversal inside version strings)
if (semver.valid(version) == null) {
throw new PnpmError(
'INVALID_CONFIG_DEP_VERSION',
`The config dependency "${name}" has an invalid version "${version}"`,
{ hint: 'A config dependency version must be an exact semver version.' }
)
}
}These checks guarantee that characters like . and / cannot be leveraged inside configuration dependency identifiers, thereby resolving the path traversal vulnerability. The fix is considered complete because it restricts inputs to safe, standardized schemas (npm package names and SemVer specs) before they reach any filesystem sink.
An attacker targets this vulnerability by modifying a project's pnpm-lock.yaml file to inject path traversal structures into the keys mapped within the configDependencies block. The target project is then uploaded to a version control hosting service or distributed through standard channels, waiting for a victim or automated system to run an installation command.
importers:
.:
configDependencies:
legit-config-dep:
specifier: '1.0.0'
version: '1.0.0'
'../../PWNED_CFGDEP':
specifier: '1.0.0'
version: '1.0.0'When a victim clones the repository and runs pnpm install, the installer parses the configuration structures and triggers path resolution. This execution path is represented by the following logical process flow:
Once the path resolution completes, the installer executes filesystem changes to map the cache directory to the local folder. If an attacker directs the path traversal to critical targets, they can create and link to folders that manipulate or overlay development settings, build configurations, or localized tooling directories.
The impact of this vulnerability is significant for multi-tenant CI/CD systems, automated analysis sandboxes, and developer workstations. In modern software engineering workflows, testing pipelines pull external code and run pnpm install automatically to construct test environments. Even when administrators disable post-install scripting mechanisms, the creation of arbitrary directories and symlinks still proceeds because it occurs inside pnpm's core binary routines.
Because the CVSS vector is rated as CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:L (CVSS Base Score: 8.3), the exploitation of this vulnerability has direct implications for integrity. The Scope Change (S:C) metric is particularly important: it means pnpm can write symbolic links outside of its designated sandbox (the workspace's node_modules directory), potentially affecting parent directories or system-wide configuration folders.
An attacker can utilize this primitive to execute symbolic link overwriting attacks, targeting adjacent folders in shared CI/CD agents or placing malicious symlinks that point to sensitive files. This can lead to local configuration hijacking or unauthorized file writing across build steps, depending on the permissions of the user execution context under which the installer is running.
Organizations should prioritize upgrading global and local pnpm installations. To secure systems running active workflows, update the package manager to the designated patched versions: version 10.34.4 or later for the v10 release branch, and version 11.8.0 or later for the v11 release branch.
Security teams can identify potential exploitation attempts in existing repositories by scanning lockfiles for structural path traversal sequences. Using standard command-line tools, administrators can audit codebase histories to detect anomalous entries within the target blocks:
grep -E "configDependencies:" -A 10 pnpm-lock.yaml | grep -E "\.\./"As a defense-in-depth measure, automated build environments and CI systems should enforce strict engine constraints in project settings, preventing local execution on insecure pnpm runtimes. This configuration can be formalized in the project's package.json file to block installation attempts if an obsolete version is detected:
"engines": {
"pnpm": ">=11.8.0"
}CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
pnpm pnpm | >= 0 < 10.34.4 | 10.34.4 |
pnpm pnpm | >= 11.0.0 < 11.8.0 | 11.8.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 8.3 |
| EPSS Score | N/A |
| Impact | Integrity (High) |
| Exploit Status | Proof of Concept (PoC) Available |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located beneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location outside of the restricted directory.
A path traversal vulnerability in the pnpm package manager's 'patch-remove' command allows an attacker to delete arbitrary files outside the patches directory. By manipulating configuration files like package.json, an attacker can specify a traversal path that the application deletes recursively without validating the path's containment.
An arbitrary file write vulnerability exists in Gonic, a music streaming server implementing the Subsonic API. Due to an unreachable guard clause combined with missing path containment validation in the playlist storage engine, authenticated users can write playlist contents to arbitrary filesystem paths with overly permissive directory permissions.
An incomplete mitigation of a predecessor vulnerability (GHSA-xvp4-phqj-cjr3 / CVE-2026-35671) in phpMyFAQ leaves sister administrative API endpoints vulnerable to Insecure Direct Object Reference (IDOR). Specifically, the `editUser` and `updateUserRights` endpoints lack object-level access controls, permitting authenticated low-privilege administrators to escalate their privileges or hijack SuperAdmin accounts.
A critical-severity Cross-Site Scripting (XSS) and Content-Type spoofing vulnerability in Remark42 (versions 1.6.0 through 1.15.0) allows remote attackers to execute arbitrary client-side script code via a crafted image proxy request.
CVE-2026-53462 is a heap Use-After-Free (UAF) vulnerability in ImageMagick's vector drawing subsystem, specifically within the coordinate allocation mechanism in CheckPrimitiveExtent. By parsing a crafted vector image (such as SVG or MVG) with extremely complex primitives, an attacker can trigger a memory reallocation failure. If the application fails to handle this allocation failure cleanly, it leaves a dangling pointer that can subsequently be accessed or freed again, causing memory corruption or an application crash.
A critical security flaw was identified in the Go package golang.org/x/crypto/ssh/agent. The vulnerability arises during the serialization of key constraints when adding SSH identities to a remote agent or an in-memory keyring. Specifically, custom constraint extensions, such as destination restrictions like restrict-destination-v00@openssh.com, were silently omitted from serialization in client requests. This omission allowed keys to be loaded into the remote agent with zero destination-based restrictions, enabling unauthorized users with access to the agent socket on intermediate hosts to authenticate to any downstream host without policy enforcement. The issue was resolved in version v0.52.0 of the golang.org/x/crypto library.