CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-FR4H-3CPH-29XV

GHSA-FR4H-3CPH-29XV: Path Traversal and Directory Hijacking in pnpm and pacquet Dependency Resolution

Amit Schendel
Amit Schendel
Senior Security Researcher

Jun 27, 2026·8 min read·12 visits

Executive Summary (TL;DR)

A path traversal vulnerability in pnpm and pacquet under 'hoisted' mode allows attackers to overwrite files outside the installation directory or hijack binaries inside the virtual store via malicious lockfiles.

GHSA-FR4H-3CPH-29XV is a high-severity path traversal vulnerability in pnpm and its Rust-based port pacquet. The flaw manifests when using the hoisted node-linker configuration, allowing an attacker to manipulate the lockfile to resolve relative traversal sequences or target reserved subdirectories, leading to arbitrary file write or execution hijacking.

Vulnerability Overview

The fast Node.js package manager, pnpm, and its Rust port, pacquet, are designed to optimize disk space and speed up dependency installation through content-addressable storage. In a typical default installation, pnpm uses a hard-link strategy alongside a virtual store to prevent duplicate modules on the file system. However, when users configure the manager to run with the hoisted node-linker topology (nodeLinker: hoisted), pnpm alters its default isolated behavior and falls back to a flattened layout similar to classic npm. This topology aims to maximize compatibility with legacy Node.js projects by positioning dependencies directly inside the top-level node_modules directory.

This specific configuration exposes an attack surface during the resolution of dependencies defined within the package's lockfile (pnpm-lock.yaml). When an installation command is executed in a headless context (such as clean developer setups or automated CI/CD jobs), the package manager builds a hoisted graph using dependency keys straight from the untrusted lockfile. Under this threat model, an attacker who can modify or submit a malicious lockfile can control dependency alias keys, paving the way for directory traversal attacks and structural filesystem modifications.

This security vulnerability, tracked under GitHub Security Advisory GHSA-FR4H-3CPH-29XV (internal tracking ID CAND-PNPM-059), belongs to the CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and CWE-73 (External Control of File Name or Path) weakness classes. If exploited, an unauthenticated attacker can execute arbitrary file writes outside the project directory or hijack execution paths of build tools within the node_modules workspace. The vulnerability has been confirmed to affect pnpm versions prior to 10.34.4 and versions 11.x prior to 11.7.0, along with the pacquet Rust port.

Root Cause Analysis

To analyze the root cause of this vulnerability, we must examine how pnpm constructs its dependency resolution mappings. When parsing the dependency graph in hoisted mode, the engine reads the alias mapping defined under each dependency block in the lockfile. In vulnerable versions, the resolver processes these alias strings without validating them against standard package-naming rules, immediately performing path-joining operations to designate where each symbolic link or directory structure must be materialized on disk.

The bug resides within the hoisted graph building logic, where the system calls the native path.join API with the base node_modules path and the unvalidated dependency alias retrieved from the lockfile snapshot. Because the native path.join algorithm automatically parses and resolves relative path indicators like .., any input containing directory traversal elements will propagate upward through the filesystem tree. Consequently, an alias defined as ../../../escape is resolved relative to the target node_modules folder, causing the destination pointer to escape the confinement boundary completely.

Furthermore, the system historically relied on an insufficient path containment check that compared the starts of resolved paths. This legacy check was easily bypassed by targeting pnpm's internal directories, such as node_modules/.bin/, which are located inside the top-level directory and therefore share the identical prefix. This allowed attackers to point their malicious aliases to internal binary shims, effectively overwriting legitimate executable paths while remaining within the technical boundaries of the original validation check.

Code Analysis

The security vulnerability has been remediated in commit 352ae489f1b14ffdc19d2c6eacb1b06b098c2ddc. This patch replaces the insecure path concatenation routine with a strict input validation check leveraging validate-npm-package-name and robust path containment boundaries.

// Insecure pattern in vulnerable versions (lockfileToHoistedDepGraph.ts):
// The engine directly joined the target folder using unvalidated keys.
const dir = path.join(modules, dep.name)
const depLocation = path.relative(opts.lockfileDir, dir)
// Patched logic (lockfileToHoistedDepGraph.ts):
// The code now invokes safeJoinModulesDir to sanitize and check boundaries.
import { safeJoinModulesDir } from '@pnpm/symlink-dependency'
 
const dir = safeJoinModulesDir(modules, dep.name)
const depLocation = path.relative(opts.lockfileDir, dir)

