Files
Charon/tests/security/waf-config.spec.ts
akanealw eec8c28fb3
Some checks are pending
Go Benchmark / Performance Regression Check (push) Waiting to run
Cerberus Integration / Cerberus Security Stack Integration (push) Waiting to run
Upload Coverage to Codecov / Backend Codecov Upload (push) Waiting to run
Upload Coverage to Codecov / Frontend Codecov Upload (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (go) (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Waiting to run
CrowdSec Integration / CrowdSec Bouncer Integration (push) Waiting to run
Docker Build, Publish & Test / build-and-push (push) Waiting to run
Docker Build, Publish & Test / Security Scan PR Image (push) Blocked by required conditions
Quality Checks / Auth Route Protection Contract (push) Waiting to run
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Waiting to run
Quality Checks / Backend (Go) (push) Waiting to run
Quality Checks / Frontend (React) (push) Waiting to run
Rate Limit integration / Rate Limiting Integration (push) Waiting to run
Security Scan (PR) / Trivy Binary Scan (push) Waiting to run
Supply Chain Verification (PR) / Verify Supply Chain (push) Waiting to run
WAF integration / Coraza WAF Integration (push) Waiting to run
changed perms
2026-04-22 18:19:14 +00:00

236 lines
8.1 KiB
TypeScript
Executable File

/**
* WAF (Coraza) Configuration E2E Tests
*
* Tests the Web Application Firewall configuration:
* - Page loading and status display
* - WAF mode toggle (blocking/detection)
* - Ruleset management
* - Rule group configuration
* - Whitelist/exclusions
*
* @see /projects/Charon/docs/plans/current_spec.md
*/
import { test, expect, loginUser } from '../fixtures/auth-fixtures';
import { waitForLoadingComplete, waitForToast } from '../utils/wait-helpers';
import { clickSwitch } from '../utils/ui-helpers';
test.describe('WAF Configuration @security', () => {
test.beforeEach(async ({ page, adminUser }) => {
await loginUser(page, adminUser);
await waitForLoadingComplete(page);
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
test.describe('Page Loading', () => {
test('should display WAF configuration page', async ({ page }) => {
// Page should load with WAF heading or content
const heading = page.getByRole('heading', { name: /waf|firewall|coraza/i });
const headingVisible = await heading.isVisible().catch(() => false);
if (!headingVisible) {
// Might have different page structure
const wafContent = page.getByText(/web application firewall|waf|coraza/i).first();
await expect(wafContent).toBeVisible();
} else {
await expect(heading).toBeVisible();
}
});
test('should display WAF status indicator', async ({ page }) => {
const statusBadge = page.locator('[class*="badge"]').filter({
hasText: /enabled|disabled|blocking|detection|active/i
});
const statusVisible = await statusBadge.first().isVisible().catch(() => false);
if (statusVisible) {
await expect(statusBadge.first()).toBeVisible();
}
});
});
test.describe('WAF Mode Toggle', () => {
test('should display current WAF mode', async ({ page }) => {
const modeIndicator = page.getByText(/blocking|detection|mode/i).first();
const modeVisible = await modeIndicator.isVisible().catch(() => false);
if (modeVisible) {
await expect(modeIndicator).toBeVisible();
}
});
test('should have mode toggle switch or selector', async ({ page }) => {
const modeToggle = page.getByTestId('waf-mode-toggle').or(
page.locator('button, [role="switch"]').filter({
hasText: /blocking|detection/i
})
);
const toggleVisible = await modeToggle.isVisible().catch(() => false);
if (toggleVisible) {
await expect(modeToggle).toBeEnabled();
}
});
test('should toggle between blocking and detection mode', async ({ page }) => {
const modeSwitch = page.locator('[role="switch"]').first();
const switchVisible = await modeSwitch.isVisible().catch(() => false);
if (switchVisible) {
await test.step('Click mode switch', async () => {
await clickSwitch(modeSwitch);
});
await test.step('Revert mode switch', async () => {
await clickSwitch(modeSwitch);
});
}
});
});
test.describe('Ruleset Management', () => {
test('should display available rulesets', async ({ page }) => {
const rulesetSection = page.getByText(/ruleset|rules|owasp|crs/i).first();
await expect(rulesetSection).toBeVisible();
});
test('should show rule groups with toggle controls', async ({ page }) => {
// Each rule group should be toggleable
const ruleGroupToggles = page.locator('[role="switch"], input[type="checkbox"]').filter({
has: page.locator('text=/sql|xss|rce|lfi|rfi|scanner/i')
});
// Count available toggles
const count = await ruleGroupToggles.count().catch(() => 0);
expect(count >= 0).toBeTruthy();
});
test('should allow enabling/disabling rule groups', async ({ page }) => {
const ruleToggle = page.locator('[role="switch"]').first();
const toggleVisible = await ruleToggle.isVisible().catch(() => false);
if (toggleVisible) {
// Record initial state
const wasPressed = await ruleToggle.getAttribute('aria-pressed') === 'true' ||
await ruleToggle.getAttribute('aria-checked') === 'true';
await test.step('Toggle rule group', async () => {
await ruleToggle.click();
await page.waitForTimeout(500);
});
await test.step('Restore original state', async () => {
await ruleToggle.click();
await page.waitForTimeout(500);
});
}
});
});
test.describe('Anomaly Threshold', () => {
test('should display anomaly threshold setting', async ({ page }) => {
const thresholdSection = page.getByText(/threshold|score|anomaly/i).first();
const thresholdVisible = await thresholdSection.isVisible().catch(() => false);
if (thresholdVisible) {
await expect(thresholdSection).toBeVisible();
}
});
test('should have threshold input control', async ({ page }) => {
const thresholdInput = page.locator('input[type="number"], input[type="range"]').filter({
has: page.locator('text=/threshold|score/i')
}).first();
const inputVisible = await thresholdInput.isVisible().catch(() => false);
// Threshold control might not be visible on all pages
expect(inputVisible !== undefined).toBeTruthy();
});
});
test.describe('Whitelist/Exclusions', () => {
test('should display whitelist section', async ({ page }) => {
const whitelistSection = page.getByText(/whitelist|exclusion|exception|ignore/i).first();
const whitelistVisible = await whitelistSection.isVisible().catch(() => false);
if (whitelistVisible) {
await expect(whitelistSection).toBeVisible();
}
});
test('should have ability to add whitelist entries', async ({ page }) => {
const addButton = page.getByRole('button', { name: /add.*whitelist|add.*exclusion|add.*exception/i });
const addVisible = await addButton.isVisible().catch(() => false);
if (addVisible) {
await expect(addButton).toBeEnabled();
}
});
});
test.describe('Save and Apply', () => {
test('should have save button', async ({ page }) => {
const saveButton = page.getByRole('button', { name: /save|apply|update/i });
const saveVisible = await saveButton.isVisible().catch(() => false);
if (saveVisible) {
await expect(saveButton).toBeVisible();
}
});
test('should show confirmation on save', async ({ page }) => {
const saveButton = page.getByRole('button', { name: /save|apply/i });
const saveVisible = await saveButton.isVisible().catch(() => false);
if (saveVisible) {
await saveButton.click();
// Should show either confirmation dialog or success toast
const dialog = page.getByRole('dialog');
const dialogVisible = await dialog.isVisible().catch(() => false);
if (dialogVisible) {
const cancelButton = page.getByRole('button', { name: /cancel|close/i });
await cancelButton.click();
}
}
});
});
test.describe('Navigation', () => {
test('should navigate back to security dashboard', async ({ page }) => {
const backLink = page.getByRole('link', { name: /security|back/i });
const backVisible = await backLink.isVisible().catch(() => false);
if (backVisible) {
await backLink.click();
await waitForLoadingComplete(page);
await expect(page).toHaveURL(/\/security(?!\/waf)/);
}
});
});
test.describe('Accessibility', () => {
test('should have accessible controls', async ({ page }) => {
const switches = page.locator('[role="switch"]');
const count = await switches.count();
for (let i = 0; i < Math.min(count, 5); i++) {
const switchEl = switches.nth(i);
const visible = await switchEl.isVisible();
if (visible) {
// Each switch should have accessible name
const name = await switchEl.getAttribute('aria-label') ||
await switchEl.getAttribute('aria-labelledby');
// Some form of accessible name should exist
}
}
});
});
});