/** * Security Page - QA Security Audit Tests * * Tests edge cases, input validation, error states, and security concerns * for the Cerberus Dashboard implementation. */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { BrowserRouter } from 'react-router-dom' import Security from '../Security' import * as securityApi from '../../api/security' import * as crowdsecApi from '../../api/crowdsec' import * as settingsApi from '../../api/settings' import { toast } from '../../utils/toast' const mockSecurityStatus = { cerberus: { enabled: true }, crowdsec: { mode: 'local' as const, api_url: 'http://localhost', enabled: true }, waf: { mode: 'enabled' as const, enabled: true }, rate_limit: { enabled: true }, acl: { enabled: true }, } vi.mock('../../api/security') vi.mock('../../api/crowdsec') vi.mock('../../api/settings') vi.mock('../../utils/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn(), }, })) vi.mock('../../hooks/useSecurity', async (importOriginal) => { const actual = await importOriginal() return { ...actual, useSecurityConfig: vi.fn(() => ({ data: { config: { admin_whitelist: '' } } })), useUpdateSecurityConfig: vi.fn(() => ({ mutate: vi.fn(), isPending: false })), useGenerateBreakGlassToken: vi.fn(() => ({ mutate: vi.fn(), isPending: false })), useRuleSets: vi.fn(() => ({ data: { rulesets: [] } })), } }) describe('Security Page - QA Security Audit', () => { let queryClient: QueryClient beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }) vi.clearAllMocks() vi.mocked(securityApi.getSecurityStatus).mockResolvedValue(mockSecurityStatus) vi.mocked(crowdsecApi.statusCrowdsec).mockResolvedValue({ running: false, pid: 0, lapi_ready: false }) vi.mocked(settingsApi.updateSetting).mockResolvedValue() vi.mocked(crowdsecApi.exportCrowdsecConfig).mockResolvedValue(new Blob()) vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {}) vi.spyOn(window, 'prompt').mockReturnValue('crowdsec-export.tar.gz') }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const renderSecurityPage = async () => { await act(async () => { render(, { wrapper }) }) } describe('Input Validation', () => { it('React escapes XSS in rendered text - validation check', async () => { // Note: React automatically escapes text content, so XSS in input values // won't execute. This test verifies that property. vi.mocked(securityApi.getSecurityStatus).mockResolvedValue(mockSecurityStatus) await renderSecurityPage() await waitFor(() => screen.getByText(/Cerberus Dashboard/i)) // DOM should not contain any actual script elements from user input expect(document.querySelectorAll('script[src*="alert"]').length).toBe(0) // Verify React is escaping properly - any text rendered should be text, not HTML expect(screen.queryByText('