CVE Patch Review

CVE-2026-9595: Exact HMR WebSocket Path Matching Fix in webpack-dev-server

CVE-2026-9595 · Updated 2026-06-17 Root-cause

Summary

The patch changes webpack-dev-server's upgrade dispatch so HMR WebSocket requests are identified using the same raw path comparison semantics as the underlying ws server, preventing proxy middleware from intercepting URL variants that ws itself would reject as HMR. This addresses the parser discrepancy described in the CVE and is reinforced by regression tests covering exact-match and variant paths.

Analysis

Vulnerability

CVE-2026-9595 describes a WebSocket proxying flaw in webpack-dev-server where HMR upgrade dispatch used URL parsing and normalization that did not match the underlying ws server's request-target handling. Per the patch comments and tests in the upstream fix, webpack-dev-server previously derived pathname via new URL(req.url, "http://0.0.0.0"), while ws performs a raw, case-sensitive comparison with only the query string stripped. That discrepancy allowed path variants such as //ws, /WS, or percent-encoded forms to be classified by dev-server as HMR traffic even though ws would not accept them as the HMR socket. As a result, upgrade handling could fall through into user-configured proxy middleware, enabling interception of local HMR WebSocket traffic and bypassing Host/Origin protections, with cookie leakage to proxy targets as noted in the CVE record and related references.

The issue is documented in the upstream commit 948d5e6089bebcd801dac2cbe3ed4f80b64f117a, the associated pull request #4316, the NVD entry, and the CVE record.

if (hmrPath && req.url) {
  const { pathname } = new URL(req.url, "http://0.0.0.0");
  if (pathname === hmrPath) {
    return;
  }
}
proxyUpgrade(req, socket, head);

Patch

The patch updates lib/Server.js so HMR path detection mirrors ws semantics instead of Node URL normalization. It first extracts the configured HMR path from this.options.webSocketServer.options.path, then wraps proxy upgrade handling with a guard that compares the raw request target exactly, stripping only the query string. The patch comment explicitly states the intent: match WebSocketServer#shouldHandle behavior in ws and avoid normalization that would incorrectly classify variants as HMR.

if (hmrPath && typeof req.url === "string") {
  const queryIndex = req.url.indexOf("?");
  const pathname =
    queryIndex !== -1 ? req.url.slice(0, queryIndex) : req.url;

  if (pathname === hmrPath) {
    return;
  }
}

proxyUpgrade(req, socket, head);

The test suite additions in the official patch and PR #4316 are substantial and directly target the bug mechanics:

  • Regression coverage verifies that the real HMR path is served locally and not forwarded to a permissive backend proxy.
  • Non-HMR upgrade paths are still forwarded to the user proxy, preserving intended behavior.
  • ws-specific exact-match tests assert that /ws and /ws?token=1 are local HMR, while //ws, /ws/, /WS, /wS, and /%77%73 are forwarded.
  • Tests also cover custom configured HMR paths rather than assuming a hardcoded /ws.

Review

Pros

  • The fix addresses the actual parser mismatch rather than adding another validation layer. Aligning dispatch logic with ws removes the ambiguity at the source.
  • The implementation is minimal and low-risk: it replaces URL normalization with a raw string split on ?, which is exactly the behavior described in the patch comment.
  • The new tests are strong regression guards because they exercise both security-relevant variants and expected proxy pass-through behavior.
  • The patch preserves compatibility for legitimate HMR connections, including query strings and custom HMR paths.
  • The code comment documents the dependency on ws semantics, which is important for future maintainers and reduces the chance of reintroducing normalization bugs.

Cons

  • The correctness of the fix is intentionally coupled to current ws path-handling behavior. If upstream ws semantics change, webpack-dev-server may need to be updated again.
  • The patch is narrowly scoped to HMR upgrade dispatch. It does not establish a broader invariant or shared helper for path matching across all WebSocket-related code paths.
  • The available diff does not show additional hardening around Host/Origin checks themselves; it prevents the bypass by ensuring the request is routed correctly before proxying.

Verdict

Root-cause.

This patch fixes the root cause identified in the advisory: inconsistent interpretation of the WebSocket request target between webpack-dev-server and ws. By making HMR path classification use the same raw, case-sensitive, query-stripped comparison as the underlying WebSocket server, it closes the dispatch gap that allowed proxy interception of local HMR traffic. The regression tests are directly aligned with the exploit shape described in NVD and the upstream patch discussion in PR #4316. For engineering teams, this looks like a well-scoped and technically correct remediation rather than a superficial filter.

Sources