import { handleJsonParseError } from './json'; import type { Request, Response, NextFunction } from 'express'; describe('handleJsonParseError', () => { let req: Partial; let res: Partial; let next: NextFunction; let jsonSpy: jest.Mock; let statusSpy: jest.Mock; beforeEach(() => { req = { path: '/api/test', method: 'POST', ip: '127.0.0.1', }; jsonSpy = jest.fn(); statusSpy = jest.fn().mockReturnValue({ json: jsonSpy }); res = { status: statusSpy, json: jsonSpy, }; next = jest.fn(); }); describe('JSON parse errors', () => { it('should handle JSON SyntaxError with 400 status', () => { const err = new SyntaxError('Unexpected token < in JSON at position 0') as SyntaxError & { status?: number; body?: unknown; }; err.status = 400; err.body = {}; handleJsonParseError(err, req as Request, res as Response, next); expect(statusSpy).toHaveBeenCalledWith(400); expect(jsonSpy).toHaveBeenCalledWith({ error: 'Invalid JSON format', message: 'The request body contains malformed JSON', }); expect(next).not.toHaveBeenCalled(); }); it('should not reflect user input in error message', () => { const maliciousInput = ''; const err = new SyntaxError( `Unexpected token < in JSON at position 0: ${maliciousInput}`, ) as SyntaxError & { status?: number; body?: unknown; }; err.status = 400; err.body = maliciousInput; handleJsonParseError(err, req as Request, res as Response, next); expect(statusSpy).toHaveBeenCalledWith(400); const errorResponse = jsonSpy.mock.calls[0][0]; expect(errorResponse.message).not.toContain(maliciousInput); expect(errorResponse.message).toBe('The request body contains malformed JSON'); expect(next).not.toHaveBeenCalled(); }); it('should handle JSON parse error with HTML tags in body', () => { const err = new SyntaxError('Invalid JSON') as SyntaxError & { status?: number; body?: unknown; }; err.status = 400; err.body = '

XSS

'; handleJsonParseError(err, req as Request, res as Response, next); expect(statusSpy).toHaveBeenCalledWith(400); const errorResponse = jsonSpy.mock.calls[0][0]; expect(errorResponse.message).not.toContain(''); expect(errorResponse.message).not.toContain('', '">', ]; testCases.forEach((errorMsg) => { const err = new SyntaxError(errorMsg) as SyntaxError & { status?: number; body?: unknown; }; err.status = 400; err.body = errorMsg; jsonSpy.mockClear(); statusSpy.mockClear(); (next as jest.Mock).mockClear(); handleJsonParseError(err, req as Request, res as Response, next); const errorResponse = jsonSpy.mock.calls[0][0]; // Verify the generic message is always returned, not the user input expect(errorResponse.message).toBe('The request body contains malformed JSON'); expect(errorResponse.error).toBe('Invalid JSON format'); }); }); }); });