Files
Charon/tests/security/crowdsec-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

302 lines
11 KiB
TypeScript
Executable File

/**
* CrowdSec Configuration E2E Tests
*
* Tests the CrowdSec configuration page functionality including:
* - Page loading and status display
* - Preset management (view, apply, preview)
* - Configuration file management
* - Import/Export functionality
* - Console enrollment (if enabled)
*
* @see /projects/Charon/docs/plans/current_spec.md
*/
import { test, expect, loginUser } from '../fixtures/auth-fixtures';
import { waitForLoadingComplete, waitForToast } from '../utils/wait-helpers';
test.describe('CrowdSec Configuration @security', () => {
test.beforeEach(async ({ page, adminUser }) => {
await loginUser(page, adminUser);
await waitForLoadingComplete(page);
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
test.describe('Page Loading', () => {
test('should display CrowdSec configuration page', async ({ page }) => {
// The page should load without errors
await expect(page.getByRole('heading', { name: /crowdsec/i }).first()).toBeVisible();
});
test('should show navigation back to security dashboard', async ({ page }) => {
// Should have breadcrumb, back link, or navigation element
const backLink = page.getByRole('link', { name: /security|back/i });
const backLinkVisible = await backLink.isVisible().catch(() => false);
if (backLinkVisible) {
await expect(backLink).toBeVisible();
} else {
// Check for navigation button instead
const backButton = page.getByRole('button', { name: /back/i });
const buttonVisible = await backButton.isVisible().catch(() => false);
if (!buttonVisible) {
// Check for breadcrumbs or navigation in header
const breadcrumb = page.locator('nav, [class*="breadcrumb"], [aria-label*="breadcrumb"]');
const breadcrumbVisible = await breadcrumb.isVisible().catch(() => false);
// At minimum, the page should be loaded
if (!breadcrumbVisible) {
await expect(page).toHaveURL(/crowdsec/);
}
}
}
});
test('should display presets section', async ({ page }) => {
// Look for presets, packages, scenarios, or collections section
// This feature may not be fully implemented
const presetsSection = page.getByText(/packages|presets|scenarios|collections|bouncers/i).first();
const presetsVisible = await presetsSection.isVisible().catch(() => false);
if (presetsVisible) {
await expect(presetsSection).toBeVisible();
} else {
test.info().annotations.push({
type: 'info',
description: 'Presets section not visible - feature may not be implemented'
});
}
});
});
test.describe('Preset Management', () => {
// Preset management may not be fully implemented
test('should display list of available presets', async ({ page }) => {
await test.step('Verify presets are listed', async () => {
// Wait for presets to load
await page.waitForResponse(resp =>
resp.url().includes('/presets') || resp.url().includes('/crowdsec') || resp.url().includes('/hub'),
{ timeout: 10000 }
).catch(() => {
// If no API call, presets might be loaded statically or not implemented
});
// Should show preset cards or list items
const presetElements = page.locator('[class*="card"], [class*="preset"], button').filter({
hasText: /apply|install|owasp|basic|advanced|paranoid/i
});
const count = await presetElements.count();
if (count === 0) {
// Presets might not be implemented - check for config file management instead
const configSection = page.getByText(/configuration|file|config/i).first();
const configVisible = await configSection.isVisible().catch(() => false);
if (!configVisible) {
test.info().annotations.push({
type: 'info',
description: 'No presets displayed - feature may not be implemented'
});
}
}
});
});
test('should allow searching presets', async ({ page }) => {
const searchInput = page.getByPlaceholder(/search/i);
const searchVisible = await searchInput.isVisible().catch(() => false);
if (searchVisible) {
await test.step('Search for a preset', async () => {
await searchInput.fill('basic');
// Results should be filtered
await page.waitForTimeout(500); // Debounce
});
}
});
test('should show preset preview when selected', async ({ page }) => {
// Find and click on a preset to preview
const presetButton = page.locator('button').filter({ hasText: /preview|view|select/i }).first();
const buttonVisible = await presetButton.isVisible().catch(() => false);
if (buttonVisible) {
await presetButton.click();
// Should show preview content
await page.waitForTimeout(500);
}
});
test('should apply preset with confirmation', async ({ page }) => {
// Find apply button
const applyButton = page.locator('button').filter({ hasText: /apply/i }).first();
const buttonVisible = await applyButton.isVisible().catch(() => false);
if (buttonVisible) {
await test.step('Click apply button', async () => {
await applyButton.click();
});
await test.step('Handle confirmation or result', async () => {
// Either a confirmation dialog or success toast should appear
const confirmDialog = page.getByRole('dialog');
const dialogVisible = await confirmDialog.isVisible().catch(() => false);
if (dialogVisible) {
// Cancel to not make permanent changes
const cancelButton = page.getByRole('button', { name: /cancel/i });
await cancelButton.click();
}
});
}
});
});
test.describe('Configuration Files', () => {
test('should display configuration file list', async ({ page }) => {
// Look for file selector or list
const fileSelector = page.locator('select, [role="listbox"]').filter({
hasNotText: /sort|filter/i
}).first();
const filesVisible = await fileSelector.isVisible().catch(() => false);
if (filesVisible) {
await expect(fileSelector).toBeVisible();
}
});
test('should show file content when selected', async ({ page }) => {
// Find file selector
const fileSelector = page.locator('select').first();
const selectorVisible = await fileSelector.isVisible().catch(() => false);
if (selectorVisible) {
// Select a file - content area should appear
const contentArea = page.locator('textarea, pre, [class*="editor"]');
const contentVisible = await contentArea.isVisible().catch(() => false);
// Content area may or may not be visible depending on selection
expect(contentVisible !== undefined).toBeTruthy();
}
});
});
test.describe('Import/Export', () => {
test('should have export functionality', async ({ page }) => {
const exportButton = page.getByRole('button', { name: /export/i });
const exportVisible = await exportButton.isVisible().catch(() => false);
if (exportVisible) {
await expect(exportButton).toBeEnabled();
}
});
test('should have import functionality', async ({ page }) => {
// Look for import button or file input
const importButton = page.getByRole('button', { name: /import/i });
const importInput = page.locator('input[type="file"]');
const importVisible = await importButton.isVisible().catch(() => false);
const inputVisible = await importInput.isVisible().catch(() => false);
// Import functionality may not be implemented
if (importVisible || inputVisible) {
await expect(importButton.or(importInput)).toBeVisible();
} else {
test.info().annotations.push({
type: 'info',
description: 'Import functionality not visible - feature may not be implemented'
});
}
});
});
test.describe('Console Enrollment', () => {
test('should display console enrollment section if feature enabled', async ({ page }) => {
// Console enrollment is a feature-flagged component
const enrollmentSection = page.getByTestId('console-section').or(
page.locator('[class*="card"]').filter({ hasText: /console.*enrollment|enroll/i })
);
const enrollmentVisible = await enrollmentSection.isVisible().catch(() => false);
if (enrollmentVisible) {
await test.step('Verify enrollment form elements', async () => {
// Should have token input
const tokenInput = page.getByTestId('crowdsec-token-input').or(
page.getByPlaceholder(/token|key/i)
);
await expect(tokenInput).toBeVisible();
});
} else {
// Feature might be disabled - that's OK
test.info().annotations.push({
type: 'info',
description: 'Console enrollment feature not enabled'
});
}
});
test('should show enrollment status when enrolled', async ({ page }) => {
const statusText = page.getByTestId('console-token-state').or(
page.locator('text=/enrolled|pending|not enrolled/i')
);
const statusVisible = await statusText.isVisible().catch(() => false);
if (statusVisible) {
// Verify status is displayed
await expect(statusText).toBeVisible();
}
});
});
test.describe('Status Indicators', () => {
test('should display CrowdSec running status', async ({ page }) => {
// Look for status indicators
const statusBadge = page.locator('[class*="badge"]').filter({
hasText: /running|stopped|enabled|disabled/i
});
const statusVisible = await statusBadge.first().isVisible().catch(() => false);
if (statusVisible) {
await expect(statusBadge.first()).toBeVisible();
}
});
test('should display LAPI status', async ({ page }) => {
// LAPI ready status might be displayed
const lapiStatus = page.getByText(/lapi.*ready|lapi.*status/i);
const lapiVisible = await lapiStatus.isVisible().catch(() => false);
// LAPI status might not always be visible
expect(lapiVisible !== undefined).toBeTruthy();
});
});
test.describe('Accessibility', () => {
test('should have accessible form controls', async ({ page }) => {
// Check that inputs have associated labels
const inputs = page.locator('input:not([type="hidden"])');
const count = await inputs.count();
for (let i = 0; i < Math.min(count, 5); i++) {
const input = inputs.nth(i);
const visible = await input.isVisible();
if (visible) {
// Input should have some form of label (explicit, aria-label, or placeholder)
const hasLabel = await input.getAttribute('aria-label') ||
await input.getAttribute('placeholder') ||
await input.getAttribute('id');
expect(hasLabel).toBeTruthy();
}
}
});
});
});