/** * Combined Security Enforcement Tests * * Tests that verify multiple security modules working together, * settings persistence, and audit logging integration. * * Pattern: Toggle-On-Test-Toggle-Off * * @see /projects/Charon/docs/plans/current_spec.md - Combined Enforcement Tests */ import { test, expect } from '../fixtures/test'; import { request } from '@playwright/test'; import type { APIRequestContext } from '@playwright/test'; import { STORAGE_STATE } from '../constants'; // CI-specific timeout multiplier: CI environments have higher I/O latency const CI_TIMEOUT_MULTIPLIER = process.env.CI ? 3 : 1; const BASE_PROPAGATION_WAIT = 500; const BASE_RETRY_INTERVAL = 300; const BASE_RETRY_COUNT = 5; import { getSecurityStatus, setSecurityModuleEnabled, captureSecurityState, restoreSecurityState, CapturedSecurityState, SecurityStatus, } from '../utils/security-helpers'; /** * Configure admin whitelist to allow test runner IPs. * CRITICAL: Must be called BEFORE enabling any security modules to prevent 403 blocking. */ async function configureAdminWhitelist(requestContext: APIRequestContext) { // Configure whitelist to allow test runner IPs (localhost, Docker networks) const testWhitelist = '127.0.0.1/32,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8'; const response = await requestContext.patch( `${process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080'}/api/v1/config`, { data: { security: { admin_whitelist: testWhitelist, }, }, } ); if (!response.ok()) { throw new Error(`Failed to configure admin whitelist: ${response.status()}`); } console.log('✅ Admin whitelist configured for test IP ranges'); } test.describe('Combined Security Enforcement', () => { let requestContext: APIRequestContext; let originalState: CapturedSecurityState; test.beforeAll(async () => { requestContext = await request.newContext({ baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080', storageState: STORAGE_STATE, }); // CRITICAL: Configure admin whitelist BEFORE enabling security modules try { await configureAdminWhitelist(requestContext); } catch (error) { console.error('Failed to configure admin whitelist:', error); } // Capture original state try { originalState = await captureSecurityState(requestContext); } catch (error) { console.error('Failed to capture original security state:', error); } }); test.afterAll(async () => { // Restore original state if (originalState) { try { await restoreSecurityState(requestContext, originalState); console.log('✓ Security state restored'); } catch (error) { console.error('Failed to restore security state:', error); // Emergency disable all try { await setSecurityModuleEnabled(requestContext, 'acl', false); await setSecurityModuleEnabled(requestContext, 'waf', false); await setSecurityModuleEnabled(requestContext, 'rateLimit', false); await setSecurityModuleEnabled(requestContext, 'crowdsec', false); await setSecurityModuleEnabled(requestContext, 'cerberus', false); } catch { console.error('Emergency security disable also failed'); } } } await requestContext.dispose(); }); test('should enable all security modules simultaneously', async ({}, testInfo) => { // SKIP: Security module enforcement verified via Cerberus middleware (port 80). // See: backend/integration/cerberus_integration_test.go }); test('should log security events to audit log', async () => { // Make a settings change to trigger audit log entry await setSecurityModuleEnabled(requestContext, 'cerberus', true); await setSecurityModuleEnabled(requestContext, 'acl', true); // Wait a moment for audit log to be written await new Promise((resolve) => setTimeout(resolve, BASE_PROPAGATION_WAIT * CI_TIMEOUT_MULTIPLIER)); // Fetch audit logs const response = await requestContext.get('/api/v1/security/audit-logs'); if (response.ok()) { const logs = await response.json(); expect(Array.isArray(logs) || logs.items !== undefined).toBe(true); // Verify structure (may be empty if audit logging not configured) console.log(`✓ Audit log endpoint accessible, ${Array.isArray(logs) ? logs.length : logs.items?.length || 0} entries`); } else { // Audit logs may require additional configuration console.log(`Audit logs endpoint returned ${response.status()}`); } }); test('should handle rapid module toggle without race conditions', async () => { // Enable Cerberus first await setSecurityModuleEnabled(requestContext, 'cerberus', true); // Rapidly toggle ACL on/off const toggles = 5; for (let i = 0; i < toggles; i++) { await requestContext.post('/api/v1/settings', { data: { key: 'security.acl.enabled', value: i % 2 === 0 ? 'true' : 'false' }, }); } // Final toggle leaves ACL in known state (i=4 sets 'true') // Wait with retry for state to propagate let status = await getSecurityStatus(requestContext); let retries = BASE_RETRY_COUNT * CI_TIMEOUT_MULTIPLIER; while (!status.acl.enabled && retries > 0) { await new Promise((resolve) => setTimeout(resolve, BASE_RETRY_INTERVAL * CI_TIMEOUT_MULTIPLIER)); status = await getSecurityStatus(requestContext); retries--; } // After 5 toggles (0,1,2,3,4), final state is i=4 which sets 'true' expect(status.acl.enabled).toBe(true); console.log('✓ Rapid toggle completed without race conditions'); }); test('should persist settings across API calls', async () => { // Enable a specific configuration await setSecurityModuleEnabled(requestContext, 'cerberus', true); await setSecurityModuleEnabled(requestContext, 'waf', true); await setSecurityModuleEnabled(requestContext, 'acl', false); // Create a new request context to simulate fresh session const freshContext = await request.newContext({ baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080', storageState: STORAGE_STATE, }); try { const status = await getSecurityStatus(freshContext); expect(status.cerberus.enabled).toBe(true); expect(status.waf.enabled).toBe(true); expect(status.acl.enabled).toBe(false); console.log('✓ Settings persisted across API calls'); } finally { await freshContext.dispose(); } }); test('should enforce correct priority when multiple modules enabled', async () => { // Module priority enforcement happens at the proxy layer through Caddy middleware. console.log( '✓ Multiple modules enabled - priority enforcement is at middleware level' ); }); });