'use strict'; const assert = require('node:assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const test = require('node:test'); const { createLogger } = require('../../src/platform/logger'); const { createAuditLogService, normalizeEntry } = require('../../src/services/audit-log-service'); test('audit center filters command events and exports redacted authenticated data', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-audit-')); try { const logger = createLogger(root, { maxBytes: 64 * 1024, maxFiles: 2, consoleOutput: false }); logger.audit('command', { command: 'recovery:restore', classification: 'destructive', ok: true, requestIp: '192.0.2.4', password: 'must-not-appear' }); logger.audit('command', { command: 'settings:set', classification: 'sensitive', ok: false, error: 'geweigerd' }); logger.info('server ready Authorization: should-never-leak'); const service = createAuditLogService({ logger }); const result = service.query({ level: 'audit', classification: 'destructive', result: 'success', search: 'recovery', limit: 10 }); assert.equal(result.entries.length, 1); assert.equal(result.entries[0].command, 'recovery:restore'); assert.equal(result.entries[0].values[0].password, '[REDACTED]'); assert.equal(result.summary.destructive, 1); const exported = service.exportAudit({ level: 'audit' }).toString('utf8'); assert.match(exported, /recovery:restore/); assert.doesNotMatch(exported, /must-not-appear/); assert.doesNotMatch(fs.readFileSync(logger.file, 'utf8'), /should-never-leak/); assert.equal(service.health().exportAuthenticated, true); logger.info({ oversized: 'z'.repeat(100_000) }); assert.ok(fs.statSync(logger.file).size < 64 * 1024); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); test('logger rotates bounded files and audit center rejects invalid filters', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-audit-rotate-')); try { const logger = createLogger(root, { maxBytes: 64 * 1024, maxFiles: 2, consoleOutput: false }); for (let index = 0; index < 180; index += 1) logger.audit('command', { command: `test:${index}`, classification: 'mutating', ok: true, note: 'x'.repeat(500) }); assert.equal(fs.existsSync(`${logger.file}.1`), true); assert.equal(fs.existsSync(`${logger.file}.3`), false); const service = createAuditLogService({ logger, maxReadBytes: 256 * 1024 }); assert.throws(() => service.query({ classification: 'root-shell' }), /Ongeldige auditclassificatie/); assert.ok(service.query({ limit: 500 }).entries.length > 0); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); test('audit normalizer drops malformed timestamps and bounds untrusted fields', () => { assert.equal(normalizeEntry({ time: 'invalid', level: 'audit', values: [] }), null); const entry = normalizeEntry({ time: '2026-07-16T00:00:00Z', level: 'audit', values: [{ event: 'command', command: `x\n${'y'.repeat(300)}`, classification: 'destructive', ok: false }] }); assert.equal(entry.command.includes('\n'), false); assert.equal(entry.command.length, 160); assert.equal(entry.ok, false); });