fix(security): resolve API key logging vulnerability and enhance import validation

Critical security fix addressing CWE-312/315/359 (Cleartext Storage/Cookie
Storage/Privacy Exposure) where CrowdSec bouncer API keys were logged in cleartext.
Implemented maskAPIKey() utility to show only first 4 and last 4 characters,
protecting sensitive credentials in production logs.

Enhanced CrowdSec configuration import validation with:
- Zip bomb protection via 100x compression ratio limit
- Format validation rejecting zip archives (only tar.gz allowed)
- CrowdSec-specific YAML structure validation
- Rollback mechanism on validation failures

UX improvement: moved CrowdSec API key display from Security Dashboard to
CrowdSec Config page for better logical organization.

Comprehensive E2E test coverage:
- Created 10 test scenarios including valid import, missing files, invalid YAML,
  zip bombs, wrong formats, and corrupted archives
- 87/108 E2E tests passing (81% pass rate, 0 regressions)

Security validation:
- CodeQL: 0 CWE-312/315/359 findings (vulnerability fully resolved)
- Docker Image: 7 HIGH base image CVEs documented (non-blocking, Debian upstream)
- Pre-commit hooks: 13/13 passing (fixed 23 total linting issues)

Backend coverage: 82.2% (+1.1%)
Frontend coverage: 84.19% (+0.3%)
This commit is contained in:
GitHub Actions
2026-02-04 00:12:13 +00:00
parent c9965bb45b
commit 93894c517b
19 changed files with 4971 additions and 1384 deletions
@@ -99,10 +99,7 @@ vi.mock('../../components/LiveLogViewer', () => ({
LiveLogViewer: () => <div data-testid="live-log-viewer">Mocked Live Log Viewer</div>,
}))
// Mock CrowdSecBouncerKeyDisplay
vi.mock('../../components/CrowdSecBouncerKeyDisplay', () => ({
CrowdSecBouncerKeyDisplay: () => <div data-testid="bouncer-key-display">Mocked Bouncer Key Display</div>,
}))
// NOTE: CrowdSecBouncerKeyDisplay mock removed (moved to CrowdSecConfig page)
vi.mock('../../hooks/useSecurity', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../hooks/useSecurity')>()
@@ -404,31 +401,8 @@ describe('Security Page - Functional Tests', () => {
})
})
describe('CrowdSec Bouncer Key Display', () => {
it('should show bouncer key display when Cerberus and CrowdSec are enabled', async () => {
vi.mocked(securityApi.getSecurityStatus).mockResolvedValue(mockSecurityStatusAllEnabled)
vi.mocked(crowdsecApi.statusCrowdsec).mockResolvedValue({ running: true, pid: 1234, lapi_ready: true })
await renderSecurityPage()
await waitFor(() => {
expect(screen.getByTestId('bouncer-key-display')).toBeInTheDocument()
})
})
it('should not show bouncer key display when CrowdSec is disabled', async () => {
vi.mocked(securityApi.getSecurityStatus).mockResolvedValue(mockSecurityStatusCrowdsecDisabled)
vi.mocked(crowdsecApi.statusCrowdsec).mockResolvedValue({ running: false, pid: 0, lapi_ready: false })
await renderSecurityPage()
await waitFor(() => {
expect(screen.getByText(/Cerberus Dashboard/i)).toBeInTheDocument()
})
expect(screen.queryByTestId('bouncer-key-display')).not.toBeInTheDocument()
})
})
// NOTE: CrowdSec Bouncer Key Display moved to CrowdSecConfig page (Sprint 3)
// Tests for bouncer key display are now in CrowdSecConfig tests
describe('Live Log Viewer', () => {
it('should show live log viewer when Cerberus is enabled', async () => {