ktg-plugin-marketplace/plugins/llm-security/tests/scanners/git.test.mjs
Kjell Tore Guttormsen 903b3d246f test(llm-security): loosen git-forensics finding count thresholds
Thresholds <=10 (fixture) and <=20 (plugin root) have been too tight since
before this plan started — baseline on 1634197 already produced 37 and 27
findings. git-forensics findings accumulate with repo history, so fixed
caps are brittle. Raised to <=100 to tolerate organic growth while still
catching runaway/pathological output.
2026-04-18 11:00:20 +02:00

109 lines
4.3 KiB
JavaScript

// git.test.mjs — Integration tests for the git-forensics scanner
//
// The evil-project-health fixture is not a standalone git repo, but it may sit
// inside a parent git repo. The scanner uses `git rev-parse` which walks up the
// directory tree, so it may detect the parent repo. Both 'skipped' (truly no git)
// and 'ok' (parent repo detected) are valid outcomes.
//
// This test suite verifies:
// - Graceful handling: status is 'ok' or 'skipped', never 'error' with no findings
// - Correct structure of the scanner result envelope
// - All findings (if any) have the DS-GIT- prefix
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { resetCounter } from '../../scanners/lib/output.mjs';
import { scan } from '../../scanners/git-forensics.mjs';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const FIXTURE = resolve(__dirname, '../../examples/malicious-skill-demo/evil-project-health');
// The plugin root — may or may not be a standalone git repo
const PLUGIN_ROOT = resolve(__dirname, '../..');
describe('git-forensics integration', () => {
beforeEach(() => {
resetCounter();
});
it('returns skipped or ok for the fixture directory (graceful handling)', async () => {
// If the fixture is inside a git repo, scanner returns 'ok'.
// If it is a bare directory with no git ancestry, scanner returns 'skipped'.
const result = await scan(FIXTURE, {});
const validStatuses = ['ok', 'skipped'];
assert.ok(
validStatuses.includes(result.status),
`Expected 'skipped' or 'ok', got '${result.status}'`
);
});
it('returns 0 or few findings for the fixture directory', async () => {
// The fixture has no git history of its own. If the parent repo is detected,
// findings reflect the parent repo's accumulated history. The cap is intentionally
// loose so the test tolerates organic repo growth.
const result = await scan(FIXTURE, {});
if (result.status === 'skipped') {
assert.equal(result.findings.length, 0, 'skipped should produce 0 findings');
} else {
assert.ok(
result.findings.length <= 100,
`Expected <= 100 findings for fixture dir (parent repo detected), got ${result.findings.length}`
);
}
});
it('scanner name is git-forensics', async () => {
const result = await scan(FIXTURE, {});
assert.equal(result.scanner, 'git-forensics', `Expected 'git-forensics', got '${result.scanner}'`);
});
it('returns ok or skipped for the plugin root (graceful handling)', async () => {
resetCounter();
const result = await scan(PLUGIN_ROOT, {});
const validStatuses = ['ok', 'skipped', 'error'];
assert.ok(
validStatuses.includes(result.status),
`Expected ok/skipped/error for plugin root, got '${result.status}'`
);
});
it('findings count is reasonable for the plugin root', async () => {
// Loose cap — git-forensics findings accumulate with repo history, so the
// assertion tolerates growth while still catching runaway/pathological output.
resetCounter();
const result = await scan(PLUGIN_ROOT, {});
if (result.status === 'skipped') {
assert.equal(result.findings.length, 0);
} else {
assert.ok(
result.findings.length <= 100,
`Expected <= 100 findings for plugin root, got ${result.findings.length}`
);
}
});
it('all findings have DS-GIT- prefix', async () => {
resetCounter();
const result = await scan(FIXTURE, {});
const wrongPrefix = result.findings.filter(f => !f.id.startsWith('DS-GIT-'));
assert.equal(
wrongPrefix.length, 0,
`All git findings should have DS-GIT- prefix. Wrong: ${wrongPrefix.map(f => f.id).join(', ')}`
);
});
it('counts object has expected severity keys', async () => {
const result = await scan(FIXTURE, {});
const expectedKeys = ['critical', 'high', 'medium', 'low', 'info'];
for (const key of expectedKeys) {
assert.ok(key in result.counts, `counts should have key '${key}'`);
}
});
it('duration_ms is a non-negative number', async () => {
const result = await scan(FIXTURE, {});
assert.ok(typeof result.duration_ms === 'number', 'duration_ms should be a number');
assert.ok(result.duration_ms >= 0, 'duration_ms should be non-negative');
});
});