Back to Blog
critical SEVERITY5 min read

How a vulnerable websocket-driver dependency happens in Node.js lockfiles and how to fix it

A Trivy scan flagged `websocket-driver@0.7.4` in this repository's `bun.lock` as affected by CVE-2026-54466, a critical issue in a WebSocket protocol handler that parses untrusted HTTP upgrade requests and frame data. The fix upgrades the package to `0.7.5` and adds an explicit `websocket-driver` entry to the lockfile's override block so every transitive consumer — webpack-dev-server, sockjs, faye-websocket — resolves to the patched build instead of the pinned vulnerable one.

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

Answer Summary

This is a vulnerable third-party dependency issue (CWE-1395) in a Node.js/Bun project: `websocket-driver@0.7.4`, a WebSocket protocol handler that parses untrusted handshake headers and frames, is affected by CVE-2026-54466 (critical). Making things worse, 0.7.4 hard-pinned `http-parser-js` to exactly `0.5.3`, freezing a transitive HTTP-parsing dependency that cannot be patched independently. The fix is to upgrade to `websocket-driver@0.7.5` — which relaxes those pins to `http-parser-js: >=0.5.1`, `safe-buffer: >=5.1.0`, `websocket-extensions: >=0.1.1` — and to add `"websocket-driver": "0.7.5"` to the override/catalog block in `bun.lock` and `package.json` so no dependency can pull the old version back in.

Vulnerability at a Glance

cweCWE-1395
fixUpgrade to `websocket-driver@0.7.5` and add an explicit `"websocket-driver": "0.7.5"` override so the whole dependency tree dedupes onto the patched version
riskUntrusted WebSocket handshake and frame data is parsed by an unpatched protocol handler, with a hard-pinned HTTP parser that cannot receive security fixes
languageJavaScript / Node.js (Bun workspace)
root cause`bun.lock` resolved `websocket-driver@0.7.4` transitively with no override, and 0.7.4 pins `http-parser-js` to exactly `0.5.3`
vulnerabilityUse of a vulnerable third-party dependency (`websocket-driver@0.7.4`, CVE-2026-54466)

Summary

A Trivy scan flagged websocket-driver@0.7.4 in this repository's bun.lock as affected by CVE-2026-54466, rated critical. websocket-driver is the low-level WebSocket protocol handler that parses attacker-supplied HTTP upgrade headers and binary frames, so an unpatched version sits directly on an untrusted input path. The fix upgrades the package to 0.7.5 and adds an explicit websocket-driver entry to the lockfile's override block, so every transitive consumer in the workspace dedupes onto the patched build.

Introduction

The bun.lock file in this monorepo is a 9,000-line map of exactly which byte-for-byte version of every package gets loaded at runtime. Nobody wrote websocket-driver into a package.json here — it arrived transitively, the way it usually does: webpack-dev-server → sockjs → websocket-driver, or faye-websocket → websocket-driver. It was resolved, locked, and forgotten.

That is precisely the problem. Line 8898 of the lockfile read:

