CVE-2022-21676 (JavaScript, published 2022-01-12T19:15Z)
Description: value: Engine.IO is the implementation of transport-based cross-browser/cross-device bi-directional communication layer for Socket.IO. A specially crafted HTTP request can trigger an uncaught exception on the Engine.IO server, thus killing the Node.js process. This impacts all the users of the `engine.io` package starting from version `4.0.0`, including those who uses depending packages like `socket.io`. Versions prior to `4.0.0` are not impacted. A fix has been released for each major branch, namely `4.1.2` for the `4.x.x` branch, `5.2.1` for the `5.x.x` branch, and `6.1.1` for the `6.x.x` branch. There is no known workaround except upgrading to a safe version.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-755 \u2014 Improper Handling of Exceptional Conditions
The product does not handle or incorrectly handles an exceptional condition.
Repository: https://github.com/socketio/engine.io
Files changed:
lib/server.ts
test/server.js
Commit message: fix: properly handle invalid data sent by a malicious websocket client

**IMPORTANT SECURITY FIX**

A malicious client could send a specially crafted HTTP request,
triggering an uncaught exception and killing the Node.js process:

> RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
>   at Receiver.getInfo (/.../node_modules/ws/lib/receiver.js:176:14)
>   at Receiver.startLoop (/.../node_modules/ws/lib/receiver.js:136:22)
>   at Receiver._write (/.../node_modules/ws/lib/receiver.js:83:10)
>   at writeOrBuffer (internal/streams/writable.js:358:12)

This bug was introduced by [1], included in `engine.io@4.0.0`, so
previous releases are not impacted.

[1]: https://github.com/socketio/engine.io/commit/f3c291fa613a9d50c924d74293035737fdace4f2

Thanks to Marcus Wejderot from Mevisio for the responsible disclosure.


Vulnerable code:
      // transport error handling takes over
      websocket.removeListener("error", onUpgradeError);


Fixed code:

    it("should not throw when the client sends invalid data during the handshake (ws only)", done => {
      listen(port => {
        // will throw "RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear"
        request
          .get(`http://localhost:${port}/engine.io/`)
          .set("connection", "upgrade")
          .set("upgrade", "websocket")
          .set("Sec-WebSocket-Version", "13")
          .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==")
          .query({ transport: "websocket", EIO: 4 })
          .send("test")
          .end(() => {});

        setTimeout(done, 50);
      });
    });

    it("should not throw when the client sends invalid data during the handshake (upgrade)", done => {
      listen(port => {
        request
          .get(`http://localhost:${port}/engine.io/`)
          .query({ transport: "polling", EIO: 4 })
          .end((err, res) => {
            const sid = JSON.parse(res.text.substring(1)).sid;

            request
              .get(`http://localhost:${port}/engine.io/`)
              .set("connection", "upgrade")
              .set("upgrade", "websocket")
              .set("Sec-WebSocket-Version", "13")
              .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==")
              .query({ transport: "websocket", EIO: 4, sid })
              .send("test")
              .end(() => {});

            setTimeout(done, 50);
          });
      });
    });