Files
bun.sh/test/js/bun/jsonc/jsonc.test.ts
robobun 1344151576 fix(json): prevent stack overflow in JSONC parser on deeply nested input (#26174)
## Summary
- Add stack overflow protection to JSON/JSONC parser to prevent
segmentation faults
- Parser now throws `RangeError: Maximum call stack size exceeded`
instead of crashing
- Fixes DoS vulnerability when parsing deeply nested JSON structures
(~150k+ depth)

## Test plan
- [x] Added regression tests for deeply nested arrays and objects (25k
depth)
- [x] Verified system Bun v1.3.6 crashes with segfault at 150k depth
- [x] Verified fix throws proper error instead of crashing
- [x] All existing JSONC tests pass

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 16:23:01 -08:00

138 lines
3.1 KiB
TypeScript

import { expect, test } from "bun:test";
test("Bun.JSONC exists", () => {
expect(Bun.JSONC).toBeDefined();
expect(typeof Bun.JSONC).toBe("object");
expect(typeof Bun.JSONC.parse).toBe("function");
});
test("Bun.JSONC.parse handles basic JSON", () => {
const result = Bun.JSONC.parse('{"name": "test", "value": 42}');
expect(result).toEqual({ name: "test", value: 42 });
});
test("Bun.JSONC.parse handles comments", () => {
const jsonc = `{
// This is a comment
"name": "test",
/* This is a block comment */
"value": 42
}`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual({ name: "test", value: 42 });
});
test("Bun.JSONC.parse handles trailing commas", () => {
const jsonc = `{
"name": "test",
"value": 42,
}`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual({ name: "test", value: 42 });
});
test("Bun.JSONC.parse handles arrays with trailing commas", () => {
const jsonc = `[
1,
2,
3,
]`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual([1, 2, 3]);
});
test("Bun.JSONC.parse handles complex JSONC", () => {
const jsonc = `{
// Configuration object
"name": "my-app",
"version": "1.0.0",
/* Dependencies section */
"dependencies": {
"react": "^18.0.0",
"typescript": "^5.0.0", // Latest TypeScript
},
"scripts": [
"build",
"test",
"lint", // Code formatting
],
}`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual({
name: "my-app",
version: "1.0.0",
dependencies: {
react: "^18.0.0",
typescript: "^5.0.0",
},
scripts: ["build", "test", "lint"],
});
});
test("Bun.JSONC.parse handles nested objects", () => {
const jsonc = `{
"outer": {
// Nested comment
"inner": {
"value": 123,
}
},
}`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual({
outer: {
inner: {
value: 123,
},
},
});
});
test("Bun.JSONC.parse handles boolean and null values", () => {
const jsonc = `{
"enabled": true, // Boolean true
"disabled": false, // Boolean false
"nothing": null, // Null value
}`;
const result = Bun.JSONC.parse(jsonc);
expect(result).toEqual({
enabled: true,
disabled: false,
nothing: null,
});
});
test("Bun.JSONC.parse throws on invalid JSON", () => {
expect(() => {
Bun.JSONC.parse("{ invalid json }");
}).toThrow();
});
test("Bun.JSONC.parse handles empty object", () => {
const result = Bun.JSONC.parse("{}");
expect(result).toEqual({});
});
test("Bun.JSONC.parse handles empty array", () => {
const result = Bun.JSONC.parse("[]");
expect(result).toEqual([]);
});
test("Bun.JSONC.parse throws on deeply nested arrays instead of crashing", () => {
const depth = 25_000;
const deepJson = "[".repeat(depth) + "]".repeat(depth);
expect(() => Bun.JSONC.parse(deepJson)).toThrow(RangeError);
});
test("Bun.JSONC.parse throws on deeply nested objects instead of crashing", () => {
const depth = 25_000;
const deepJson = '{"a":'.repeat(depth) + "1" + "}".repeat(depth);
expect(() => Bun.JSONC.parse(deepJson)).toThrow(RangeError);
});