"websocket-driver": ["websocket-driver@0.7.4", "", {
  "dependencies": {
    "http-parser-js": "0.5.3",
    "safe-buffer": "5.2.1",
    "websocket-extensions": "0.1.4"
  }
}, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="],

Two things are worth staring at. First, websocket-driver@0.7.4 is the version named in CVE-2026-54466. Second — and this is the part developers usually miss — look at "http-parser-js": "0.5.3". That is not a range. It is an exact pin, hard-coded inside a third-party package's manifest. No amount of bun update in this repository can move that transitive dependency forward while websocket-driver@0.7.4 is in the tree. The vulnerable version pinned another package into place with it.

If you maintain a Node.js service that terminates WebSockets, runs a dev server, or embeds SockJS, this pattern is almost certainly in your lockfile too.

The Vulnerability Explained

What websocket-driver actually does

websocket-driver is described by its own authors as "a WebSocket protocol handler with pluggable I/O." It is the state machine underneath faye-websocket, sockjs, and webpack-dev-server's HMR channel. Its job is to:

  1. Read a raw HTTP request and validate the upgrade handshake — Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Extensions.
  2. Compute and emit the handshake response.
  3. Parse the framing protocol byte by byte: opcodes, FIN/RSV bits, payload length fields (7-bit, 16-bit, and 64-bit variants), masking keys, and continuation frames.

Every single one of those inputs comes from the network before any authentication has happened. A WebSocket handshake is, by definition, pre-auth traffic. That is why an advisory in this package carries a critical rating even when the library is three levels deep in a dependency tree: the parser runs before your code gets a say.

The frozen http-parser-js pin

The most concrete, inspectable defect in the old entry is the dependency pinning. Compare the resolved manifests:

// BEFORE — websocket-driver@0.7.4
"dependencies": {
  "http-parser-js": "0.5.3",       // exact pin — cannot be patched
  "safe-buffer": "5.2.1",          // exact pin
  "websocket-extensions": "0.1.4"  // exact pin
}

http-parser-js is the pure-JavaScript HTTP parser that websocket-driver uses to read the upgrade request in lib/websocket/driver/server.js. Versions of http-parser-js below 0.5.8 are affected by CVE-2022-32210, an HTTP request smuggling issue (CWE-444, "Inconsistent Interpretation of HTTP Requests") caused by lenient header parsing that a fronting proxy interprets differently. Because 0.7.4 pinned 0.5.3 exactly, that fix could never reach this application, no matter how aggressively the team updated its own direct dependencies.

This is the compounding effect of stale dependencies: one outdated package can act as a version lock on a whole subtree of security patches.

Attack scenario for this application

Suppose a service in this workspace exposes a SockJS or faye-websocket endpoint behind a reverse proxy or CDN that enforces authentication on /api/* but passes /ws through untouched:

  1. The attacker sends a single TCP connection carrying a crafted HTTP upgrade request to /ws. The headers are constructed so the fronting proxy and http-parser-js@0.5.3 disagree about where the first request ends — a header value containing bytes the proxy treats as terminators but the pinned parser folds into the previous header, or vice versa.
  2. The proxy sees one benign upgrade request and forwards the whole byte stream.
  3. websocket-driver's server-side handshake code, backed by the vulnerable parser, sees two requests. The second one — never inspected by the proxy — is smuggled directly to the origin, carrying whatever path and headers the attacker chose, including a forged trusted identity header the proxy would normally strip.
  4. Alternatively, the attacker skips the handshake abuse and targets the frame parser: an upgrade that negotiates a malformed Sec-WebSocket-Extensions value, followed by frames with inconsistent length fields, drives the 0.7.4 state machine down an unpatched path.

Note the honest caveat in the PR's own assessment: "Present in dependency tree, not confirmed reachable." Whether a given service in this monorepo actually terminates untrusted WebSocket traffic through this driver — versus only loading it inside webpack-dev-server on a developer laptop — determines real exploitability. That nuance is worth documenting per-service. It is not a reason to leave a critical, pre-auth protocol parser on a known-vulnerable version in a committed lockfile.

The Fix

The change touches exactly two files and does not modify a single line of application code. That is the point: this is a supply-chain fix, and the correct remediation is a version pin, not a code rewrite.

1. Bump the resolved version in bun.lock

-    "websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": "0.5.3", "safe-buffer": "5.2.1", "websocket-extensions": "0.1.4" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="],
+    "websocket-driver": ["websocket-driver@0.7.5", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA=="],

Two distinct security wins are packed into this one line:

  • The integrity hash changed (sha512-b17K… → sha512-ZL2+…), which is the cryptographic proof that a different, patched tarball will be installed. Bun verifies this hash on install, so the fix cannot be silently downgraded by a compromised registry mirror.
  • The exact pins became ranges: http-parser-js: "0.5.3" → ">=0.5.1", safe-buffer: "5.2.1" → ">=5.1.0", websocket-extensions: "0.1.4" → ">=0.1.1". The version lock described above is gone. http-parser-js is now free to resolve to a release that includes the CVE-2022-32210 fix, and the resolver can dedupe it with whatever version the rest of the tree already uses instead of forcing a second copy into node_modules.

One follow-up is worth doing by hand: because the loosened range only takes effect on resolution, confirm that the http-parser-js entry elsewhere in bun.lock actually landed on 0.5.8 or newer after install. A range that permits a patched version is not the same as a lockfile that records one.

Also note that websocket-extensions stays at 0.1.4 in the lockfile — which is correct. 0.1.4 is the patched release for CVE-2020-7662, the ReDoS in

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #10809

Related Articles

high

CWE-778: Tribute WebHookHandler Logs No Subscription Events

The Tribute payment webhook handler verified request signatures and then mutated subscription state — granting new subscriptions and cancelling existing ones — while logging nothing on success. Only error paths and a no-op test hook produced log lines, so a forged or replayed `NewSubscription` event that passed signature verification left no record of which `TelegramUserID` was affected. The fix adds structured `slog.Info` records at signature verification and at both state-changing branches.

medium

path-to-regexp 0.1.12 DoS: Catastrophic Backtracking on Malformed URL

path-to-regexp version 0.1.12 contains a regular expression engine vulnerability where maliciously crafted URL parameters trigger exponential backtracking, causing CPU exhaustion and service unavailability. Upgrading to version 0.1.13 eliminates the vulnerable pattern from the parsing logic.

critical

CVE-2026-59873: node-tar 7.5.11 DoS via Crafted Gzip Bomb

node-tar versions 7.5.11 through 7.5.18 are vulnerable to a denial-of-service attack through maliciously crafted gzip archives that decompress to disproportionately large sizes. An attacker can exploit this to exhaust memory and CPU resources by submitting a small, highly compressed archive that expands beyond configured limits during extraction.

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