8000 refactor: move packet validation to the server/client · socketio/socket.io-parser@6ddf6b3 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6ddf6b3

Browse files
refactor: move packet validation to the server/client
1 parent 887396d commit 6ddf6b3

File tree

2 files changed

+82
-51
lines changed

2 files changed

+82
-51
lines changed

lib/index.ts

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ export class Encoder {
131131
}
132132
}
133133

134-
// see https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript
135-
function isObject(value: any): boolean {
136-
return Object.prototype.toString.call(value) === "[object Object]";
137-
}
138-
139134
interface DecoderReservedEvents {
140135
decoded: (packet: Packet) => void;
141136
}
@@ -262,12 +257,7 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
262257

263258
// look up json data
264259
if (str.charAt(++i)) {
265-
const payload = this.tryParse(str.substr(i));
266-
if (Decoder.isPayloadValid(p.type, payload)) {
267-
p.data = payload;
268-
} else {
269-
throw new Error("invalid payload");
270-
}
260+
p.data = this.tryParse(str.substr(i));
271261
}
272262

273263
debug("decoded %s as %j", str, p);
@@ -282,28 +272,6 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
282272
}
283273
}
284274

285-
private static isPayloadValid(type: PacketType, payload: any): boolean {
286-
switch (type) {
287-
case PacketType.CONNECT:
288-
return isObject(payload);
289-
case PacketType.DISCONNECT:
290-
return payload === undefined;
291-
case PacketType.CONNECT_ERROR:
292-
return typeof payload === "string" || isObject(payload);
293-
case PacketType.EVENT:
294-
case PacketType.BINARY_EVENT:
295-
return (
296-
Array.isArray(payload) &&
297-
(typeof payload[0] === "number" ||
298-
(typeof payload[0] === "string" &&
299-
RESERVED_EVENTS.indexOf(payload[0]) === -1))
300-
);
301-
case PacketType.ACK:
302-
case PacketType.BINARY_ACK:
303-
return Array.isArray(payload);
304-
}
305-
}
306-
307275
/**
308276
* Deallocates a parser's resources
309277
*/
@@ -359,3 +327,56 @@ class BinaryReconstructor {
359327
this.buffers = [];
360328
}
361329
}
330+
331+
function isNamespaceValid(nsp: unknown) {
332+
return typeof nsp === "string";
333+
}
334+
335+
const isInteger =
336+
Number.isInteger ||
337+
function (value) {
338+
return (
339+
typeof value === "number" &&
340+
isFinite(value) &&
341+
Math.floor(value) === value
342+
);
343+
};
344+
345+
function isAckIdValid(id: unknown) {
346+
return id === undefined || isInteger(id);
347+
}
348+
349+
// see https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript
350+
function isObject(value: any): boolean {
351+
return Object.prototype.toString.call(value) === "[object Object]";
352+
}
353+
354+
function isDataValid(type: PacketType, payload: unknown) {
355+
switch (type) {
356+
case PacketType.CONNECT:
357+
return payload === undefined || isObject(payload);
358+
case PacketType.DISCONNECT:
359+
return payload === undefined;
360+
case PacketType.EVENT:
361+
return (
362+
Array.isArray(payload) &&
363+
(typeof payload[0] === "number&q B41A uot; ||
364+
(typeof payload[0] === "string" &&
365+
RESERVED_EVENTS.indexOf(payload[0]) === -1))
366+
);
367+
case PacketType.ACK:
368+
return Array.isArray(payload);
369+
case PacketType.CONNECT_ERROR:
370+
return typeof payload === "string" || isObject(payload);
371+
default:
372+
return false;
373+
}
374+
}
375+
376+
export function isPacketValid(packet: Packet): boolean {
377+
return (
378+
isNamespaceValid(packet.nsp) &&
379+
isAckIdValid(packet.id) &&
380+
isDataValid(packet.type, packet.data)
381+
);
382+
}

test/parser.js

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { PacketType, Decoder, Encoder } = require("..");
1+
const { PacketType, Decoder, Encoder, isPacketValid } = require("..");
22
const expect = require("expect.js");
33
const helpers = require("./helpers.js");
44

@@ -108,23 +108,6 @@ describe("socket.io-parser", () => {
108108
});
109109

110110
it("throw an error upon parsing error", () => {
111-
const isInvalidPayload = (str) =>
112-
expect(() => new Decoder().add(str)).to.throwException(
113-
/^invalid payload$/
114-
);
115-
116-
isInvalidPayload('442["some","data"');
117-
isInvalidPayload('0/admin,"invalid"');
118-
isInvalidPayload("0[]");
119-
isInvalidPayload("1/admin,{}");
120-
isInvalidPayload('2/admin,"invalid');
121-
isInvalidPayload("2/admin,{}");
122-
isInvalidPayload('2[{"toString":"foo"}]');
123-
isInvalidPayload('2[true,"foo"]');
124-
isInvalidPayload('2[null,"bar"]');
125-
isInvalidPayload('2["connect"]');
126-
isInvalidPayload('2["disconnect","123"]');
127-
128111
expect(() => new Decoder().add("999")).to.throwException(
129112
/^unknown packet type 9$/
130113
);
@@ -148,4 +131,31 @@ describe("socket.io-parser", () => {
148131
decoder.add('2["hello"]');
149132
});
150133
});
134+
135+
it("should ensure that a packet is valid", async () => {
136+
function decode(str) {
137+
return new Promise((resolve) => {
138+
const decoder = new Decoder();
139+
140+
decoder.on("decoded", (data) => {
141+
resolve(data);
142+
});
143+
144+
decoder.add(str);
145+
});
146+
}
147+
148+
expect(isPacketValid(await decode("0"))).to.eql(true);
149+
expect(isPacketValid(await decode('442["some","data"'))).to.eql(false);
150+
expect(isPacketValid(await decode('0/admin,"invalid"'))).to.eql(false);
151+
expect(isPacketValid(await decode("0[]"))).to.eql(false);
152+
expect(isPacketValid(await decode("1/admin,{}"))).to.eql(false);
153+
expect(isPacketValid(await decode('2/admin,"invalid'))).to.eql(false);
154+
expect(isPacketValid(await decode("2/admin,{}"))).to.eql(false);
155+
expect(isPacketValid(await decode('2[{"toString":"foo"}]'))).to.eql(false);
156+
expect(isPacketValid(await decode('2[true,"foo"]'))).to.eql(false);
157+
expect(isPacketValid(await decode('2[null,"bar"]'))).to.eql(false);
158+
expect(isPacketValid(await decode('2["connect"]'))).to.eql(false);
159+
expect(isPacketValid(await decode('2["disconnect","123"]'))).to.eql(false);
160+
});
151161
});

0 commit comments

Comments
 (0)
0