chore: git cache cleanup
This commit is contained in:
167
tests/security-enforcement/zzz-admin-whitelist-blocking.spec.ts
Normal file
167
tests/security-enforcement/zzz-admin-whitelist-blocking.spec.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 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, request, APIRequestContext } from '@playwright/test';
|
||||
import { STORAGE_STATE } from '../constants';
|
||||
|
||||
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';
|
||||
let apiContext: APIRequestContext;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
if (!EMERGENCY_TOKEN) {
|
||||
throw new Error(
|
||||
'CHARON_EMERGENCY_TOKEN required for admin whitelist tests\n' +
|
||||
'Generate with: openssl rand -hex 32'
|
||||
);
|
||||
}
|
||||
|
||||
apiContext = await request.newContext({
|
||||
baseURL: BASE_URL,
|
||||
storageState: STORAGE_STATE,
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (apiContext) {
|
||||
await apiContext.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
test('Test 1: should block non-whitelisted IP when Cerberus enabled', async () => {
|
||||
// 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 apiContext.patch('/api/v1/security/acl', {
|
||||
data: {
|
||||
enabled: false, // Ensure disabled first
|
||||
},
|
||||
});
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
// Set the admin whitelist
|
||||
const configResponse = await apiContext.patch('/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 apiContext.patch('/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 () => {
|
||||
// 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 apiContext.patch('/api/v1/config', {
|
||||
data: {
|
||||
security: {
|
||||
admin_whitelist: testWhitelist,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(response.ok()).toBeTruthy();
|
||||
});
|
||||
|
||||
await test.step('Enable ACL with whitelisted IP', async () => {
|
||||
const response = await apiContext.patch('/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 apiContext.get('/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 apiContext.patch('/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 apiContext.patch('/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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user