Jun 27, 2026·5 min read·3 visits
The pnpm package manager failed to validate file paths in its 'patch-remove' routine. A malicious actor could exploit this by embedding directory traversal sequences in the project's configuration, leading to arbitrary file deletion when a user or CI/CD runner executed the command.
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.
The pnpm package manager is an alternative to npm and yarn designed for performance and efficient disk utilization through hard links and a content-addressable store. To support custom dependency modification, pnpm offers a native patching framework. This subsystem exposes an attack surface when processing localized configuration assets such as package.json files or workspace lockfiles.
Historically, the 'patch-remove' command enabled developers to reverse local dependency patches and clean up corresponding patch files. However, the mechanism lacked security boundaries between the local project folder and the host filesystem. This design flaw introduced a path-traversal vulnerability that allows arbitrary file deletion.
Classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), the underlying issue stems from a lack of boundary validation during path resolution. An attacker who can influence configuration inputs can force the tool to clean up assets outside the designated directory structure, compromising filesystem integrity and causing denial-of-service conditions on vulnerable workstations and build runners.
The vulnerability is located in the core execution logic of the 'pnpm patch-remove' command. This utility is responsible for cleaning up local overrides configured within the 'patchedDependencies' block of the configuration. When executed, the command parses the workspace metadata to locate the patch files to delete. Prior to remediation, the application trusted the path strings provided in the configuration map directly without sanitization.
A typical 'patchedDependencies' configuration maps a target package to its localized patch file. Because the resolution routine accepted arbitrary string values, it did not verify that the resolved path remained inside the boundaries of the workspace's designated 'patches' folder. An attacker could introduce relative traversal sequences or absolute paths to target sensitive resources.
The system then executed a highly privilege-insensitive deletion routine using Node's 'fs.rm' with recursive and force options enabled. This API call resolved the user-controlled path relative to the workspace directory and deleted the target asset. Because no validation occurred, the utility deleted any host-accessible file or directory, bypassing structural folder boundaries.
The original vulnerable implementation resolved the target path and immediately invoked destructive file operations. To mitigate this, a subdirectory containment validation utility named 'isSubdirectory' was introduced to verify parent-child directory structures.
export function isSubdirectory (parentDir: string, childPath: string, pathUtils: PathUtils = path): boolean {
const relativePath = pathUtils.relative(parentDir, childPath)
return relativePath === '' || (
relativePath !== '..' &&
!relativePath.startsWith(`..${pathUtils.sep}`) &&
!pathUtils.isAbsolute(relativePath)
)
}The utility uses relative path calculations to guarantee that the child path resides within the parent directory. The 'patch-remove' routine was also updated to validate the target's physical location against symbolic link attacks. By utilizing 'fs.realpath' to canonicalize paths, the system ensures that symbolic links cannot be abused to point outside the workspace.
const targetPath = path.resolve(ctx.lockfileDir, patchFile)
if (
targetPath === ctx.patchesDir ||
!isSubdirectory(ctx.patchesDir, targetPath)
) {
throw new PnpmError('PATCH_FILE_OUTSIDE_PATCHES_DIR', `Patch file "${patchFile}" is outside the configured patches directory`)
}Additionally, the recursive file deletion API was replaced with 'fs.unlink'. This prevents directory erasure if a target resolves to a folder structure, limiting the tool to deleting individual files and symbolic links.
Exploitation requires the manipulation of the workspace configuration. This is typically achieved by submitting a malicious pull request to a public repository or distributing a compromised configuration inside an open-source archive. The attacker injects a path traversal sequence into the 'patchedDependencies' structure within 'package.json'.
{
"pnpm": {
"patchedDependencies": {
"target-package": "../../../../etc/passwd"
}
}
}When a developer or an automated CI/CD pipeline runs the 'pnpm patch-remove target-package' command, the execution flow proceeds as follows:
In automation environments, this primitive can be used to destroy credentials, configurations, or critical software dependencies to disrupt operations.
The primary impact of this vulnerability is arbitrary file deletion on the system hosting the pnpm process. The severity depends on the privileges of the user executing the 'pnpm' command. On local development workstations, this can lead to the loss of source code, configuration files, and authentication keys.
In continuous integration and continuous deployment (CI/CD) environments, the impact is significant. Automated pipelines often run with elevated permissions. If an attacker can trigger the vulnerability inside a build runner, they can destroy pipeline configurations, build assets, or deployment credentials. This results in pipeline denial of service or configuration tampering.
Because the vulnerability does not directly expose read capabilities, it represents a threat to data integrity and availability rather than confidentiality. However, deleting critical security controls can weaken a system's overall security posture.
To address the vulnerability, users must update pnpm to a version containing the path verification patch. The fix was integrated into the main branch and backported to the version 10 release line.
For environments where immediate patching is not possible, the following defense-in-depth measures are recommended:
| Product | Affected Versions | Fixed Version |
|---|---|---|
pnpm pnpm | < 10.0.0 (and versions without the containment patch) | v10.x (patched releases) |
@pnpm/plugin-commands-patching pnpm | < 10.0.0 | - |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) |
| Attack Vector | Local / Context-dependent execution of malicious workspace files |
| CVSS Severity | 7.1 (High Severity Recommendation) |
| Exploit Status | Concept-proven (PoC verified in test cases) |
| Impact Type | Arbitrary File and Folder Deletion |
| KEV Status | Not Listed |
The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize sequences such as '..' that can resolve to a location outside of the directory.
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.
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.