chore: git cache cleanup
This commit is contained in:
108
tests/security-enforcement/security-headers-enforcement.spec.ts
Normal file
108
tests/security-enforcement/security-headers-enforcement.spec.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Security Headers Enforcement Tests
|
||||
*
|
||||
* Tests that verify security headers are properly set on responses.
|
||||
*
|
||||
* NOTE: Security headers are applied at Caddy layer. These tests verify
|
||||
* the expected headers through the API proxy.
|
||||
*
|
||||
* @see /projects/Charon/docs/plans/current_spec.md - Security Headers Enforcement Tests
|
||||
*/
|
||||
|
||||
import { test, expect } from '../fixtures/test';
|
||||
import { request } from '@playwright/test';
|
||||
import type { APIRequestContext } from '@playwright/test';
|
||||
import { STORAGE_STATE } from '../constants';
|
||||
|
||||
test.describe('Security Headers Enforcement', () => {
|
||||
let requestContext: APIRequestContext;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
requestContext = await request.newContext({
|
||||
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080',
|
||||
storageState: STORAGE_STATE,
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await requestContext.dispose();
|
||||
});
|
||||
|
||||
test('should return X-Content-Type-Options header', async () => {
|
||||
const response = await requestContext.get('/api/v1/health');
|
||||
expect(response.ok()).toBe(true);
|
||||
|
||||
// X-Content-Type-Options should be 'nosniff'
|
||||
const header = response.headers()['x-content-type-options'];
|
||||
if (header) {
|
||||
expect(header).toBe('nosniff');
|
||||
} else {
|
||||
// If backend doesn't set it, Caddy should when configured
|
||||
console.log(
|
||||
'X-Content-Type-Options not set directly (may be set at Caddy layer)'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('should return X-Frame-Options header', async () => {
|
||||
const response = await requestContext.get('/api/v1/health');
|
||||
expect(response.ok()).toBe(true);
|
||||
|
||||
// X-Frame-Options should be 'DENY' or 'SAMEORIGIN'
|
||||
const header = response.headers()['x-frame-options'];
|
||||
if (header) {
|
||||
expect(['DENY', 'SAMEORIGIN', 'deny', 'sameorigin']).toContain(header);
|
||||
} else {
|
||||
// If backend doesn't set it, Caddy should when configured
|
||||
console.log(
|
||||
'X-Frame-Options not set directly (may be set at Caddy layer)'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('should document HSTS behavior on HTTPS', async () => {
|
||||
// HSTS (Strict-Transport-Security) is only set on HTTPS responses
|
||||
// In test environment, we typically use HTTP
|
||||
//
|
||||
// Expected header on HTTPS:
|
||||
// Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
//
|
||||
// This test verifies HSTS is not incorrectly set on HTTP
|
||||
|
||||
const response = await requestContext.get('/api/v1/health');
|
||||
expect(response.ok()).toBe(true);
|
||||
|
||||
const hsts = response.headers()['strict-transport-security'];
|
||||
|
||||
// On HTTP, HSTS should not be present (browsers ignore it anyway)
|
||||
if (process.env.PLAYWRIGHT_BASE_URL?.startsWith('https://')) {
|
||||
expect(hsts).toBeDefined();
|
||||
expect(hsts).toContain('max-age');
|
||||
} else {
|
||||
// HTTP is fine without HSTS in test env
|
||||
console.log('HSTS not present on HTTP (expected behavior)');
|
||||
}
|
||||
});
|
||||
|
||||
test('should verify Content-Security-Policy when configured', async () => {
|
||||
// CSP is optional and configured per-host
|
||||
// This test verifies CSP header handling when present
|
||||
|
||||
const response = await requestContext.get('/');
|
||||
// May be 200 or redirect
|
||||
expect(response.status()).toBeLessThan(500);
|
||||
|
||||
const csp = response.headers()['content-security-policy'];
|
||||
if (csp) {
|
||||
// CSP should contain valid directives
|
||||
expect(
|
||||
csp.includes("default-src") ||
|
||||
csp.includes("script-src") ||
|
||||
csp.includes("style-src")
|
||||
).toBe(true);
|
||||
} else {
|
||||
// CSP is optional - document its behavior when configured
|
||||
console.log('CSP not configured (optional - set per proxy host)');
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user