ktg-plugin-marketplace/plugins/config-audit/tests/lib/suppression.test.mjs

199 lines
6 KiB
JavaScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { writeFile, mkdir, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import {
loadSuppressions,
parseIgnoreFile,
applySuppressions,
formatSuppressionSummary,
} from '../../scanners/lib/suppression.mjs';
// --- Helpers ---
function makeFinding(id, scanner, severity = 'medium') {
return {
id,
scanner,
severity,
title: `Finding ${id}`,
description: `Description for ${id}`,
file: null,
line: null,
evidence: null,
category: null,
recommendation: null,
autoFixable: false,
};
}
// ========================================
// parseIgnoreFile
// ========================================
describe('parseIgnoreFile', () => {
it('parses exact finding IDs', () => {
const result = parseIgnoreFile('CA-CML-001\nCA-SET-003');
assert.equal(result.length, 2);
assert.equal(result[0].pattern, 'CA-CML-001');
assert.equal(result[1].pattern, 'CA-SET-003');
});
it('parses glob patterns', () => {
const result = parseIgnoreFile('CA-GAP-*');
assert.equal(result.length, 1);
assert.equal(result[0].pattern, 'CA-GAP-*');
});
it('skips comments and empty lines', () => {
const content = `# This is a comment
CA-CML-001
# Another comment
CA-SET-002`;
const result = parseIgnoreFile(content);
assert.equal(result.length, 2);
});
it('extracts inline comments', () => {
const result = parseIgnoreFile('CA-HKV-003 # Known timeout in CI');
assert.equal(result.length, 1);
assert.equal(result[0].pattern, 'CA-HKV-003');
assert.equal(result[0].comment, 'Known timeout in CI');
});
it('returns empty array for empty content', () => {
const result = parseIgnoreFile('');
assert.deepEqual(result, []);
});
it('returns empty array for comment-only content', () => {
const result = parseIgnoreFile('# Just comments\n# Nothing else');
assert.deepEqual(result, []);
});
});
// ========================================
// applySuppressions
// ========================================
describe('applySuppressions', () => {
it('filters exact match', () => {
const findings = [
makeFinding('CA-CML-001', 'CML'),
makeFinding('CA-CML-002', 'CML'),
];
const suppressions = [{ pattern: 'CA-CML-001', comment: '' }];
const { active, suppressed } = applySuppressions(findings, suppressions);
assert.equal(active.length, 1);
assert.equal(active[0].id, 'CA-CML-002');
assert.equal(suppressed.length, 1);
assert.equal(suppressed[0].id, 'CA-CML-001');
});
it('filters glob pattern CA-SET-*', () => {
const findings = [
makeFinding('CA-SET-001', 'SET'),
makeFinding('CA-SET-002', 'SET'),
makeFinding('CA-CML-001', 'CML'),
];
const suppressions = [{ pattern: 'CA-SET-*', comment: '' }];
const { active, suppressed } = applySuppressions(findings, suppressions);
assert.equal(active.length, 1);
assert.equal(active[0].id, 'CA-CML-001');
assert.equal(suppressed.length, 2);
});
it('returns all active when no suppressions', () => {
const findings = [makeFinding('CA-CML-001', 'CML')];
const { active, suppressed } = applySuppressions(findings, []);
assert.equal(active.length, 1);
assert.equal(suppressed.length, 0);
});
it('returns all active when suppressions is null', () => {
const findings = [makeFinding('CA-CML-001', 'CML')];
const { active, suppressed } = applySuppressions(findings, null);
assert.equal(active.length, 1);
assert.equal(suppressed.length, 0);
});
it('handles empty findings list', () => {
const suppressions = [{ pattern: 'CA-CML-*', comment: '' }];
const { active, suppressed } = applySuppressions([], suppressions);
assert.equal(active.length, 0);
assert.equal(suppressed.length, 0);
});
it('applies multiple suppression patterns', () => {
const findings = [
makeFinding('CA-CML-001', 'CML'),
makeFinding('CA-SET-001', 'SET'),
makeFinding('CA-GAP-001', 'GAP'),
];
const suppressions = [
{ pattern: 'CA-CML-001', comment: '' },
{ pattern: 'CA-GAP-*', comment: '' },
];
const { active, suppressed } = applySuppressions(findings, suppressions);
assert.equal(active.length, 1);
assert.equal(active[0].id, 'CA-SET-001');
assert.equal(suppressed.length, 2);
});
});
// ========================================
// formatSuppressionSummary
// ========================================
describe('formatSuppressionSummary', () => {
it('formats correct count and groups', () => {
const suppressed = [
makeFinding('CA-GAP-001', 'GAP'),
makeFinding('CA-GAP-002', 'GAP'),
makeFinding('CA-HKV-003', 'HKV'),
];
const summary = formatSuppressionSummary(suppressed);
assert.ok(summary.includes('3 finding(s) suppressed'));
assert.ok(summary.includes('CA-GAP-*'));
assert.ok(summary.includes('CA-HKV-*'));
});
it('returns zero message for empty array', () => {
assert.equal(formatSuppressionSummary([]), '0 findings suppressed');
});
it('returns zero message for null', () => {
assert.equal(formatSuppressionSummary(null), '0 findings suppressed');
});
});
// ========================================
// loadSuppressions
// ========================================
describe('loadSuppressions', () => {
const tmpDir = join(tmpdir(), `config-audit-suppress-test-${Date.now()}`);
it('returns empty when no .config-audit-ignore exists', async () => {
const result = await loadSuppressions('/nonexistent/path');
assert.deepEqual(result.suppressions, []);
assert.equal(result.source, 'none');
});
it('loads from project directory', async () => {
await mkdir(tmpDir, { recursive: true });
await writeFile(join(tmpDir, '.config-audit-ignore'), 'CA-GAP-*\nCA-CML-001\n');
const result = await loadSuppressions(tmpDir);
assert.equal(result.suppressions.length, 2);
assert.equal(result.source, 'project');
await rm(tmpDir, { recursive: true, force: true });
});
});