Skip to content

Comments

feat(javascript): limit the depth of deserialize & serialize#3382

Open
miantalha45 wants to merge 4 commits intoapache:mainfrom
miantalha45:limit-depth-of-deserialize-serialize
Open

feat(javascript): limit the depth of deserialize & serialize#3382
miantalha45 wants to merge 4 commits intoapache:mainfrom
miantalha45:limit-depth-of-deserialize-serialize

Conversation

@miantalha45
Copy link
Contributor

@miantalha45 miantalha45 commented Feb 20, 2026

What does this PR do?

Adds depth limiting for deserialization to prevent stack overflow and denial-of-service attacks from maliciously crafted deeply nested data structures.

Why is this needed?

Without depth limits, an attacker could send deeply nested serialized data that causes stack overflow during deserialization, crashing the application or causing resource exhaustion.

Implementation

  • Added maxDepth config option (default: 50, minimum: 2)
  • Depth tracked only during deserialization (security-focused)
  • Integrated into code generator with try/finally for proper cleanup
  • Comprehensive test coverage (29 tests)

Usage

const fory = new Fory({ maxDepth: 100 });

Consistency

Follows the same pattern as Java and Python implementations for cross-language alignment.

Fixes #3335

${this.readTypeInfo()}
${this.read(assignStmt, refState)};
fory.incReadDepth();
try {
Copy link
Collaborator

@chaokunyang chaokunyang Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the try finally will introduce extra cost. Please remove try finally, and refactor serialize/deserializae entry point, create resetWrite/resetRead methods, move code like:

    this.referenceResolver.resetRead();
    this.binaryReader.reset(bytes);
    this.typeMetaResolver.resetRead();
    this.metaStringResolver.resetRead();

into resetRead()

and move smililiar methods into resetWrite.

Then set depth to 0 in resetRead() and resetWrite.

Current referenceResolver/typeMetaResolver/metaStringResolver may lack some such methods, please take java as referencev and add to javascript

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

… overhead

- Remove try/finally blocks from generated read code for performance
- Create resetRead() and resetWrite() methods in Fory class
- Move all reset logic into these methods (depth, resolvers, etc)
- Add resetRead() and resetWrite() methods to all resolver classes
- Depth is now reset to 0 at the start of each deserialization
- Depth accumulates during deserialization without manual decrement
- Keeps zero-cost performance while maintaining security protection
- Update tests to match new behavior: depth resets at start of each call
fory.decReadDepth();
}
${this.readTypeInfo()}
${this.read(assignStmt, refState)};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where you decReadDepth?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also curious why the tests don't fail? The tests for depth is also wrong

@chaokunyang
Copy link
Collaborator

Please do not write filename such as javascript/test/depth-limit.test.ts, this is not right. Please see how other javascript files are named.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[JavaScript] limit the depth of deserialize & serialize

2 participants