Sep 30, 2026·7 min read·30 visits
A path traversal vulnerability in @xhmikosr/decompress allows attackers to write files outside the target directory via a crafted chain of symlink entries, bypassing lexical containment checks.
CVE-2026-101894 is a critical path traversal vulnerability in @xhmikosr/decompress before versions 10.2.2 and 11.1.4, stemming from an incomplete hardening bypass of CVE-2026-53486 where static lexical containment checks fail to detect kernel-level resolution of crafted symlink chains, allowing arbitrary local file modification and execution.
Node.js archive extraction libraries handle the unpacking of raw archive streams, typically ZIP or TAR files, mapping internal archive paths to filesystem structures. The default decompress(input, output) API in the @xhmikosr/decompress library and its unmaintained upstream counterpart decompress serves as the primary interface for this behavior. This component exposes a large attack surface when consuming untrusted compressed archives, particularly in applications processing automated uploads, third-party integrations, or build-system artifacts.
The vulnerability, tracked as CVE-2026-101894, belongs to the Improper Limitation of a Pathname to a Restricted Directory (CWE-22) and Improper Link Resolution Before File Access (CWE-59) classes. It represents a direct bypass of the security mechanisms introduced to resolve CVE-2026-53486. The earlier fix attempted to validate that target paths did not escape the output directory by implementing static string checks on the path components.
By constructing a malicious archive with nested, self-referential symbolic link chains, attackers can subvert these static checks. At runtime, the operating system kernel resolves the path physically rather than lexically, resulting in file creation or modification outside the designated target folder. This execution behavior can lead to local file disclosure, parameter overwrite, or arbitrary remote code execution if system configuration or startup files are altered.
The root cause of CVE-2026-101894 is the library's reliance on static lexical validation instead of physical filesystem validation before creating files. Static path resolution methods evaluate strings without querying the physical state of the underlying filesystem. When analyzing symbolic links, the physical target of the symlink dictates how subsequent relative path segments (specifically parent directory navigations via ..) are processed by the operating system kernel.
If an archive contains a symbolic link entry a that points to the current directory ., the physical directory structure is unaffected at the lexical layer. A subsequent entry b pointing to a/../secret.txt appears to the static path-resolution mechanism as secret.txt after collapsing the relative segments statically because the parent of .. is evaluated as a. Lexically, the path secret.txt is judged to reside within the designated extraction boundary, and the security boundary check evaluates to a pass status.
When the extraction engine writes these files, the operating system processes the actual creation. The kernel accesses the directory entry a, recognizes it physically points to ., and stays within the extraction folder. However, when parsing the next segment .., the kernel navigates to the physical parent of the current folder, effectively escaping the extraction directory entirely. The terminal segment secret.txt is then written to this higher-level directory, bypassing all static directory boundary enforcement.
The previous, vulnerable validation logic attempted to enforce directory containment by inspecting paths prior to disk writes. The function ensureLinkTargetInsideOutput performed simple string operations and lexical resolves, which failed to evaluate runtime filesystem layouts. Below is the conceptual representation of the vulnerable check alongside the patched approach which implements physical resolution.
The updated implementation introduces two main functions: resolveMaybeMissing and assertSymlinkResolvesInside. The resolveMaybeMissing function recursively climbs the directory structure of a target path, resolving the longest existing physical prefix using the realpath API. It then appends any non-existent trailing directories to ensure that physical paths are computed accurately even if files do not yet exist on disk.
To inspect the exact patch changes, refer to the following code snippet showing the replacement logic that prevents the lexical containment bypass:
// Vulnerable Lexical Path Containment (Concept)
// const safe = path.resolve(outputPath, linkname).startsWith(outputPath);
// Patched Physical Resolution Mechanism
const resolveMaybeMissing = async target => {
let existing = target;
const tail = [];
for (;;) {
try {
// Resolve the longest physically existing path using the filesystem realpath
return path.join(await realpath(existing), ...tail.toReversed());
} catch {
const parent = path.dirname(existing);
if (parent === existing) {
return target;
}
tail.push(path.basename(existing));
existing = parent;
}
}
};
const assertSymlinkResolvesInside = async (dest, linkname, realOutputPath) => {
// Evaluate the raw link target dynamically without collapsing relative segments lexically
const rawTarget = path.isAbsolute(linkname) ? linkname : path.dirname(dest) + path.sep + linkname;
const resolved = await resolveMaybeMissing(rawTarget);
if (!isInsideOutput(resolved, realOutputPath)) {
await unlink(dest).catch(() => null);
throw new Error(`Refusing to keep a symlink that escapes the output directory: ${dest}`);
}
};In addition to the physical path check, the patch introduces multi-phase extraction where symlinks are validated post-extraction, and files are opened with the O_NOFOLLOW flag to prevent writing through pre-existing symlinks. The O_NOFOLLOW flag forces the operating system to reject writes that would follow symbolic links, eliminating Time-of-Check to Time-of-Use (TOCTOU) race conditions.
An exploitation chain targeting CVE-2026-101894 requires an attacker to construct a ZIP or TAR archive containing at least two distinct entries. The first entry must establish a symbolic link pointing to a directory within the structure, typically using the relative path self-reference .. The second entry is another symbolic link that references the first link followed by a parent traversal string pointing to a sensitive file target.
When the target Node.js application invokes the decompress API on this archive, the vulnerable library processes the directory entry. It fails to detect that the second symbolic link resolves physically to an external file. Once the symbolic links are written to disk, subsequent files or directory writes targeting the second symlink name will overwrite files outside the root extraction folder.
The process flow below illustrates the evaluation sequence of the symbolic link chain during extraction:
No specialized privileges or complex configurations are required to trigger the vulnerability. The attack can be executed remotely if the application allows users to upload custom compressed archives and extracts them to a server-side directory. If the application runs as a privileged user such as root, the write vector can target system configuration directories.
The impact of CVE-2026-101894 is categorized as critical, represented by a CVSS v3.1 score of 9.1. An attacker who successfully exploits this vulnerability can read or write arbitrary files on the host filesystem with the privileges of the Node.js process. If the application runs with high administrative access, the entire operating system environment can be compromised.
In web application deployments, arbitrary file write access often leads directly to remote code execution. Attackers can overwrite node dependency files within the node_modules directory, inject malicious scripts into startup handlers, or drop configuration files like .bashrc or systemd service configurations. When the server restarts or the affected node process executes the modified code paths, the attacker gains shell access.
While the EPSS score of 0.00814 indicates a low current probability of widespread exploitation, this score does not reflect the severity of a targeted attack. Applications operating in multitenant or shared environments that process file uploads are at elevated risk. This threat vector requires immediate remediation to prevent lateral movement and privilege escalation inside corporate infrastructure.
Complete remediation of this vulnerability requires upgrading all instances of the @xhmikosr/decompress package. The developer has published official security updates addressing this vulnerability in versions 10.2.2 and 11.1.4. System administrators must identify any transitive dependencies pulling in vulnerable versions of the library.
If the legacy, unmaintained decompress package is in use within the codebase, it must be replaced. The legacy decompress library remains unpatched through its final version 4.2.1 and should be swapped for the actively maintained @xhmikosr/decompress fork. No configuration flags can secure the unmaintained package against this flaw.
Temporary mitigation strategies involve limiting the execution environment of the Node.js runtime. Deploying the application in an isolated Docker container with a non-root user and mounting extraction directories as restricted, non-executable partitions reduces the impact of directory traversal. Additionally, preprocessing incoming archives to filter out symlinks before invoking the extraction library provides an effective defense-in-depth layer.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@xhmikosr/decompress @xhmikosr | < 10.2.2 | 10.2.2 |
@xhmikosr/decompress @xhmikosr | < 11.1.4 | 11.1.4 |
decompress kevva | <= 4.2.1 | None |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS Score | 9.1 |
| EPSS Score | 0.00814 |
| Exploit Status | PoC |
| 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 within a restricted 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.
The Natural Language Toolkit (NLTK) versions 3.6 through 3.10.3 contain an algorithmic complexity vulnerability in the LegalitySyllableTokenizer.tokenize method. When processing tokens composed of continuous runs of vowels, an unguarded string reversal and set membership lookup loop executes in quadratic time relative to the input token length. Remote attackers capable of submitting arbitrary text to applications utilizing this tokenizer can trigger sustained single-core CPU exhaustion and application-level denial of service.
CVE-2026-126304 is a symlink path traversal vulnerability (CWE-59) in the Natural Language Toolkit (NLTK) versions 3.10.0 prior to 3.10.4. The YCOECorpusReader class constructs internal sub-readers for the 'psd' and 'pos' subdirectories using FileSystemPathPointer.join(), which relies exclusively on lexical string-prefix validation rather than canonical path resolution. An attacker who supplies a crafted YCOE corpus containing symbolic links in place of the 'psd' or 'pos' subdirectories can bypass directory confinement and read arbitrary files from sibling corpora or sensitive directories within the NLTK data sandbox.
CVE-2026-128077 (GHSA-r99w-jh2x-3mg9) is an authorization bypass and interpretation conflict vulnerability affecting Nuxt and its underlying @nuxt/nitro-server package in versions 4.6.0 through 4.6.3. When an incoming HTTP request path begins with a double forward slash (e.g., //x/admin), the WHATWG URL parser within the Nuxt server-side page renderer interprets the initial segment as a network host/authority and resolves the remaining path (/admin) against the application's page router. Because Nitro server middleware, route-scoped serverHandlers, and Nitro routeRules evaluate the unparsed request path (//x/admin), path-based access controls, redirects, and security header policies configured for /admin fail to match and are bypassed.
Prior to version 2.10.2, the sqlite3-ruby gem explicitly cast 64-bit Ruby string lengths (long) to 32-bit signed integers (int) when invoking the underlying SQLite C API. On 64-bit platforms, providing a string of 4 GiB (2^32 bytes) or larger causes integer truncation, instructing SQLite to process only a prefix of the string. This flaw affects SQL query preparation, bound parameter values, and return values from custom Ruby-defined SQL functions and aggregates, allowing trailing SQL clauses such as WHERE restrictions to be silently dropped under specific conditions.
CVE-2026-128068 (GHSA-cpgf-5mjf-73p7) is a Regular Expression Denial of Service (ReDoS) vulnerability in the rails-html-sanitizer Ruby gem affecting versions 1.0.3 through 1.7.1. The flaw resides in the Rails::HTML::PermitScrubber#scrub_attribute method, which uses the multiline regular expression /^\s*[^#\s].*/m to enforce local-only URI fragment references on SVG elements. On Ruby runtimes prior to version 3.2 (CRuby <= 3.1 and JRuby), evaluating an attribute value composed of repeated newline and whitespace characters without a hash character triggers quadratic backtracking O(n^2). Applications are only vulnerable if their sanitization allowlist is explicitly configured to permit SVG reference elements such as <use> alongside href or xlink:href attributes.
CVE-2026-126360 (GHSA-mf5v-w3w3-f6cg) is an algorithmic complexity vulnerability (Regular Expression Denial of Service) in the trace context propagation logic of the opentelemetry-api package within the OpenTelemetry Python ecosystem. In versions prior to 1.45.2, the TraceContextTextMapPropagator.extract() method utilizes a regular expression susceptible to quadratic-time backtracking when parsing malformed W3C traceparent headers containing long sequences of whitespace followed by an embedded newline character. In Python applications—particularly those using single-threaded asynchronous event loops—an unauthenticated remote attacker transmitting payloads over newline-preserving carriers can monopolize CPU execution and induce application-level Denial of Service.