Back to Blog
high SEVERITY7 min read

How Inherited Dependency Vulnerabilities Happen in Node.js and how to fix it

A vulnerability in the `tmp` Node.js package (CVE-2026-44705) was discovered lurking as a transitive dependency via `tmp-promise@3.0.3`, leaving applications exposed to unsafe temporary file handling. The fix pins `tmp` to version `0.2.7` using a pnpm override across `package.json`, `dist/cli.js`, and `pnpm-lock.yaml`, eliminating the vulnerable code path without affecting any valid application behavior.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed August 26, 2026

Answer Summary

CVE-2026-44705 is a HIGH-severity vulnerability in the `tmp` Node.js package (versions prior to 0.2.6) that allows unsafe handling of temporary files and directories, potentially enabling symlink attacks or insecure temp file creation. The vulnerability surfaces as a transitive dependency through `tmp-promise@3.0.3`. The fix, aligned with CWE-377 (Insecure Temporary File), is to override the `tmp` dependency to version `0.2.7` in `package.json` and `pnpm-lock.yaml`, preventing the vulnerable version from being resolved anywhere in the dependency tree.

Vulnerability at a Glance

cweCWE-377
fixPin `tmp` to `0.2.7` via a pnpm override in `package.json` and `pnpm-lock.yaml`
riskAttackers may exploit unsafe temp file creation to perform symlink attacks, race conditions, or information disclosure
languageJavaScript / Node.js
root cause`tmp@0.2.5` (pulled in transitively by `tmp-promise@3.0.3`) contained unsafe temporary file handling logic
vulnerabilityInsecure Temporary File Creation (CVE-2026-44705)

How Inherited Dependency Vulnerabilities Happen in Node.js and How to Fix It

The pnpm-lock.yaml file in this project quietly contained a ticking clock: tmp@0.2.5, a transitive dependency pulled in by tmp-promise@3.0.3, carried CVE-2026-44705 — a HIGH-severity vulnerability in temporary file handling. No direct code change introduced it. No developer consciously chose it. It arrived as invisible cargo inside another package, and it would have stayed invisible without automated scanning.

This post walks through exactly what happened, why it matters, and how a targeted pnpm override resolved it across three files.


The Vulnerability Explained

What Is CVE-2026-44705?

tmp is a widely-used Node.js library for creating temporary files and directories. Prior to version 0.2.6, it contained unsafe temporary file creation logic — the kind of flaw covered by CWE-377: Insecure Temporary File.

Insecure temp file creation typically involves one or more of the following weaknesses:

  • Predictable file names that attackers can guess and pre-create as symlinks
  • Race conditions (TOCTOU) between checking whether a temp path exists and actually creating the file
  • Insufficient permission bits on created temp files, allowing other local users to read or write them

In the case of CVE-2026-44705 specifically, tmp versions before 0.2.6 did not adequately guard against these conditions, leaving any application that creates temporary files via this library open to local privilege escalation or information disclosure.

How It Arrived: The Transitive Dependency Chain

The project did not directly depend on tmp. It depended on tmp-promise@3.0.3, a promise-based wrapper around tmp. And tmp-promise@3.0.3 pulled in tmp@0.2.5 — the vulnerable version.

You can see this clearly in the lockfile snapshot before the fix:

# pnpm-lock.yaml (BEFORE)
tmp-promise@3.0.3:
  resolution: {integrity: sha512-RwM7MoPojPxs...}

tmp@0.2.5:
  resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/...}
  engines: {node: '>=14.14'}

And in the snapshots section:

# pnpm-lock.yaml snapshots (BEFORE)
tmp-promise@3.0.3:
  dependencies:
    tmp: 0.2.5

tmp@0.2.5: {}

This is a classic transitive dependency vulnerability: the vulnerable package is two levels deep in the dependency graph, invisible to a developer scanning only their package.json.

Attack Scenario

Imagine this application runs on a shared Linux server or inside a CI/CD pipeline where multiple processes share a filesystem. A component of the application uses tmp-promise to create a temporary file for intermediate processing — perhaps staging output from the esbuild build step or writing a temporary config file.

With tmp@0.2.5:

  1. The application calls tmp.file() or tmp.dir() to create a temp path.
  2. An attacker process (running as a different user on the same system) predicts the temp file name based on the predictable naming scheme.
  3. The attacker pre-creates a symlink at that path pointing to a sensitive file (e.g., /etc/passwd or an SSH key).
  4. When the application writes to the "temp file," it actually writes to the symlink target — overwriting a sensitive system file or leaking data.

This is a TOCTOU (Time-of-Check to Time-of-Use) race condition, and it's especially dangerous in automated build pipelines where temp files are created and destroyed rapidly.


The Fix

Strategy: pnpm Dependency Override

