Back to Blog
critical SEVERITY6 min read

How Path Traversal happens in Node.js decompress and how to fix it

A critical zip-slip vulnerability (CVE-2026-53486) in the `@xhmikosr/decompress` package allowed crafted archives to write files outside the intended extraction directory, enabling arbitrary file read/write on the host. The fix upgrades `@xhmikosr/decompress` from 5.0.0 to 10.2.1/11.1.3 and its dependency `@xhmikosr/bin-wrapper` from ^5.0.0 to ^13.2.0, closing the path-sanitization gap in the underlying extractors.

O
By Orbis AppSec
Published August 31, 2026Reviewed August 31, 2026

Answer Summary

CVE-2026-53486 is a path traversal (zip-slip, CWE-22) vulnerability in the Node.js package `@xhmikosr/decompress` (v5.0.0), where crafted archive entries with `../` sequences or absolute paths in their filenames could escape the target extraction directory during decompression, leading to arbitrary file read/write. The fix is to upgrade the dependency to `@xhmikosr/decompress@10.2.1`/`11.1.3` (and `@xhmikosr/bin-wrapper` to `^13.2.0`), which added proper entry-path validation before writing extracted files to disk.

Vulnerability at a Glance

cweCWE-22
fixUpgrade `@xhmikosr/decompress` to `10.2.1`/`11.1.3` and `@xhmikosr/bin-wrapper` to `^13.2.0` in `package.json`/`bun.lock`
riskArbitrary file read/write outside the intended extraction directory via a crafted archive
languageJavaScript / Node.js
root cause`@xhmikosr/decompress@5.0.0` did not validate extracted entry paths for `../` traversal sequences or absolute paths before writing files
vulnerabilityPath Traversal / Zip Slip in archive extraction

Introduction

The @xhmikosr/decompress package is a common building block in Node.js tooling used to unpack .zip, .tar, .tar.gz, and other archive formats — often as part of build scripts, CLI tools, or binary download helpers. In this repository, it's pulled in transitively through @xhmikosr/bin-wrapper, which several workspace packages (packages/binaries/celestia, packages/binaries/near-sandbox, packages/binaries/ord, packages/binaries/solana-node, and others) depend on to download and extract pre-built binaries during installation or build.

The problem: version 5.0.0 of @xhmikosr/decompress, tracked in bun.lock and pinned via @xhmikosr/bin-wrapper@^5.0.0, is vulnerable to CVE-2026-53486 — a critical zip-slip vulnerability that allows a maliciously crafted archive to write (or read) files outside the directory the code intended to extract into. If any workflow in this repo extracts an archive from a source that isn't fully trusted (a downloaded binary release, a CI artifact, a third-party mirror), a single crafted archive entry could overwrite files far outside the expected output folder.

Trivy's dependency scanner flagged this exact pattern against bun.lock, and while the assessment notes it as "present in dependency tree, not confirmed reachable," that's precisely the kind of latent risk that turns into a real incident the moment a build script or binary-fetch path starts consuming an untrusted archive.

The Vulnerability Explained

Archive formats like zip and tar store a relative path for every file entry — that's how you get a directory structure back out when you extract. A well-behaved archive entry looks like:

readme.txt
bin/celestia
lib/config.json

A malicious archive entry, however, can specify a path designed to escape the extraction directory:

../../../../etc/cron.d/malicious-job
../../.ssh/authorized_keys

Older versions of extraction libraries — including @xhmikosr/decompress@5.0.0 — historically did not fully canonicalize and validate each entry's destination path before writing it to disk. Internally, code in that vintage of the extractor effectively does something equivalent to:

// Simplified representation of the vulnerable pattern
const outputPath = path.join(destDir, entry.path); // entry.path is attacker-controlled
fs.writeFileSync(outputPath, entry.data);

Because path.join(destDir, entry.path) doesn't stop entry.path from containing .. segments (or an absolute path on some platforms), the resulting outputPath can resolve to a location completely outside destDir. This is the classic "zip slip" pattern, and it's exactly the class of bug CVE-2026-53486 targets in decompress.