The implementation of safeJoinModulesDir acts as a crucial defensive barrier. It imports validate-npm-package-name to verify that the alias is structural and adheres to standard npm constraints, which naturally prohibits relative traversals and special system directories.

// Implementation in safeJoinModulesDir.ts:
export function safeJoinModulesDir (modulesDir: string, alias: string): string {
  // 1. Strict name check to prevent reserved or traversal inputs
  if (!validateNpmPackageName(alias).validForOldPackages) {
    throw invalidDependencyNameError(modulesDir, alias)
  }
  const link = path.join(modulesDir, alias)
  const resolvedDir = path.resolve(modulesDir)
  const resolvedLink = path.resolve(link)
  // 2. Defensive prefix check ensures files reside within the directory
  if (resolvedLink === resolvedDir || !resolvedLink.startsWith(resolvedDir + path.sep)) {
    throw invalidDependencyNameError(modulesDir, alias, resolvedLink)
  }
  return link
}

This multi-tiered defense is highly effective. The validation phase rejects names with leading dots, uppercase characters outside allowed parameters, and reserved phrases, which immediately stops exploitation attempts using traversal payloads or targeting sensitive directories like .bin. By adding validate-npm-package-name, the patch prevents any exploitation paths through malformed aliases, while the secondary prefix check remains as a fallback mechanism.

Exploitation Methodology

An exploitation attempt against this vulnerability requires an attacker to inject a crafted lockfile into a repository. Because development workflows often accept modifications to pnpm-lock.yaml during standard pull requests, this file represents a low-complexity vector for supply chain attacks. The attacker alters the importers block in the lockfile to define a dependency with a relative path as the key, pointing to a remote registry or a localized workspace definition.

# Example payload in pnpm-lock.yaml
lockfileVersion: '9.0'
importers:
  .:
    dependencies:
      '../../../.ssh/authorized_keys': '1.0.0'
packages:
  '../../../.ssh/authorized_keys@1.0.0':
    resolution: { integrity: 'sha512-...' }

When a victim clones the repository and runs pnpm install, the installer reads the dependency tree and processes the malicious entry. Under hoisted mode, the client attempts to link the fetched directory contents using the calculated path, causing the system to write files directly into the victim's SSH directory. Alternatively, targeting .bin/tsc will write an executable script into the build tools directory, causing arbitrary script execution when the compilation process is triggered.

This attack vector requires no special privileges on the target system and relies purely on standard user interaction. The execution phase leverages existing shell access, meaning that any pipeline executing the build stage of a compromised repository will execute the payload under the privileges of the active installer.

Impact Assessment

The impact of this path traversal vulnerability is severe and directly affects system integrity. In a localized development environment, arbitrary file write permissions can escalate to remote code execution. For example, overwriting system profiles, shell rc files, or application-specific compilers can allow attackers to establish persistent backdoors that execute automatically upon system startup or when developers run basic commands.

In continuous integration and delivery (CI/CD) environments, the vulnerability is similarly severe. Because build agents typically possess credentials, API keys, and deployment certificates, compromising the runner during the initial pnpm install step allows immediate access to those secrets. An attacker who hijacks build tools like the TypeScript compiler can inject backdoors into production code artifacts before they are compiled and packaged.

The CVSS score is rated at 7.1 (High) with a vector of CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L. While confidentiality is not directly impacted during the initial write, the direct progression to execution hijacking makes this a priority for defensive teams.

Remediation & Defensive Practices

The recommended and complete fix for this vulnerability is upgrading the package manager to a patched version. Development teams must immediately audit global installations and ensure local lockfiles require secure engines. For teams using the 10.x release cycle, pnpm must be updated to version 10.34.4 or above. For organizations using the latest 11.x releases, version 11.7.0 is the minimum safe release.

# Upgrade to the latest secure version globally
npm install -g pnpm@latest
# Alternatively, update using Corepack
corepack prepare pnpm@latest --activate

If upgrading the utility is not an immediate option, several workarounds can reduce the attack surface. Disabling the hoisted mode by removing nodeLinker: hoisted from .npmrc forces the package manager to use its default isolated symlink structure. Since isolated installations use a different code path that does not perform flat hoisting, the vulnerability is not exposed in this mode. Additionally, workflows in CI/CD environments must employ the --frozen-lockfile command to ensure that any unexpected modifications to lockfile schemas halt the process automatically.

