Apr 16, 2026·7 min read·1 visit
The `downloadPackageManager()` function in `vite-plus/binding` (< 0.1.17) is vulnerable to a path traversal attack via unvalidated version strings. This allows arbitrary file writes and directory manipulation outside the designated `VP_HOME` directory.
A path traversal vulnerability exists in the `vite-plus/binding` component of the `vite-plus` npm package prior to version 0.1.17. The `downloadPackageManager()` function fails to validate the `version` parameter, allowing programmatic attackers to escape the `VP_HOME` directory, overwrite arbitrary directories, and write executable shims to unintended filesystem locations.
The vite-plus npm package provides tooling for modern web development workflows. The vite-plus/binding module exposes core functionalities, including package manager administration, via native Rust bindings. Programmatic consumers utilize these bindings to handle internal build tasks or dependency resolution pipelines within their applications.
A critical path traversal vulnerability, tracked as GHSA-33R3-4WHC-44C2, exists in the downloadPackageManager() function exported by the vite-plus/binding module. This function accepts a configuration object containing a target package manager name and a version string. The native binding relies on this parameter to determine the local installation path for the requested package manager.
Versions prior to 0.1.17 fail to validate that the provided version string conforms to a valid semantic versioning (semver) format. Consequently, the application incorporates raw, user-supplied input directly into standard filesystem operations. This lack of sanitization allows attackers to inject path traversal sequences such as ../ into the resolution logic.
By manipulating the version parameter, an attacker can escape the restricted VP_HOME directory. The resulting out-of-bounds operations allow the arbitrary creation, deletion, and overwriting of directories on the host filesystem. The impact is constrained only by the operating system permissions assigned to the executing Node.js process.
The root cause of GHSA-33R3-4WHC-44C2 is an unvalidated input flow bridging the Node.js application layer and the underlying Rust filesystem operations. When a user calls downloadPackageManager(), the function accepts an options object containing the target package manager specifications. The system uses these values to dynamically compute an installation path string.
The intended directory structure for package manager installations follows the format VP_HOME/package_manager/<pm>/<version>. The application constructs this target directory using standard path joining functions. It then performs a destructive setup sequence: it removes any existing directory at the computed target, renames the extracted package download into that location, and writes executable shim files into the new directory.
Because the version parameter undergoes no structural validation, a string like ../../../escaped passes directly into the path computation routine. The runtime environment interprets the directory traversal characters, collapsing the path elements and resolving the target directory to a location outside the VP_HOME boundary. The subsequent destructive operations execute against this escaped path.
The vulnerability specifically affects programmatic usage of the module. The standard command-line interface (CLI) for vite-plus is inherently safe from this flaw. The CLI layer pre-validates version strings using the semver::Version::parse() function before forwarding them to the binding. Programmatic consumers directly calling vite-plus/binding bypass this crucial validation step entirely.
Analyzing the architectural differences between the CLI and the native bindings provides insight into the exact failure mechanism. The vulnerability stems from a validation discrepancy between two distinct input layers. The framework applies strict validation at the outer CLI boundary but relies on implicit trust at the inner programmatic API boundary.
In safe executions via the vite-plus CLI, user input undergoes strict normalization. When a user requests a specific package manager version, the CLI Rust implementation parses the string using the semver crate. If the string contains invalid characters like directory traversals, the parser rejects it, preventing the payload from reaching the filesystem logic.
Programmatic executions directly target the vite-plus/binding API, bypassing the CLI's protective layer. The exported downloadPackageManager() function receives the JavaScript object and directly extracts the version field. The Rust-Node.js interoperability layer translates this string without enforcing the semantic versioning constraints applied by the CLI.
// Vulnerable Programmatic Invocation
import { downloadPackageManager } from "vite-plus/binding";
// The version string bypasses CLI semver parsing
await downloadPackageManager({
name: "pnpm",
version: "../../../malicious-path"
});The fix for version 0.1.17 centralizes the validation logic. By moving the semver::Version::parse() requirement directly into the core binding function, the application ensures that all input sources undergo structural verification. This design pattern eliminates the validation discrepancy and neutralizes the path traversal vector regardless of the entry point.
Exploiting this path traversal vulnerability requires the attacker to control the arguments passed to the downloadPackageManager() function. Since the attack vector is local, the adversary must either possess existing code execution capabilities within the Node.js environment or exploit a secondary input vector that channels unvalidated data into the programmatic API.
The provided proof-of-concept demonstrates a comprehensive exploitation chain combining the path traversal with a custom package registry. The attacker first establishes a local HTTP server that acts as a mock package repository. This server returns a malicious or dummy .tgz payload when queried for the target package manager by the native binding.
The attacker then sets the npm_config_registry environment variable to point to the local mock registry. When the application invokes downloadPackageManager(), it queries the mock registry and retrieves the attacker-controlled .tgz payload. The version parameter is simultaneously populated with the traversal string ../../../vite-plus-escape.
The system computes the escaped path, deletes any existing directory at that location, and unpacks the malicious .tgz payload into the arbitrary directory. Finally, the native binding writes executable shims into the escaped directory. This sequence confirms the ability to deploy arbitrary files to arbitrary locations, limited only by process privileges.
The successful exploitation of GHSA-33R3-4WHC-44C2 results in a high-severity impact on system integrity and availability. The most immediate consequence is the arbitrary overwriting or deletion of directories. As part of its installation routine, the vulnerable function explicitly removes the target directory before unpacking the downloaded package.
An attacker can leverage this destructive behavior to delete critical system directories or application data files, causing localized denial-of-service conditions. The ability to dictate the installation path also enables arbitrary file writes. By serving custom payloads via a manipulated package registry, the attacker controls the exact contents placed into the target directory.
The vulnerability facilitates potential local privilege escalation and persistence mechanisms. The installation process writes executable shims into a bin subdirectory within the target path. If the attacker targets a directory included in the system's PATH environment variable, they can overwrite legitimate binaries with malicious shims.
Subsequent execution of these replaced binaries by other users or automated system processes would execute the attacker's payload. The overall risk is reflected in the CVSS v4.0 score of 8.6. The requirement for local execution or programmatic access restricts the exploitability scope, but the high impact metrics underscore the severity of the flaw once triggered.
The primary remediation for GHSA-33R3-4WHC-44C2 is upgrading the vite-plus package to version 0.1.17 or later. The patched version enforces strict semantic version validation within the binding layer. This ensures that traversal characters are rejected before any filesystem operations occur, protecting programmatic consumers.
If immediate patching is not feasible, organizations must implement robust input validation within their own codebases. Any application programmatically calling vite-plus/binding must ensure that user-supplied version strings are validated against a strict semver regular expression. Utilizing a dedicated validation library like the semver npm package prevents traversal strings from reaching the vulnerable function.
Detection strategies should focus on both application behavior and filesystem integrity. Security teams should monitor application logs for unusual programmatic calls to the vite-plus bindings. Inputs containing encoded or plaintext traversal sequences, such as ../ or %2e%2e%2f, indicate potential exploitation attempts.
File integrity monitoring (FIM) solutions can detect the secondary artifacts of exploitation. Analysts should configure alerts for unexpected directory creation, deletion, or file writes originating from the Node.js process. Suspicious activity targeting sensitive system directories or binary paths outside the expected VP_HOME environment requires immediate investigation.
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:H/SA:H| Product | Affected Versions | Fixed Version |
|---|---|---|
vite-plus voidzero-dev | >= 0, < 0.1.17 | 0.1.17 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | GHSA-33R3-4WHC-44C2 |
| CWE ID | CWE-22 |
| CVSS v4.0 Score | 8.6 |
| Attack Vector | Local |
| Impact | High Integrity, High Availability |
| Exploit Status | PoC Available |
| Affected Component | vite-plus/binding |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath 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.