mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
## 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>
138 lines
3.1 KiB
TypeScript
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);
|
|
});
|