Skip to content

Commit 1fa1f46

Browse files
fix(eio): handle invalid packets when upgrading to WebTransport
1 parent 4f7edb4 commit 1fa1f46

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

packages/engine.io/lib/server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ function parseSessionId(data: string): string | undefined {
166166
} catch (e) {}
167167
}
168168

169+
// Object.hasOwn() was introduced in Node.js 16.9
170+
function hasOwn(obj: Record<string, any>, key: string): boolean {
171+
return Object.prototype.hasOwnProperty.call(obj, key);
172+
}
173+
169174
export abstract class BaseServer extends EventEmitter {
170175
public opts: ServerOptions;
171176

@@ -583,7 +588,7 @@ export abstract class BaseServer extends EventEmitter {
583588

584589
const sid = parseSessionId(value.data);
585590

586-
if (!sid) {
591+
if (!sid || !hasOwn(this.clients, sid)) {
587592
debug("invalid WebTransport handshake");
588593
return session.close();
589594
}

packages/engine.io/test/webtransport.mjs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,96 @@ describe("WebTransport", () => {
302302
);
303303
});
304304

305+
it("should close a connection that sends an invalid upgrade", (done) => {
306+
setupServer(
307+
{
308+
transports: ["polling", "websocket", "webtransport"],
309+
},
310+
async ({ engine, h3Server, certificate }) => {
311+
const httpServer = await createHttpServer(h3Server.port);
312+
engine.attach(httpServer);
313+
314+
request(`http://localhost:${h3Server.port}/engine.io/`)
315+
.query({ EIO: 4, transport: "polling" })
316+
.end(async (_, res) => {
317+
const payload = JSON.parse(res.text.substring(1));
318+
319+
expect(payload.upgrades).to.eql(["websocket", "webtransport"]);
320+
321+
const client = new WebTransport(
322+
`https://127.0.0.1:${h3Server.port}/engine.io/`,
323+
{
324+
serverCertificateHashes: [
325+
{
326+
algorithm: "sha-256",
327+
value: certificate.hash,
328+
},
329+
],
330+
},
331+
);
332+
333+
await client.ready;
334+
335+
const stream = await client.createBidirectionalStream();
336+
const writer = stream.writable.getWriter();
337+
338+
await writer.write(Uint8Array.of(31));
339+
await writer.write(
340+
TEXT_ENCODER.encode(`0{"sid":"11111111111111111111"}`),
341+
);
342+
343+
client.closed.then(() => {
344+
success(engine, h3Server, done);
345+
});
346+
});
347+
},
348+
);
349+
});
350+
351+
it("should close a connection that sends an invalid upgrade (bis)", (done) => {
352+
setupServer(
353+
{
354+
transports: ["polling", "websocket", "webtransport"],
355+
},
356+
async ({ engine, h3Server, certificate }) => {
357+
const httpServer = await createHttpServer(h3Server.port);
358+
engine.attach(httpServer);
359+
360+
request(`http://localhost:${h3Server.port}/engine.io/`)
361+
.query({ EIO: 4, transport: "polling" })
362+
.end(async (_, res) => {
363+
const payload = JSON.parse(res.text.substring(1));
364+
365+
expect(payload.upgrades).to.eql(["websocket", "webtransport"]);
366+
367+
const client = new WebTransport(
368+
`https://127.0.0.1:${h3Server.port}/engine.io/`,
369+
{
370+
serverCertificateHashes: [
371+
{
372+
algorithm: "sha-256",
373+
value: certificate.hash,
374+
},
375+
],
376+
},
377+
);
378+
379+
await client.ready;
380+
381+
const stream = await client.createBidirectionalStream();
382+
const writer = stream.writable.getWriter();
383+
384+
await writer.write(Uint8Array.of(20));
385+
await writer.write(TEXT_ENCODER.encode(`0{"sid":"__proto__"}`));
386+
387+
client.closed.then(() => {
388+
success(engine, h3Server, done);
389+
});
390+
});
391+
},
392+
);
393+
});
394+
305395
it("should send ping/pong packets", (done) => {
306396
setup(
307397
{

0 commit comments

Comments
 (0)