149 lines
5.3 KiB
JavaScript
149 lines
5.3 KiB
JavaScript
import { describe, it, beforeEach } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { finding, scannerResult, envelope, resetCounter } from '../../scanners/lib/output.mjs';
|
|
|
|
describe('resetCounter', () => {
|
|
it('resets finding ID counter', () => {
|
|
resetCounter();
|
|
const f1 = finding({ scanner: 'TST', severity: 'info', title: 'a', description: 'b' });
|
|
assert.strictEqual(f1.id, 'CA-TST-001');
|
|
resetCounter();
|
|
const f2 = finding({ scanner: 'TST', severity: 'info', title: 'c', description: 'd' });
|
|
assert.strictEqual(f2.id, 'CA-TST-001');
|
|
});
|
|
});
|
|
|
|
describe('finding', () => {
|
|
beforeEach(() => { resetCounter(); });
|
|
|
|
it('generates correct ID format', () => {
|
|
const f = finding({ scanner: 'CML', severity: 'high', title: 't', description: 'd' });
|
|
assert.match(f.id, /^CA-CML-\d{3}$/);
|
|
});
|
|
|
|
it('auto-increments IDs', () => {
|
|
const f1 = finding({ scanner: 'CML', severity: 'info', title: 'a', description: 'b' });
|
|
const f2 = finding({ scanner: 'CML', severity: 'info', title: 'c', description: 'd' });
|
|
assert.strictEqual(f1.id, 'CA-CML-001');
|
|
assert.strictEqual(f2.id, 'CA-CML-002');
|
|
});
|
|
|
|
it('includes all required fields', () => {
|
|
const f = finding({
|
|
scanner: 'SET',
|
|
severity: 'critical',
|
|
title: 'Test',
|
|
description: 'Desc',
|
|
file: '/foo.json',
|
|
line: 10,
|
|
evidence: 'x=1',
|
|
category: 'Structure',
|
|
recommendation: 'Fix it',
|
|
autoFixable: true,
|
|
});
|
|
assert.strictEqual(f.scanner, 'SET');
|
|
assert.strictEqual(f.severity, 'critical');
|
|
assert.strictEqual(f.title, 'Test');
|
|
assert.strictEqual(f.description, 'Desc');
|
|
assert.strictEqual(f.file, '/foo.json');
|
|
assert.strictEqual(f.line, 10);
|
|
assert.strictEqual(f.evidence, 'x=1');
|
|
assert.strictEqual(f.category, 'Structure');
|
|
assert.strictEqual(f.recommendation, 'Fix it');
|
|
assert.strictEqual(f.autoFixable, true);
|
|
});
|
|
|
|
it('defaults nullable fields to null', () => {
|
|
const f = finding({ scanner: 'TST', severity: 'info', title: 't', description: 'd' });
|
|
assert.strictEqual(f.file, null);
|
|
assert.strictEqual(f.line, null);
|
|
assert.strictEqual(f.evidence, null);
|
|
assert.strictEqual(f.category, null);
|
|
assert.strictEqual(f.recommendation, null);
|
|
assert.strictEqual(f.autoFixable, false);
|
|
});
|
|
});
|
|
|
|
describe('scannerResult', () => {
|
|
beforeEach(() => { resetCounter(); });
|
|
|
|
it('counts severity correctly', () => {
|
|
const findings = [
|
|
finding({ scanner: 'TST', severity: 'critical', title: 'a', description: 'b' }),
|
|
finding({ scanner: 'TST', severity: 'high', title: 'c', description: 'd' }),
|
|
finding({ scanner: 'TST', severity: 'high', title: 'e', description: 'f' }),
|
|
finding({ scanner: 'TST', severity: 'info', title: 'g', description: 'h' }),
|
|
];
|
|
const r = scannerResult('TST', 'ok', findings, 5, 100);
|
|
assert.strictEqual(r.counts.critical, 1);
|
|
assert.strictEqual(r.counts.high, 2);
|
|
assert.strictEqual(r.counts.medium, 0);
|
|
assert.strictEqual(r.counts.low, 0);
|
|
assert.strictEqual(r.counts.info, 1);
|
|
});
|
|
|
|
it('includes error message when provided', () => {
|
|
const r = scannerResult('TST', 'error', [], 0, 50, 'boom');
|
|
assert.strictEqual(r.error, 'boom');
|
|
});
|
|
|
|
it('omits error when not provided', () => {
|
|
const r = scannerResult('TST', 'ok', [], 3, 100);
|
|
assert.strictEqual(r.error, undefined);
|
|
});
|
|
|
|
it('returns correct structure', () => {
|
|
const r = scannerResult('CML', 'ok', [], 2, 42);
|
|
assert.strictEqual(r.scanner, 'CML');
|
|
assert.strictEqual(r.status, 'ok');
|
|
assert.strictEqual(r.files_scanned, 2);
|
|
assert.strictEqual(r.duration_ms, 42);
|
|
assert.deepStrictEqual(r.findings, []);
|
|
});
|
|
});
|
|
|
|
describe('envelope', () => {
|
|
beforeEach(() => { resetCounter(); });
|
|
|
|
it('aggregates across scanners', () => {
|
|
const r1 = scannerResult('A', 'ok', [
|
|
finding({ scanner: 'A', severity: 'high', title: 'x', description: 'y' }),
|
|
], 1, 10);
|
|
resetCounter();
|
|
const r2 = scannerResult('B', 'ok', [
|
|
finding({ scanner: 'B', severity: 'critical', title: 'a', description: 'b' }),
|
|
finding({ scanner: 'B', severity: 'low', title: 'c', description: 'd' }),
|
|
], 2, 20);
|
|
|
|
const env = envelope('/target', [r1, r2], 50);
|
|
assert.strictEqual(env.aggregate.total_findings, 3);
|
|
assert.strictEqual(env.aggregate.counts.critical, 1);
|
|
assert.strictEqual(env.aggregate.counts.high, 1);
|
|
assert.strictEqual(env.aggregate.counts.low, 1);
|
|
assert.strictEqual(env.aggregate.scanners_ok, 2);
|
|
});
|
|
|
|
it('counts scanner statuses', () => {
|
|
const r1 = scannerResult('A', 'ok', [], 1, 10);
|
|
const r2 = scannerResult('B', 'skipped', [], 0, 5);
|
|
const r3 = scannerResult('C', 'error', [], 0, 3, 'fail');
|
|
|
|
const env = envelope('/t', [r1, r2, r3], 30);
|
|
assert.strictEqual(env.aggregate.scanners_ok, 1);
|
|
assert.strictEqual(env.aggregate.scanners_skipped, 1);
|
|
assert.strictEqual(env.aggregate.scanners_error, 1);
|
|
});
|
|
|
|
it('includes meta with version and tool', () => {
|
|
const env = envelope('/t', [], 0);
|
|
assert.strictEqual(env.meta.version, '2.2.0');
|
|
assert.strictEqual(env.meta.tool, 'config-audit');
|
|
assert.strictEqual(env.meta.target, '/t');
|
|
assert.ok(env.meta.timestamp);
|
|
});
|
|
|
|
it('calculates verdict correctly', () => {
|
|
const env = envelope('/t', [], 0);
|
|
assert.strictEqual(env.aggregate.verdict, 'PASS');
|
|
});
|
|
});
|