diff --git a/frontend/e2e/security-headers.spec.ts b/frontend/e2e/security-headers.spec.ts new file mode 100644 index 0000000..7602168 --- /dev/null +++ b/frontend/e2e/security-headers.spec.ts @@ -0,0 +1,61 @@ +/** + * E2E Tests for Security Headers + * Verifies that security headers are properly configured per OWASP 2025 recommendations + * + * References: + * - https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html + * - https://nextjs.org/docs/app/api-reference/config/next-config-js/headers + */ + +import { test, expect } from '@playwright/test'; + +test.describe('Security Headers', () => { + test('should include all required security headers', async ({ request }) => { + const response = await request.get('/'); + const headers = response.headers(); + + // X-Frame-Options: Prevents clickjacking attacks + expect(headers['x-frame-options']).toBe('DENY'); + + // X-Content-Type-Options: Prevents MIME type sniffing + expect(headers['x-content-type-options']).toBe('nosniff'); + + // Referrer-Policy: Controls referrer information leakage + expect(headers['referrer-policy']).toBe('strict-origin-when-cross-origin'); + + // Permissions-Policy: Restricts browser features + expect(headers['permissions-policy']).toContain('camera=()'); + expect(headers['permissions-policy']).toContain('microphone=()'); + expect(headers['permissions-policy']).toContain('geolocation=()'); + + // Content-Security-Policy: Mitigates XSS and injection attacks + const csp = headers['content-security-policy']; + expect(csp).toBeDefined(); + expect(csp).toContain("default-src 'self'"); + expect(csp).toContain("frame-ancestors 'none'"); + expect(csp).toContain("object-src 'none'"); + }); + + test('should NOT include deprecated security headers', async ({ request }) => { + const response = await request.get('/'); + const headers = response.headers(); + + // X-XSS-Protection is deprecated and should not be set + // (Modern browsers have removed support, can cause security issues) + expect(headers['x-xss-protection']).toBeUndefined(); + }); + + test('security headers should be present on all routes', async ({ request }) => { + const routes = ['/', '/en', '/en/login', '/en/register']; + + for (const route of routes) { + const response = await request.get(route); + const headers = response.headers(); + + expect(headers['x-frame-options'], `Missing X-Frame-Options on ${route}`).toBe('DENY'); + expect(headers['x-content-type-options'], `Missing X-Content-Type-Options on ${route}`).toBe( + 'nosniff' + ); + } + }); +}); diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index a7cf0d3..aa8ca70 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -113,6 +113,7 @@ export default defineConfig({ /auth-flows\.spec\.ts/, /auth-oauth\.spec\.ts/, /theme-toggle\.spec\.ts/, + /security-headers\.spec\.ts/, ], use: { ...devices['Desktop Chrome'] }, },