import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; import helmet from '@fastify/helmet'; describe('Server Security Configuration', () => { describe('Content Security Policy', () => { let app: FastifyInstance; beforeEach(async () => { app = Fastify(); }); afterEach(async () => { if (app) { await app.close(); } }); it('should include CSP headers in response', async () => { // Register helmet with the same config as the server await app.register(helmet, { contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:'], fontSrc: ["'self'"], objectSrc: ["'none'"], frameAncestors: ["'none'"], formAction: ["'self'"], baseUri: ["'self'"], }, }, }); app.get('/test', async () => ({ status: 'ok' })); await app.ready(); const response = await app.inject({ method: 'GET', url: '/test', }); // Check that CSP header is present const cspHeader = response.headers['content-security-policy']; expect(cspHeader).toBeDefined(); // Verify key CSP directives expect(cspHeader).toContain("default-src 'self'"); expect(cspHeader).toContain("script-src 'self'"); expect(cspHeader).toContain("object-src 'none'"); expect(cspHeader).toContain("frame-ancestors 'none'"); }); it('should include X-Frame-Options header', async () => { await app.register(helmet); app.get('/test', async () => ({ status: 'ok' })); await app.ready(); const response = await app.inject({ method: 'GET', url: '/test', }); // Helmet sets X-Frame-Options by default expect(response.headers['x-frame-options']).toBeDefined(); }); it('should include X-Content-Type-Options header', async () => { await app.register(helmet); app.get('/test', async () => ({ status: 'ok' })); await app.ready(); const response = await app.inject({ method: 'GET', url: '/test', }); expect(response.headers['x-content-type-options']).toBe('nosniff'); }); }); describe('HTML Escape Function', () => { // Test the escapeHtml function that prevents XSS in templates const escapeHtml = (unsafe: string): string => { return unsafe .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; it('should escape < and > characters', () => { const input = ''; const escaped = escapeHtml(input); expect(escaped).toBe('<script>alert("xss")</script>'); expect(escaped).not.toContain('<'); expect(escaped).not.toContain('>'); }); it('should escape ampersand', () => { const input = 'Tom & Jerry'; const escaped = escapeHtml(input); expect(escaped).toBe('Tom & Jerry'); }); it('should escape double quotes', () => { const input = 'He said "hello"'; const escaped = escapeHtml(input); expect(escaped).toBe('He said "hello"'); }); it('should escape single quotes', () => { const input = "It's a test"; const escaped = escapeHtml(input); expect(escaped).toBe('It's a test'); }); it('should handle multiple special characters', () => { const input = '