Files
Charon/tests/security-enforcement/zzz-admin-whitelist-blocking.spec.ts
GitHub Actions 11e575d6cc fix: stabilize e2e test suite and auth configuration
- Standardized E2E base URL to 127.0.0.1 to resolve cookie domain 401 errors
- Updated playwright config to strictly exclude security tests from main shards
- Refactored waitForModal helper to prevent strict mode violations on complex modals
- Fixed leak of crowdsec diagnostics tests into standard chromium project
2026-02-06 07:43:26 +00:00

157 lines
5.4 KiB
TypeScript

/**
* Admin Whitelist IP Blocking Enforcement Tests
*
* CRITICAL: This test MUST run LAST in the security-enforcement suite.
* Uses 'zzz-' prefix to ensure alphabetical ordering places it at the end.
*
* Tests validate that Cerberus admin whitelist correctly blocks non-whitelisted IPs
* and allows whitelisted IPs or emergency tokens.
*
* Recovery: Uses emergency reset in afterAll to unblock test IP.
*/
import { test, expect } from '@playwright/test';
test.describe.serial('Admin Whitelist IP Blocking (RUN LAST)', () => {
const EMERGENCY_TOKEN = process.env.CHARON_EMERGENCY_TOKEN;
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080';
test.beforeAll(() => {
if (!EMERGENCY_TOKEN) {
throw new Error(
'CHARON_EMERGENCY_TOKEN required for admin whitelist tests\n' +
'Generate with: openssl rand -hex 32'
);
}
});
test.afterAll(async ({ request }) => {
// CRITICAL: Emergency reset to unblock test IP
console.log('🔧 Emergency reset - cleaning up admin whitelist test');
try {
const response = await request.post('http://localhost:2020/emergency/security-reset', {
headers: {
'Authorization': 'Basic ' + Buffer.from('admin:changeme').toString('base64'),
'X-Emergency-Token': EMERGENCY_TOKEN,
'Content-Type': 'application/json',
},
data: { reason: 'E2E test cleanup - admin whitelist blocking test' },
});
if (response.ok()) {
console.log('✅ Emergency reset completed - test IP unblocked');
} else {
console.error(`❌ Emergency reset failed: ${response.status()}`);
}
} catch (error) {
console.error('Emergency reset error:', error);
}
});
test('Test 1: should block non-whitelisted IP when Cerberus enabled', async ({ request }) => {
// Use a fake whitelist IP that will never match the test runner
const fakeWhitelist = '192.0.2.1/32'; // RFC 5737 TEST-NET-1 (documentation only)
await test.step('Configure admin whitelist with non-matching IP', async () => {
const response = await request.patch(`${BASE_URL}/api/v1/security/acl`, {
data: {
enabled: false, // Ensure disabled first
},
});
expect(response.ok()).toBeTruthy();
// Set the admin whitelist
const configResponse = await request.patch(`${BASE_URL}/api/v1/config`, {
data: {
security: {
admin_whitelist: fakeWhitelist,
},
},
});
expect(configResponse.ok()).toBeTruthy();
});
await test.step('Enable ACL - expect 403 because IP not in whitelist', async () => {
const response = await request.patch(`${BASE_URL}/api/v1/security/acl`, {
data: { enabled: true },
});
// Should be blocked because our IP is not in the admin_whitelist
expect(response.status()).toBe(403);
const body = await response.json().catch(() => ({}));
expect(body.error || '').toMatch(/whitelist|forbidden|access/i);
});
});
test('Test 2: should allow whitelisted IP to enable Cerberus', async ({ request }) => {
// Use localhost/Docker network IP that will match test runner
// In Docker compose, Playwright runs from host connecting to localhost:8080
const testWhitelist = '127.0.0.1/32,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8';
await test.step('Configure admin whitelist with test IP ranges', async () => {
const response = await request.patch(`${BASE_URL}/api/v1/config`, {
data: {
security: {
admin_whitelist: testWhitelist,
},
},
});
expect(response.ok()).toBeTruthy();
});
await test.step('Enable ACL with whitelisted IP', async () => {
const response = await request.patch(`${BASE_URL}/api/v1/security/acl`, {
data: { enabled: true },
});
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.enabled).toBe(true);
});
await test.step('Verify ACL is enforcing', async () => {
const response = await request.get(`${BASE_URL}/api/v1/security/status`);
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.acl?.enabled).toBe(true);
});
});
test('Test 3: should allow emergency token to bypass admin whitelist', async ({ request }) => {
await test.step('Configure admin whitelist with non-matching IP', async () => {
// First disable ACL so we can change config
await request.post('http://localhost:2020/emergency/security-reset', {
headers: {
'Authorization': 'Basic ' + Buffer.from('admin:changeme').toString('base64'),
'X-Emergency-Token': EMERGENCY_TOKEN,
},
data: { reason: 'Test setup - reset for emergency token test' },
});
const response = await request.patch(`${BASE_URL}/api/v1/config`, {
data: {
security: {
admin_whitelist: '192.0.2.1/32', // Fake IP
},
},
});
expect(response.ok()).toBeTruthy();
});
await test.step('Enable ACL using emergency token despite IP mismatch', async () => {
const response = await request.patch(`${BASE_URL}/api/v1/security/acl`, {
data: { enabled: true },
headers: {
'X-Emergency-Token': EMERGENCY_TOKEN,
},
});
// Should succeed with valid emergency token even though IP not in whitelist
expect(response.ok()).toBeTruthy();
});
});
});