diff --git a/lib/index.ts b/lib/index.ts index d858915..6dfc9a4 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -280,11 +280,11 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> { private static isPayloadValid(type: PacketType, payload: any): boolean { switch (type) { case PacketType.CONNECT: - return typeof payload === "object"; + return isObject(payload); case PacketType.DISCONNECT: return payload === undefined; case PacketType.CONNECT_ERROR: - return typeof payload === "string" || typeof payload === "object"; + return typeof payload === "string" || isObject(payload); case PacketType.EVENT: case PacketType.BINARY_EVENT: return ( @@ -354,3 +354,46 @@ class BinaryReconstructor { this.buffers = []; } } + +export function isPacketValid(packet: Packet): boolean { + return ( + isNamespaceValid(packet) && + isAckIdValid(packet) && + isPayloadValid(packet.type, packet.data) + ); +} + +function isNamespaceValid(packet: Packet): boolean { + return typeof packet.nsp === "string"; +} + +function isAckIdValid(packet: Packet) { + return packet.id === undefined || Number.isInteger(packet.id); +} + +// see https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript +function isObject(value: any): boolean { + return Object.prototype.toString.call(value) === "[object Object]"; +} + +function isPayloadValid(type: PacketType, payload: any): boolean { + switch (type) { + case PacketType.CONNECT: + return payload === undefined || isObject(payload); + case PacketType.DISCONNECT: + return payload === undefined; + case PacketType.EVENT: + return ( + Array.isArray(payload) && + (typeof payload[0] === "number" || + (typeof payload[0] === "string" && + RESERVED_EVENTS.indexOf(payload[0]) === -1)) + ); + case PacketType.ACK: + return Array.isArray(payload); + case PacketType.CONNECT_ERROR: + return typeof payload === "string" || isObject(payload); + default: + return false; + } +} diff --git a/test/parser.js b/test/parser.js index 915e746..7a45e6a 100644 --- a/test/parser.js +++ b/test/parser.js @@ -1,4 +1,9 @@ -const { PacketType, Decoder, Encoder } = require(".."); +const { + PacketType, + Decoder, + Encoder, + isPacketValid, +} = require(".."); const expect = require("expect.js"); const helpers = require("./helpers.js"); @@ -147,4 +152,24 @@ describe("socket.io-parser", () => { decoder.add('2["hello"]'); }); }); + + it("should ensure that a packet is valid", () => { + expect(isPacketValid({ type: 0, nsp: "/" })).to.eql(true); + expect(isPacketValid({ type: 0, nsp: "/", data: { foo: "bar" } })).to.eql(true); + expect(isPacketValid({ type: 1, nsp: "/" })).to.eql(true); + expect(isPacketValid({ type: 2, nsp: "/", data: ["foo"] })).to.eql(true); + expect(isPacketValid({ type: 2, nsp: "/", data: [1] })).to.eql(true); + expect(isPacketValid({ type: 3, nsp: "/", id: 1, data: ["foo"] })).to.eql(true); + expect(isPacketValid({ type: 4, nsp: "/", data: "foo" })).to.eql(true); + expect(isPacketValid({ type: 4, nsp: "/", data: { foo: "bar" } })).to.eql(true); + + expect(isPacketValid({ type: 9, nsp: "/" })).to.eql(false); + expect(isPacketValid({ type: 0 })).to.eql(false); + expect(isPacketValid({ type: 0, nsp: 1 })).to.eql(false); + expect(isPacketValid({ type: 0, nsp: "/", data: ["foo"] })).to.eql(false); + expect(isPacketValid({ type: 1, nsp: "/", data: ["foo"] })).to.eql(false); + expect(isPacketValid({ type: 2, nsp: "/", data: { foo: "bar" } })).to.eql(false); + expect(isPacketValid({ type: 3, nsp: "/", id: "1", data: ["foo"] })).to.eql(false); + expect(isPacketValid({ type: 4, nsp: "/" })).to.eql(false); + }); });