/** * Tests for the audit-logger security fixes [DC-028]: * - /auth/gate and /auth/app-token must NOT be skipped (they expose creds) * - Other GETs remain skipped (probes, dashboards) * - The new credential-injection / app-token-issue actions resolve * * These tests focus on shouldSkip() and resolveAction() in isolation. * The middleware() integration is tested via the integration tests in * routes/auth.*.test.js. */ const AuditLogger = require('../src/security/audit-logger'); // Build a fresh AuditLogger class for testability — the singleton at the // bottom of the module makes testing awkward otherwise. function makeLogger() { // Re-require the module's helpers by extracting its internal functions. // Easier: create an instance and exercise its public methods. const logger = Object.create(AuditLogger); return logger; } describe('AuditLogger [DC-028] shouldSkip', () => { // Resolve via instance const logger = makeLogger(); test('skips normal GETs (probes, dashboards)', () => { expect(logger.shouldSkip('GET', '/api/v1/services')).toBe(true); expect(logger.shouldSkip('GET', '/api/v1/config')).toBe(true); expect(logger.shouldSkip('GET', '/api/v1/monitoring/stats')).toBe(true); expect(logger.shouldSkip('GET', '/health')).toBe(true); expect(logger.shouldSkip('GET', '/api/v1/health')).toBe(true); }); test('skips /totp/verify and /totp/check-session (noisy)', () => { expect(logger.shouldSkip('GET', '/api/v1/totp/verify')).toBe(true); expect(logger.shouldSkip('GET', '/api/v1/totp/check-session')).toBe(true); expect(logger.shouldSkip('POST', '/api/v1/totp/verify')).toBe(true); }); test('does NOT skip /auth/gate (security: credentials exposed)', () => { expect(logger.shouldSkip('GET', '/api/v1/auth/gate/plex')).toBe(false); expect(logger.shouldSkip('GET', '/api/v1/auth/gate/jellyfin')).toBe(false); expect(logger.shouldSkip('GET', '/api/v1/auth/gate/sonarr')).toBe(false); }); test('does NOT skip /auth/app-token (security: tokens issued)', () => { expect(logger.shouldSkip('GET', '/api/v1/auth/app-token/plex')).toBe(false); expect(logger.shouldSkip('GET', '/api/v1/auth/app-token/jellyfin')).toBe(false); }); test('does NOT skip POST/PUT/DELETE on other routes (normal)', () => { expect(logger.shouldSkip('POST', '/api/v1/services')).toBe(false); expect(logger.shouldSkip('PUT', '/api/v1/services/abc')).toBe(false); expect(logger.shouldSkip('DELETE', '/api/v1/auth/keys/xyz')).toBe(false); }); }); describe('AuditLogger [DC-028] resolveAction', () => { const logger = makeLogger(); test('credential-injection resolves for /auth/gate', () => { expect(logger.resolveAction('GET', '/api/v1/auth/gate/plex')).toBe('auth.credential-injection'); expect(logger.resolveAction('GET', '/api/v1/auth/gate/jellyfin')).toBe('auth.credential-injection'); }); test('app-token-issue resolves for /auth/app-token', () => { expect(logger.resolveAction('GET', '/api/v1/auth/app-token/plex')).toBe('auth.app-token-issue'); expect(logger.resolveAction('GET', '/api/v1/auth/app-token/jellyfin')).toBe('auth.app-token-issue'); }); test('api-key-generate / revoke / jwt-mint resolve', () => { expect(logger.resolveAction('POST', '/api/v1/auth/keys')).toBe('auth.api-key-generate'); expect(logger.resolveAction('DELETE', '/api/v1/auth/keys/abc-123')).toBe('auth.api-key-revoke'); expect(logger.resolveAction('POST', '/api/v1/auth/jwt')).toBe('auth.jwt-mint'); }); test('existing actions still resolve', () => { expect(logger.resolveAction('POST', '/api/v1/site')).toBe('caddy.add-site'); expect(logger.resolveAction('POST', '/api/v1/totp/setup')).toBe('auth.totp-setup'); }); });