Skip to content

Commit 650602e

Browse files
fix: enhance XSS protection in sanitizeHttpResponse for deep nesting scenarios
1 parent 1ebac74 commit 650602e

3 files changed

Lines changed: 35 additions & 3 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "treege",
33
"description": "Powerful form generator",
44
"license": "ISC",
5-
"version": "3.0.0-beta.20",
5+
"version": "3.0.0-beta.21",
66
"type": "module",
77
"types": "./dist/main.d.ts",
88
"module": "./dist/main.js",

src/renderer/utils/__tests__/sanitize.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,34 @@ describe("sanitizeHttpResponse", () => {
362362
expect(result).toBeDefined();
363363
});
364364

365+
it("should sanitize strings even when max depth is exceeded (XSS protection)", () => {
366+
// Security test: Create an object nested beyond MAX_DEPTH with XSS payload at bottom
367+
let nested: unknown = '<script>alert("XSS at depth 150")</script>';
368+
for (let i = 0; i < 150; i++) {
369+
nested = { child: nested };
370+
}
371+
372+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
373+
374+
const result = sanitizeHttpResponse(nested);
375+
376+
warnSpy.mockRestore();
377+
378+
// Navigate to max depth level
379+
let current = result as Record<string, unknown>;
380+
for (let i = 0; i < 100; i++) {
381+
current = current.child as Record<string, unknown>;
382+
}
383+
384+
// At level 101, should get either sanitized string or safe placeholder
385+
const deepValue = current.child;
386+
387+
// Must be either empty string (sanitized XSS) or safe placeholder
388+
expect(typeof deepValue === "string").toBe(true);
389+
expect(deepValue).not.toContain("<script>");
390+
expect(deepValue).not.toContain("alert");
391+
});
392+
365393
it("should handle deeply nested arrays", () => {
366394
// Create deeply nested arrays
367395
let nested: unknown[] = ["value<script>xss</script>"];

src/renderer/utils/sanitize.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,12 @@ export const sanitizeHttpResponse = (
143143
): unknown => {
144144
// Prevent DoS via deep nesting
145145
if (depth > MAX_DEPTH) {
146-
console.warn(`sanitizeHttpResponse: Maximum depth (${MAX_DEPTH}) exceeded. Returning data as-is to prevent stack overflow.`);
147-
return data;
146+
console.warn(`sanitizeHttpResponse: Maximum depth (${MAX_DEPTH}) exceeded.`);
147+
// Still sanitize strings to prevent XSS even at max depth
148+
if (typeof data === "string") {
149+
return sanitize(data, options);
150+
}
151+
return "[Max Depth Exceeded]";
148152
}
149153

150154
if (data === null || data === undefined) {

0 commit comments

Comments
 (0)