This is an automated email from the ASF dual-hosted git repository.

jensg pushed a commit to branch 0.23.0
in repository https://gitbox.apache.org/repos/asf/thrift.git

commit 4af8c7c8768cb687b182c1839a39d4beb4f35a5d
Author: Jens Geyer <[email protected]>
AuthorDate: Sat Apr 11 12:29:02 2026 +0200

    Add recursion depth limit to Node.js protocol skip()
    Client: nodejs
    
    Add a depth parameter to skip() in TBinaryProtocol, TCompactProtocol,
    and TJSONProtocol to prevent stack exhaustion from deeply nested
    structures. Limit set to 64, matching the C++ and Java implementations.
    
    (01)
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 lib/nodejs/lib/thrift/binary_protocol.js  | 19 +++++++++++++------
 lib/nodejs/lib/thrift/compact_protocol.js | 19 +++++++++++++------
 lib/nodejs/lib/thrift/json_protocol.js    | 19 +++++++++++++------
 3 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/lib/nodejs/lib/thrift/binary_protocol.js 
b/lib/nodejs/lib/thrift/binary_protocol.js
index d5b2e1437..b2c3720a6 100644
--- a/lib/nodejs/lib/thrift/binary_protocol.js
+++ b/lib/nodejs/lib/thrift/binary_protocol.js
@@ -317,7 +317,14 @@ TBinaryProtocol.prototype.getTransport = function () {
   return this.trans;
 };
 
