63 lines
2.4 KiB
TypeScript
63 lines
2.4 KiB
TypeScript
/**
|
|
* 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'");
|
|
expect(csp).toContain("worker-src 'self' blob:"); // Required for MSW
|
|
});
|
|
|
|
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'
|
|
);
|
|
}
|
|
});
|
|
});
|