Attack scenario specific to this codebase: The packages/binaries/* workspaces (celestia, near-sandbox, ord, solana-node) use @xhmikosr/bin-wrapper, which under the hood downloads a binary release archive and hands it to decompress for extraction. If an attacker can influence the archive source — a compromised mirror, a man-in-the-middle on an unpinned download URL, or a poisoned upstream release — they could ship a .tar.gz where one entry's path is ../../../../home/deploy/.bashrc or ../../../etc/systemd/system/backdoor.service. On extraction, that entry gets written outside the intended packages/binaries/<name>/bin/ directory, potentially achieving arbitrary file write and, depending on what gets overwritten, code execution or persistence on the build/runtime host.

The Fix

The PR resolves this by bumping the vulnerable dependency chain in package.json and regenerating bun.lock:

Before:

       "dependencies": {
         "@effectstream/binary-checksum": "workspace:*",
-        "@xhmikosr/bin-wrapper": "^5.0.0",
+        "@xhmikosr/bin-wrapper": "^13.2.0",
       },

This change is applied consistently across every workspace that pulled in the vulnerable version:

  • packages/binaries/celestia
  • packages/binaries/near-sandbox
  • packages/binaries/ord
  • packages/binaries/solana-node

@xhmikosr/bin-wrapper@^13.2.0 itself depends on a patched @xhmikosr/decompress (upgraded to 10.2.1/11.1.3 in the lockfile), which includes proper entry-path validation before extraction — rejecting or normalizing entries that would resolve outside the destination directory. The lockfile diff also shows a related transitive bump, @sindresorhus/is from 4.6.0 to 5.6.0, pulled in as part of the dependency tree update for the newer bin-wrapper/decompress chain.

Why this is the right fix, and why it's low-risk: This is a pure dependency version bump, not a behavioral rewrite of extraction logic in this repo's own code. The vulnerable code lived entirely inside @xhmikosr/decompress, so the safest and most maintainable fix is to consume the upstream patch rather than hand-roll path sanitization around a third-party extractor. Because the change only tightens how malicious archive entries are handled — legitimate archives with well-formed relative paths extract exactly as before — normal build and install workflows are unaffected.

Prevention & Best Practices

  • Pin and monitor extraction library versions. Archive/extraction libraries (decompress, tar, extract-zip, unzipper, etc.) are a recurring source of zip-slip CVEs. Treat them like any other security-sensitive dependency and keep them on active, patched release lines.
  • Never trust archive entry paths. If you ever write custom extraction logic, always resolve the final path with path.resolve(destDir, entry.path) and verify it still starts with destDir (with a trailing separator check to avoid prefix-matching bypasses like destDir-evil) before writing.
  • Restrict extraction sources. Only extract archives from sources you control or that are integrity-verified (checksums/signatures) — this matters directly for the bin-wrapper-based binary downloads in packages/binaries/*.
  • Automate dependency scanning. Trivy caught this via lockfile analysis against the CVE database; running this kind of SCA scan in CI on every bun.lock/package.json change catches these before they ship.
  • Reference CWE-22 in your secure coding guidelines and code review checklists whenever a change touches file extraction, template rendering with file paths, or any code that joins user- or archive-supplied path segments with a base directory.

Key Takeaways

  • @xhmikosr/decompress@5.0.0, used transitively via @xhmikosr/bin-wrapper@^5.0.0, was vulnerable to zip-slip path traversal (CVE-2026-53486) that could enable arbitrary file read/write during archive extraction.
  • Four workspaces — celestia, near-sandbox, ord, and solana-node — pulled in the vulnerable chain through their binary-download tooling and needed the bump to @xhmikosr/bin-wrapper@^13.2.0.
  • The fix is a lockfile/manifest-only change; no application code needed to be rewritten because the flaw lived entirely in the third-party extraction library.
  • Even when a scanner marks a dependency as "not confirmed reachable," it's still worth patching promptly — archive extraction paths are exactly the kind of code that gets exercised unexpectedly (CI, install scripts, binary fetchers).
  • Treat any library that writes files based on data inside an untrusted input (archives, uploads, templates) as a high-priority target for version tracking and CVE monitoring.

How Orbis AppSec Detected This

  • Source: Archive entry filenames inside a downloaded binary release (.zip/.tar.gz) fetched by @xhmikosr/bin-wrapper in packages/binaries/{celestia,near-sandbox,ord,solana-node}.
  • Sink: Internal file-write call inside @xhmikosr/decompress@5.0.0's extraction routine, which joined the untrusted entry path with the destination directory without full containment validation.
  • Missing control: No canonicalization/containment check to ensure the resolved extraction path stayed within the intended destination directory (no ..//absolute-path rejection).
  • CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory / Path Traversal).
  • Fix: Upgraded @xhmikosr/bin-wrapper to ^13.2.0 (pulling in patched @xhmikosr/decompress@10.2.1/11.1.3) in package.json and regenerated bun.lock accordingly.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-53486 is a textbook zip-slip vulnerability: an archive extraction library that trusted entry paths just enough to let a crafted ../ sequence escape the intended output directory, opening the door to arbitrary file read/write. Because @xhmikosr/decompress@5.0.0 sat several major versions behind and was consumed transitively across four binary-handling workspaces, the blast radius wasn't obvious from a casual read of package.json — it took dependency-tree analysis to surface it. The fix here is simple and safe: bump @xhmikosr/bin-wrapper to ^13.2.0 and let the patched @xhmikosr/decompress (10.2.1/11.1.3) enforce proper path containment. The broader lesson is to keep archive-handling dependencies current and to treat any code path that extracts, unpacks, or writes files based on external input as security-critical by default.

References

  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory — https://cwe.mitre.org/data/definitions/22.html
  • OWASP Path Traversal Cheat Sheet — https://owasp.org/www-community/attacks/Path_Traversal
  • Node.js path module documentation (safe path resolution) — https://nodejs.org/api/path.html
  • Semgrep rules for zip-slip / path traversal — https://semgrep.dev/r?q=zip-slip
  • fix: upgrade @xhmikosr/decompress to 10.2.1, 11.1.3 (CVE-2026-53486)

Frequently Asked Questions

What is path traversal / zip slip?

Zip slip is a path traversal vulnerability where an archive extraction library fails to sanitize entry filenames, allowing entries like `../../etc/cron.d/evil` to be written outside the intended output directory during decompression.

How do you prevent path traversal in Node.js archive handling?

Always resolve each extracted entry's path against the target directory, reject any resolved path that falls outside that directory (e.g., using `path.resolve` and checking it starts with the destination), and prefer well-maintained extraction libraries that implement this check internally.

What CWE is path traversal / zip slip?

It's classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), and related arbitrary-write outcomes can also map to CWE-434.

Is checking for "../" in filenames enough to prevent zip slip?

No — attackers can use absolute paths, symlinks, URL-encoded sequences, or platform-specific separators to bypass naive string checks; you need canonical path resolution and containment verification, not substring matching.

Can static analysis detect zip-slip vulnerabilities?

Yes, dependency scanners like Trivy can flag known-vulnerable versions of extraction libraries such as `@xhmikosr/decompress`, and SAST tools/Semgrep rules can detect unsafe extraction patterns in custom code.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #876

Related Articles

high

How command injection happens in Node.js child_process spawn calls and how to fix it

A benchmarking helper in `bench/lib/actor.js` passed an unvalidated executable path from upstream pipeline results directly into `child_process.spawn()`. The fix resolves the path and enforces that it lives inside the sandboxed stage directory before execution, closing off a path-traversal-driven command injection primitive.

high

How path traversal happens in Python and how to fix it

A high-severity path traversal vulnerability in `posttrain_runner.py` allowed arbitrary file reads through the `base_ckpt` parameter. The fix implements `os.path.realpath()` validation to ensure all file paths remain within the working directory, preventing attackers from accessing sensitive system files.

critical

How Path Traversal and Resource Exhaustion happen in Node.js HTTP servers and how to fix them

A critical security vulnerability in `wasm-build/server.js` allowed attackers to read arbitrary files outside the web root via path traversal, while simultaneously leaving the server open to resource exhaustion through unbounded concurrent connections. The fix sanitizes URL paths before joining them to the filesystem and enforces strict connection and timeout limits to prevent denial-of-service attacks.

high

How Path Traversal happens in Node.js tmp package and how to fix it

The tmp package version 0.0.33 contained a high-severity path traversal vulnerability (CVE-2026-44705) that allowed attackers to escape temporary directories through unsanitized prefix and postfix parameters. This reddit-app project was upgraded from tmp 0.0.33 to 0.2.7, which implements proper input sanitization to prevent directory traversal attacks and removes the deprecated os-tmpdir dependency.

medium

How Path Traversal and Filename Injection Happens in Python File Handling and How to Fix It

A medium-severity path traversal vulnerability in `PainterNode/painter_node.py` allowed attackers to reference files outside the intended directory by exploiting a broken `isFileName()` validation function. The original logic used incorrect boolean operators, meaning the filename guard never actually blocked malicious inputs like `../../../etc/passwd` or paths containing backslashes. The fix rewrites the condition with proper logic and adds explicit checks for path separator characters and direc

critical

How Archive Path Traversal Happens in Node.js and How to Fix It

CVE-2026-53486 is a critical path traversal vulnerability in the Decompress library, where crafted archive entries can write files and symbolic links outside the intended extraction directory. This vulnerability was transitively introduced through `@vitest/browser` and related packages pinned at version 4.1.5, and was resolved by upgrading to 4.1.6 and 5.0.0-beta.3. Left unpatched, an attacker who controls an archive file processed by any downstream consumer of this dependency chain could overwr