-TBinaryProtocol.prototype.skip = function (type) {
+TBinaryProtocol.prototype.skip = function (type, depth) {
+  depth = (depth || 0) + 1;
+  if (depth > 64) {
+    throw new Thrift.TProtocolException(
+      Thrift.TProtocolExceptionType.DEPTH_LIMIT,
+      "Maximum skip depth exceeded"
+    );
+  }
   switch (type) {
     case Type.BOOL:
       this.readBool();
@@ -350,7 +357,7 @@ TBinaryProtocol.prototype.skip = function (type) {
         if (r.ftype === Type.STOP) {
           break;
         }
-        this.skip(r.ftype);
+        this.skip(r.ftype, depth);
         this.readFieldEnd();
       }
       this.readStructEnd();
@@ -358,22 +365,22 @@ TBinaryProtocol.prototype.skip = function (type) {
     case Type.MAP:
       var mapBegin = this.readMapBegin();
       for (var i = 0; i < mapBegin.size; ++i) {
-        this.skip(mapBegin.ktype);
-        this.skip(mapBegin.vtype);
+        this.skip(mapBegin.ktype, depth);
+        this.skip(mapBegin.vtype, depth);
       }
       this.readMapEnd();
       break;
     case Type.SET:
       var setBegin = this.readSetBegin();
       for (var i2 = 0; i2 < setBegin.size; ++i2) {
-        this.skip(setBegin.etype);
+        this.skip(setBegin.etype, depth);
       }
       this.readSetEnd();
       break;
     case Type.LIST:
       var listBegin = this.readListBegin();
       for (var i3 = 0; i3 < listBegin.size; ++i3) {
-        this.skip(listBegin.etype);
+        this.skip(listBegin.etype, depth);
       }
       this.readListEnd();
       break;
diff --git a/lib/nodejs/lib/thrift/compact_protocol.js 
b/lib/nodejs/lib/thrift/compact_protocol.js
index dd7851e7a..2f6fc71b4 100644
--- a/lib/nodejs/lib/thrift/compact_protocol.js
+++ b/lib/nodejs/lib/thrift/compact_protocol.js
@@ -901,7 +901,14 @@ TCompactProtocol.prototype.zigzagToI64 = function (n) {
   return new Int64(hi, lo);
 };
 
-TCompactProtocol.prototype.skip = function (type) {
+TCompactProtocol.prototype.skip = function (type, depth) {
+  depth = (depth || 0) + 1;
+  if (depth > 64) {
+    throw new Thrift.TProtocolException(
+      Thrift.TProtocolExceptionType.DEPTH_LIMIT,
+      "Maximum skip depth exceeded"
+    );
+  }
   switch (type) {
     case Type.BOOL:
       this.readBool();
@@ -934,7 +941,7 @@ TCompactProtocol.prototype.skip = function (type) {
         if (r.ftype === Type.STOP) {
           break;
         }
-        this.skip(r.ftype);
+        this.skip(r.ftype, depth);
         this.readFieldEnd();
       }
       this.readStructEnd();
@@ -942,22 +949,22 @@ TCompactProtocol.prototype.skip = function (type) {
     case Type.MAP:
       var mapBegin = this.readMapBegin();
       for (var i = 0; i < mapBegin.size; ++i) {
-        this.skip(mapBegin.ktype);
-        this.skip(mapBegin.vtype);
+        this.skip(mapBegin.ktype, depth);
+        this.skip(mapBegin.vtype, depth);
       }
       this.readMapEnd();
       break;
     case Type.SET:
       var setBegin = this.readSetBegin();
       for (var i2 = 0; i2 < setBegin.size; ++i2) {
-        this.skip(setBegin.etype);
+        this.skip(setBegin.etype, depth);
       }
       this.readSetEnd();
       break;
     case Type.LIST:
       var listBegin = this.readListBegin();
       for (var i3 = 0; i3 < listBegin.size; ++i3) {
-        this.skip(listBegin.etype);
+        this.skip(listBegin.etype, depth);
       }
       this.readListEnd();
       break;
diff --git a/lib/nodejs/lib/thrift/json_protocol.js 
b/lib/nodejs/lib/thrift/json_protocol.js
index 176da38c6..f8dd7e60d 100644
--- a/lib/nodejs/lib/thrift/json_protocol.js
+++ b/lib/nodejs/lib/thrift/json_protocol.js
@@ -781,7 +781,14 @@ TJSONProtocol.prototype.getTransport = function () {
 /**
  * Method to arbitrarily skip over data
  */
-TJSONProtocol.prototype.skip = function (type) {
+TJSONProtocol.prototype.skip = function (type, depth) {
+  depth = (depth || 0) + 1;
+  if (depth > 64) {
+    throw new Thrift.TProtocolException(
+      Thrift.TProtocolExceptionType.DEPTH_LIMIT,
+      "Maximum skip depth exceeded"
+    );
+  }
   switch (type) {
     case Type.BOOL:
       this.readBool();
@@ -814,7 +821,7 @@ TJSONProtocol.prototype.skip = function (type) {
         if (r.ftype === Type.STOP) {
           break;
         }
-        this.skip(r.ftype);
+        this.skip(r.ftype, depth);
         this.readFieldEnd();
       }
       this.readStructEnd();
@@ -822,22 +829,22 @@ TJSONProtocol.prototype.skip = function (type) {
     case Type.MAP:
       var mapBegin = this.readMapBegin();
       for (var i = 0; i < mapBegin.size; ++i) {
-        this.skip(mapBegin.ktype);
-        this.skip(mapBegin.vtype);
+        this.skip(mapBegin.ktype, depth);
+        this.skip(mapBegin.vtype, depth);
       }
       this.readMapEnd();
       break;
     case Type.SET:
       var setBegin = this.readSetBegin();
       for (var i2 = 0; i2 < setBegin.size; ++i2) {
-        this.skip(setBegin.etype);
+        this.skip(setBegin.etype, depth);
       }
       this.readSetEnd();
       break;
     case Type.LIST:
       var listBegin = this.readListBegin();
       for (var i3 = 0; i3 < listBegin.size; ++i3) {
-        this.skip(listBegin.etype);
+        this.skip(listBegin.etype, depth);
       }
       this.readListEnd();
       break;

Reply via email to