Since tmp is a transitive dependency (not a direct one), simply updating tmp-promise in package.json wouldn't help — tmp-promise@3.0.3 still resolves tmp@0.2.5. The correct fix is to use a pnpm override, which forces the entire dependency tree to use a specific version of a package regardless of what upstream packages request.

The fix added "tmp": "0.2.7" to the pnpm.overrides section in package.json:

Before:

"pnpm": {
  "overrides": {
    "sharp": "^0.34.5",
    "@img/sharp-libvips-darwin-arm64": "1.2.4"
  }
}

After:

"pnpm": {
  "overrides": {
    "sharp": "^0.34.5",
    "@img/sharp-libvips-darwin-arm64": "1.2.4",
    "tmp": "0.2.7"
  }
}

This single addition tells pnpm: no matter who asks for tmp, give them 0.2.7.

The Lockfile Update

The pnpm-lock.yaml reflects the resolved change. The resolution hash changes from the 0.2.5 integrity value to the 0.2.7 value:

# pnpm-lock.yaml (AFTER)
-  tmp@0.2.5:
-    resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
+  tmp@0.2.7:
+    resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==}
     engines: {node: '>=14.14'}

And the snapshot for tmp-promise now correctly resolves to the safe version:

# snapshots (AFTER)
tmp-promise@3.0.3:
  dependencies:
-    tmp: 0.2.5
+    tmp: 0.2.7

The dist/cli.js Update

The compiled dist/cli.js also embeds the package configuration, so it received the same override addition:

// dist/cli.js (AFTER)
var pnpm = {
  overrides: {
    sharp: "^0.34.5",
    "@img/sharp-libvips-darwin-arm64": "1.2.4",
    tmp: "0.2.7"   // <-- added
  },
  ...
}

This ensures that any tooling consuming the compiled CLI bundle also reflects the correct dependency policy.

Why 0.2.7 Instead of 0.2.6?

The PR title references 0.2.6 as the minimum safe version (the first version to address CVE-2026-44705), but the actual fix pins to 0.2.7 — a patch release that includes additional hardening on top of the CVE fix. Pinning to the latest patch version is the safer choice when the API surface is unchanged.


Key Takeaways

  • tmp@0.2.5 is vulnerable; the safe floor is 0.2.6, and 0.2.7 is the recommended pin — this specific version boundary is what CVE-2026-44705 is about.
  • tmp-promise@3.0.3 acts as a silent carrier — it resolves the vulnerable tmp version without any warning in your direct dependency list.
  • A pnpm override in package.json is the correct fix when you cannot update the direct dependent (tmp-promise) itself; it forces the safe version across the entire tree.
  • Lockfile integrity matters — the resolution hash in pnpm-lock.yaml changed from sha512-voyz6MA... to sha512-e0votIpp..., providing a cryptographic guarantee that the correct package is installed.
  • Compiled artifacts like dist/cli.js also embed dependency policy — forgetting to update them would leave the documented configuration inconsistent with the actual security posture.

How Orbis AppSec Detected This

  • Source: The pnpm-lock.yaml lockfile, which resolved tmp-promise@3.0.3's dependency to tmp@0.2.5
  • Sink: Any call site within the application that invokes tmp.file() or tmp.dir() via the tmp-promise wrapper, creating temporary files with unsafe guarantees
  • Missing control: No version override existed to prevent pnpm from resolving the vulnerable tmp@0.2.5 version, and no minimum-version constraint was enforced on the transitive dependency
  • CWE: CWE-377 — Insecure Temporary File
  • Fix: Added "tmp": "0.2.7" to the pnpm.overrides section in package.json and updated pnpm-lock.yaml and dist/cli.js to reflect the pinned safe version

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-44705 is a reminder that your application's security posture is only as strong as its deepest dependency. The tmp package vulnerability didn't arrive through a developer mistake or a bad architectural decision — it arrived silently, two levels deep in the dependency graph, carried by a perfectly reasonable library choice (tmp-promise).

The fix is surgical and non-breaking: a single pnpm override pins tmp to 0.2.7 across the entire tree, closing the vulnerability without touching any application logic. Three files changed, zero behavior changed, one CVE eliminated.

The broader lesson: lockfile-aware vulnerability scanning isn't optional. Tools like Trivy that read your pnpm-lock.yaml and trace the full resolution graph are the only reliable way to catch vulnerabilities like this before they reach production.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1340

Related Articles

high

MapManager.get() Race Condition Duplicates API Requests

The MapManager's `get(mapUid, cache)` method used a check-then-act pattern that permitted multiple concurrent requests to pass the cache miss check simultaneously, triggering redundant API calls and risking cache corruption. The fix introduces a `_pending` promise map to deduplicate in-flight fetches for identical map UIDs.

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.