199 lines
6 KiB
JavaScript
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 });
|
|
});
|
|
});
|