VYPR
High severity7.5NVD Advisory· Published Mar 20, 2026· Updated Apr 14, 2026

CVE-2026-33151

CVE-2026-33151

Description

Socket.IO is an open source, real-time, bidirectional, event-based, communication framework. Prior to versions 3.3.5, 3.4.4, and 4.2.6, a specially crafted Socket.IO packet can make the server wait for a large number of binary attachments and buffer them, which can be exploited to make the server run out of memory. This issue has been patched in versions 3.3.5, 3.4.4, and 4.2.6.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
socket.io-parsernpm
< 3.3.53.3.5
socket.io-parsernpm
>= 3.4.0, < 3.4.43.4.4
socket.io-parsernpm
>= 4.0.0, < 4.2.64.2.6

Affected products

1

Patches

3
719f9ebab077

fix(parser): add a limit to the number of binary attachments

https://github.com/socketio/socket.ioDamien ArrachequesneMar 17, 2026via ghsa
2 files changed · +46 4
  • index.js+25 4 modified
    @@ -218,8 +218,12 @@ function encodeAsBinary(obj, callback) {
      * @api public
      */
     
    -function Decoder() {
    +function Decoder(opts) {
       this.reconstructor = null;
    +  opts = opts || {};
    +  this.opts = {
    +    maxAttachments: opts.maxAttachments || 10,
    +  };
     }
     
     /**
    @@ -242,7 +246,7 @@ Decoder.prototype.add = function(obj) {
         if (this.reconstructor) {
           throw new Error("got plaintext data when reconstructing a packet");
         }
    -    packet = decodeString(obj);
    +    packet = decodeString(obj, this.opts.maxAttachments);
         if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json
           this.reconstructor = new BinaryReconstructor(packet);
     
    @@ -272,11 +276,12 @@ Decoder.prototype.add = function(obj) {
      * Decode a packet String (JSON data)
      *
      * @param {String} str
    + * @param {Number} maxAttachments - the maximum number of binary attachments
      * @return {Object} packet
      * @api private
      */
     
    -function decodeString(str) {
    +function decodeString(str, maxAttachments) {
       var i = 0;
       // look up type
       var p = {
    @@ -295,7 +300,13 @@ function decodeString(str) {
         if (buf != Number(buf) || str.charAt(i) !== '-') {
           throw new Error('Illegal attachments');
         }
    -    p.attachments = Number(buf);
    +    var n = Number(buf);
    +    if (!isInteger(n) || n < 0) {
    +      throw new Error("Illegal attachments");
    +    } else if (n > maxAttachments) {
    +      throw new Error("too many attachments");
    +    }
    +    p.attachments = n;
       }
     
       // look up namespace (if any)
    @@ -432,3 +443,13 @@ function error(msg) {
         data: 'parser error: ' + msg
       };
     }
    +
    +var isInteger =
    +  Number.isInteger ||
    +  function (value) {
    +    return (
    +      typeof value === "number" &&
    +      isFinite(value) &&
    +      Math.floor(value) === value
    +    );
    +  };
    
  • test/parser.js+21 0 modified
    @@ -101,5 +101,26 @@ describe('parser', function(){
         isInvalidPayload('2[{"toString":"foo"}]');
         isInvalidPayload('2[true,"foo"]');
         isInvalidPayload('2[null,"bar"]');
    +
    +    function isInvalidAttachmentCount (str) {
    +      expect(() => new parser.Decoder().add(str)).to.throwException(
    +          /^Illegal attachments$/,
    +      );
    +    }
    +
    +    isInvalidAttachmentCount("5");
    +    isInvalidAttachmentCount("51");
    +    isInvalidAttachmentCount("5a-");
    +    isInvalidAttachmentCount("51.23-");
    +  });
    +
    +  it("throws an error when receiving too many attachments", () => {
    +    const decoder = new parser.Decoder({ maxAttachments: 2 });
    +
    +    expect(() => {
    +      decoder.add(
    +          '53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
    +      );
    +    }).to.throwException(/^too many attachments$/);
       });
     });
    
9d39f1f08051

fix(parser): add a limit to the number of binary attachments

https://github.com/socketio/socket.ioDamien ArrachequesneMar 17, 2026via ghsa
2 files changed · +46 4
  • index.js+25 4 modified
    @@ -218,8 +218,12 @@ function encodeAsBinary(obj, callback) {
      * @api public
      */
     
    -function Decoder() {
    +function Decoder(opts) {
       this.reconstructor = null;
    +  opts = opts || {};
    +  this.opts = {
    +    maxAttachments: opts.maxAttachments || 10,
    +  };
     }
     
     /**
    @@ -242,7 +246,7 @@ Decoder.prototype.add = function(obj) {
         if (this.reconstructor) {
           throw new Error("got plaintext data when reconstructing a packet");
         }
    -    packet = decodeString(obj);
    +    packet = decodeString(obj, this.opts.maxAttachments);
         if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json
           this.reconstructor = new BinaryReconstructor(packet);
     
    @@ -292,11 +296,12 @@ function isPayloadValid(type, payload) {
      * Decode a packet String (JSON data)
      *
      * @param {String} str
    + * @param {Number} maxAttachments - the maximum number of binary attachments
      * @return {Object} packet
      * @api private
      */
     
    -function decodeString(str) {
    +function decodeString(str, maxAttachments) {
       var i = 0;
       // look up type
       var p = {
    @@ -315,7 +320,13 @@ function decodeString(str) {
         if (buf != Number(buf) || str.charAt(i) !== '-') {
           throw new Error('Illegal attachments');
         }
    -    p.attachments = Number(buf);
    +    var n = Number(buf);
    +    if (!isInteger(n) || n < 0) {
    +      throw new Error("Illegal attachments");
    +    } else if (n > maxAttachments) {
    +      throw new Error("too many attachments");
    +    }
    +    p.attachments = n;
       }
     
       // look up namespace (if any)
    @@ -432,3 +443,13 @@ function error(msg) {
         data: 'parser error: ' + msg
       };
     }
    +
    +var isInteger =
    +  Number.isInteger ||
    +  function (value) {
    +    return (
    +      typeof value === "number" &&
    +      isFinite(value) &&
    +      Math.floor(value) === value
    +    );
    +  };
    
  • test/parser.js+21 0 modified
    @@ -101,5 +101,26 @@ describe('parser', function(){
         isInvalidPayload('2[{"toString":"foo"}]');
         isInvalidPayload('2[true,"foo"]');
         isInvalidPayload('2[null,"bar"]');
    +
    +    function isInvalidAttachmentCount (str) {
    +      expect(() => new parser.Decoder().add(str)).to.throwException(
    +          /^Illegal attachments$/,
    +      );
    +    }
    +
    +    isInvalidAttachmentCount("5");
    +    isInvalidAttachmentCount("51");
    +    isInvalidAttachmentCount("5a-");
    +    isInvalidAttachmentCount("51.23-");
    +  });
    +
    +  it("throws an error when receiving too many attachments", () => {
    +    const decoder = new parser.Decoder({ maxAttachments: 2 });
    +
    +    expect(() => {
    +      decoder.add(
    +          '53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
    +      );
    +    }).to.throwException(/^too many attachments$/);
       });
     });
    
b25738c416c4

fix(parser): add a limit to the number of binary attachments

https://github.com/socketio/socket.ioDamien ArrachequesneMar 17, 2026via ghsa
2 files changed · +91 5
  • packages/socket.io-parser/lib/index.ts+31 5 modified
    @@ -135,21 +135,41 @@ interface DecoderReservedEvents {
       decoded: (packet: Packet) => void;
     }
     
    +type JSONReviver = (this: any, key: string, value: any) => any;
    +
    +export interface DecoderOptions {
    +  /**
    +   * Custom reviver to pass down to JSON.parse()
    +   */
    +  reviver?: JSONReviver;
    +  /**
    +   * Maximum number of binary attachments per packet
    +   * @default 10
    +   */
    +  maxAttachments?: number;
    +}
    +
     /**
      * A socket.io Decoder instance
      *
      * @return {Object} decoder
      */
     export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
       private reconstructor: BinaryReconstructor;
    +  private opts: Required<DecoderOptions>;
     
       /**
        * Decoder constructor
    -   *
    -   * @param {function} reviver - custom reviver to pass down to JSON.stringify
        */
    -  constructor(private reviver?: (this: any, key: string, value: any) => any) {
    +  constructor(opts?: DecoderOptions | JSONReviver) {
         super();
    +    this.opts = Object.assign(
    +      {
    +        reviver: undefined,
    +        maxAttachments: 10,
    +      },
    +      typeof opts === "function" ? { reviver: opts } : opts,
    +    );
       }
     
       /**
    @@ -224,7 +244,13 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
           if (buf != Number(buf) || str.charAt(i) !== "-") {
             throw new Error("Illegal attachments");
           }
    -      p.attachments = Number(buf);
    +      const n = Number(buf);
    +      if (!isInteger(n) || n < 0) {
    +        throw new Error("Illegal attachments");
    +      } else if (n > this.opts.maxAttachments) {
    +        throw new Error("too many attachments");
    +      }
    +      p.attachments = n;
         }
     
         // look up namespace (if any)
    @@ -271,7 +297,7 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
     
       private tryParse(str) {
         try {
    -      return JSON.parse(str, this.reviver);
    +      return JSON.parse(str, this.opts.reviver);
         } catch (e) {
           return false;
         }
    
  • packages/socket.io-parser/test/parser.js+60 0 modified
    @@ -107,6 +107,56 @@ describe("socket.io-parser", () => {
         }
       });
     
    +  it("throws an error when receiving too many attachments", () => {
    +    const decoder = new Decoder({ maxAttachments: 2 });
    +
    +    expect(() => {
    +      decoder.add(
    +        '53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
    +      );
    +    }).to.throwException(/^too many attachments$/);
    +  });
    +
    +  it("decodes with a custom reviver", () => {
    +    const decoder = new Decoder((key, value) => {
    +      if (key === "a") {
    +        return value.toUpperCase();
    +      } else {
    +        return value;
    +      }
    +    });
    +
    +    return new Promise((resolve) => {
    +      decoder.on("decoded", (packet) => {
    +        expect(packet.data).to.eql(["b", { a: "VAL" }]);
    +        resolve();
    +      });
    +
    +      decoder.add('2["b",{"a":"val"}]');
    +    });
    +  });
    +
    +  it("decodes with a custom reviver (options object)", () => {
    +    const decoder = new Decoder({
    +      reviver: (key, value) => {
    +        if (key === "a") {
    +          return value.toUpperCase();
    +        } else {
    +          return value;
    +        }
    +      },
    +    });
    +
    +    return new Promise((resolve) => {
    +      decoder.on("decoded", (packet) => {
    +        expect(packet.data).to.eql(["b", { a: "VAL" }]);
    +        resolve();
    +      });
    +
    +      decoder.add('2["b",{"a":"val"}]');
    +    });
    +  });
    +
       it("throw an error upon parsing error", () => {
         const isInvalidPayload = (str) =>
           expect(() => new Decoder().add(str)).to.throwException(
    @@ -125,6 +175,16 @@ describe("socket.io-parser", () => {
         isInvalidPayload('2["connect"]');
         isInvalidPayload('2["disconnect","123"]');
     
    +    const isInvalidAttachmentCount = (str) =>
    +      expect(() => new Decoder().add(str)).to.throwException(
    +        /^Illegal attachments$/,
    +      );
    +
    +    isInvalidAttachmentCount("5");
    +    isInvalidAttachmentCount("51");
    +    isInvalidAttachmentCount("5a-");
    +    isInvalidAttachmentCount("51.23-");
    +
         expect(() => new Decoder().add("999")).to.throwException(
           /^unknown packet type 9$/,
         );
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.