Finally, development teams should implement automated check stages that parse lockfiles for relative paths or atypical character structures before execution. Incorporating automated security pipelines that flag keys with path traversal indicators ensures security even if developers are running older, unpatched package manager versions.

Official Patches

pnpmFix Patch Commit (pnpm/pnpm)
pnpmpnpm v10.34.4 Release Tag
pnpmpnpm v11.7.0 Release Tag

Fix Analysis (1)

Technical Appendix

CVSS Score
7.1/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L

Affected Systems

pnpm CLIpacquet Rust port

Affected Versions Detail

Product
Affected Versions
Fixed Version
pnpm
pnpm
< 10.34.410.34.4
pnpm
pnpm
>= 11.0.0, < 11.7.011.7.0
AttributeDetail
CWE IDCWE-22, CWE-73
Attack VectorNetwork / Remote
CVSS Score7.1
Exploit StatusProof-of-Concept
ImpactArbitrary File Write / Code Execution
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1566Phishing
Initial Access
T1203Exploitation for Client Execution
Execution
T1574.006Hijack Execution Flow: Dynamic Linker Hijacking
Persistence
CWE-22
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The software uses external input to construct a pathname that is intended to identify a file or directory that is located under 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 that is outside of the restricted directory.

References & Sources

  • [1]Official GitHub Advisory
  • [2]pnpm Security Advisory

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•2 days ago•CVE-2026-54068
5.9

CVE-2026-54068: Unauthenticated Server-Side Template Injection and SQLite Exfiltration in SiYuan PKM

An authentication bypass in the SiYuan personal knowledge management system before version 3.7.0 exposes a dynamic icon rendering endpoint. This endpoint processes client-supplied Go template directives. By submitting a crafted request, an unauthenticated remote attacker can leverage registered database template functions to execute arbitrary read-only SQL queries and exfiltrate workspace contents.

Amit Schendel
Amit Schendel
14 views•5 min read
•2 days ago•CVE-2026-54069
9.1

CVE-2026-54069: Authentication Bypass in SiYuan Note via Origin Header Spoofing

CVE-2026-54069 is a critical authentication bypass vulnerability in the SiYuan Note personal knowledge management system. The flaw is located in the HTTP server's middleware handling API authorization, which unconditionally trusts requests carrying a 'chrome-extension://' scheme in the Origin HTTP header, granting administrative access without validating API tokens.

Alon Barad
Alon Barad
11 views•5 min read
•2 days ago•CVE-2026-54089
9.1

CVE-2026-54089: Authentication Bypass by Spoofing in File Browser

CVE-2026-54089 is a critical authentication bypass vulnerability in File Browser affecting instances configured with proxy-based authentication. An unauthenticated remote attacker with direct network access can impersonate arbitrary users or register new accounts by spoofing configured HTTP headers.

Amit Schendel
Amit Schendel
8 views•7 min read
•2 days ago•GHSA-99J7-FHR2-XFJ4
10.0

GHSA-99J7-FHR2-XFJ4: Malicious Remote Code Execution Payload in 'exploration' Cargo Crate

The malicious Cargo package 'exploration' was uploaded to the crates.io registry. During compilation or package import, the crate executes code designed to establish an outbound TCP/HTTP connection, download an external second-stage binary, and execute the binary locally on the host machine. This creates an unauthenticated remote code execution vector impacting developer environments and continuous integration pipelines.

Amit Schendel
Amit Schendel
12 views•6 min read
•2 days ago•CVE-2026-54088
9.3

CVE-2026-54088: Pre-Authentication Remote Code Execution in File Browser Hook Authentication

CVE-2026-54088 is a critical command injection vulnerability in File Browser prior to version 2.63.6. When Hook Authentication is enabled, the application interpolates unsanitized credentials into a shell command, allowing unauthenticated remote code execution.

Alon Barad
Alon Barad
13 views•6 min read
•2 days ago•GHSA-QV4M-M73M-8HJ7
8.8

GHSA-qv4m-m73m-8hj7: Authenticated Arbitrary File Upload leading to Remote Code Execution in NotrinosERP

An authenticated remote code execution vulnerability exists in NotrinosERP (versions up to and including 1.0.0) within the Human Resource Management (HRM) module. Users with employee management permissions can upload arbitrary file types, including PHP scripts, which are written directly to a web-accessible directory. This allows for arbitrary code execution in the context of the web-server user.

Alon Barad
Alon Barad
8 views•6 min read