feat: initial open marketplace with llm-security, config-audit, ultraplan-local

This commit is contained in:
Kjell Tore Guttormsen 2026-04-06 18:47:49 +02:00
commit f93d6abdae
380 changed files with 65935 additions and 0 deletions

View file

@ -0,0 +1,278 @@
// output.test.mjs — Tests for scanners/lib/output.mjs
// Zero external dependencies: node:test + node:assert only.
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import {
resetCounter,
finding,
scannerResult,
envelope,
} from '../../scanners/lib/output.mjs';
// ---------------------------------------------------------------------------
// finding + resetCounter
// ---------------------------------------------------------------------------
describe('finding', () => {
beforeEach(() => {
resetCounter();
});
it('returns an object with auto-incrementing ID in DS-SCANNER-NNN format', () => {
const f = finding({ scanner: 'UNI', severity: 'high', title: 'Test', description: 'Desc' });
assert.equal(f.id, 'DS-UNI-001');
});
it('increments ID with each call', () => {
const f1 = finding({ scanner: 'UNI', severity: 'high', title: 'A', description: 'Desc' });
const f2 = finding({ scanner: 'ENT', severity: 'medium', title: 'B', description: 'Desc' });
const f3 = finding({ scanner: 'PRM', severity: 'low', title: 'C', description: 'Desc' });
assert.equal(f1.id, 'DS-UNI-001');
assert.equal(f2.id, 'DS-ENT-002');
assert.equal(f3.id, 'DS-PRM-003');
});
it('zero-pads counter to 3 digits', () => {
for (let i = 0; i < 9; i++) {
finding({ scanner: 'UNI', severity: 'info', title: `F${i}`, description: 'x' });
}
const f10 = finding({ scanner: 'UNI', severity: 'info', title: 'F10', description: 'x' });
assert.equal(f10.id, 'DS-UNI-010');
});
it('includes all required fields', () => {
const f = finding({
scanner: 'ENT',
severity: 'critical',
title: 'High Entropy Secret',
description: 'Found a high-entropy string that looks like an API key.',
});
assert.equal(f.scanner, 'ENT');
assert.equal(f.severity, 'critical');
assert.equal(f.title, 'High Entropy Secret');
assert.equal(f.description, 'Found a high-entropy string that looks like an API key.');
});
it('sets optional fields to null when not provided', () => {
const f = finding({ scanner: 'UNI', severity: 'low', title: 'T', description: 'D' });
assert.equal(f.file, null);
assert.equal(f.line, null);
assert.equal(f.evidence, null);
assert.equal(f.owasp, null);
assert.equal(f.recommendation, null);
});
it('includes all provided optional fields', () => {
const f = finding({
scanner: 'TNT',
severity: 'high',
title: 'Taint flow',
description: 'Untrusted data flows into eval().',
file: 'src/runner.mjs',
line: 42,
evidence: 'eval(userInput)',
owasp: 'LLM01',
recommendation: 'Sanitize user input before evaluation.',
});
assert.equal(f.file, 'src/runner.mjs');
assert.equal(f.line, 42);
assert.equal(f.evidence, 'eval(userInput)');
assert.equal(f.owasp, 'LLM01');
assert.equal(f.recommendation, 'Sanitize user input before evaluation.');
});
});
describe('resetCounter', () => {
it('resets the counter so the next finding starts at 001', () => {
// Advance counter to some arbitrary position
finding({ scanner: 'UNI', severity: 'info', title: 'A', description: 'x' });
finding({ scanner: 'UNI', severity: 'info', title: 'B', description: 'x' });
finding({ scanner: 'UNI', severity: 'info', title: 'C', description: 'x' });
resetCounter();
const f = finding({ scanner: 'ENT', severity: 'low', title: 'After reset', description: 'x' });
assert.equal(f.id, 'DS-ENT-001');
});
it('can be called multiple times without error', () => {
assert.doesNotThrow(() => {
resetCounter();
resetCounter();
resetCounter();
});
});
});
// ---------------------------------------------------------------------------
// scannerResult
// ---------------------------------------------------------------------------
describe('scannerResult', () => {
beforeEach(() => {
resetCounter();
});
it('returns an object with the expected top-level keys', () => {
const result = scannerResult('unicode-scanner', 'ok', [], 10, 42);
assert.ok('scanner' in result);
assert.ok('status' in result);
assert.ok('findings' in result);
assert.ok('counts' in result);
assert.ok('files_scanned' in result);
assert.ok('duration_ms' in result);
});
it('sets scanner name and status correctly', () => {
const result = scannerResult('entropy-scanner', 'ok', [], 5, 100);
assert.equal(result.scanner, 'entropy-scanner');
assert.equal(result.status, 'ok');
});
it('returns empty counts for no findings', () => {
const result = scannerResult('dep-auditor', 'ok', [], 0, 0);
assert.deepEqual(result.counts, { critical: 0, high: 0, medium: 0, low: 0, info: 0 });
});
it('counts findings by severity correctly', () => {
const f1 = finding({ scanner: 'ENT', severity: 'critical', title: 'A', description: 'x' });
const f2 = finding({ scanner: 'ENT', severity: 'high', title: 'B', description: 'x' });
const f3 = finding({ scanner: 'ENT', severity: 'high', title: 'C', description: 'x' });
const f4 = finding({ scanner: 'ENT', severity: 'medium', title: 'D', description: 'x' });
const result = scannerResult('entropy-scanner', 'ok', [f1, f2, f3, f4], 20, 300);
assert.equal(result.counts.critical, 1);
assert.equal(result.counts.high, 2);
assert.equal(result.counts.medium, 1);
assert.equal(result.counts.low, 0);
assert.equal(result.counts.info, 0);
});
it('stores findings array as provided', () => {
const f = finding({ scanner: 'UNI', severity: 'low', title: 'X', description: 'y' });
const result = scannerResult('unicode-scanner', 'ok', [f], 1, 10);
assert.equal(result.findings.length, 1);
assert.equal(result.findings[0].id, f.id);
});
it('sets files_scanned and duration_ms', () => {
const result = scannerResult('git-forensics', 'ok', [], 77, 1234);
assert.equal(result.files_scanned, 77);
assert.equal(result.duration_ms, 1234);
});
it('does not include error field when errorMsg is not provided', () => {
const result = scannerResult('taint-tracer', 'ok', [], 5, 50);
assert.ok(!('error' in result));
});
it('includes error field when errorMsg is provided', () => {
const result = scannerResult('dep-auditor', 'error', [], 0, 10, 'ENOENT: package.json not found');
assert.equal(result.error, 'ENOENT: package.json not found');
assert.equal(result.status, 'error');
});
it('handles skipped status', () => {
const result = scannerResult('network-mapper', 'skipped', [], 0, 0);
assert.equal(result.status, 'skipped');
});
});
// ---------------------------------------------------------------------------
// envelope
// ---------------------------------------------------------------------------
describe('envelope', () => {
beforeEach(() => {
resetCounter();
});
it('returns an object with meta, scanners, and aggregate keys', () => {
const result = envelope('/some/path', {}, 100);
assert.ok('meta' in result);
assert.ok('scanners' in result);
assert.ok('aggregate' in result);
});
it('meta contains target, timestamp, node_version, total_duration_ms', () => {
const result = envelope('/my/project', {}, 999);
assert.equal(result.meta.target, '/my/project');
assert.ok(typeof result.meta.timestamp === 'string');
assert.ok(result.meta.timestamp.length > 0);
assert.ok(typeof result.meta.node_version === 'string');
assert.equal(result.meta.total_duration_ms, 999);
});
it('aggregate contains risk_score and verdict', () => {
const result = envelope('/project', {}, 0);
assert.ok('risk_score' in result.aggregate);
assert.ok('verdict' in result.aggregate);
});
it('aggregate has zero counts and ALLOW verdict for empty scanner results', () => {
const result = envelope('/project', {}, 0);
assert.equal(result.aggregate.total_findings, 0);
assert.equal(result.aggregate.risk_score, 0);
assert.equal(result.aggregate.verdict, 'ALLOW');
assert.deepEqual(result.aggregate.counts, { critical: 0, high: 0, medium: 0, low: 0, info: 0 });
});
it('aggregates counts from multiple scanner results', () => {
const f1 = finding({ scanner: 'UNI', severity: 'critical', title: 'A', description: 'x' });
const f2 = finding({ scanner: 'ENT', severity: 'high', title: 'B', description: 'x' });
const scanners = {
unicode: scannerResult('unicode-scanner', 'ok', [f1], 10, 50),
entropy: scannerResult('entropy-scanner', 'ok', [f2], 10, 75),
};
const result = envelope('/project', scanners, 125);
assert.equal(result.aggregate.total_findings, 2);
assert.equal(result.aggregate.counts.critical, 1);
assert.equal(result.aggregate.counts.high, 1);
});
it('computes correct risk_score from aggregated counts', () => {
// 1 critical = score 25
const f = finding({ scanner: 'ENT', severity: 'critical', title: 'C', description: 'x' });
const scanners = {
entropy: scannerResult('entropy-scanner', 'ok', [f], 5, 30),
};
const result = envelope('/project', scanners, 30);
assert.equal(result.aggregate.risk_score, 25);
});
it('returns BLOCK verdict when critical finding present', () => {
const f = finding({ scanner: 'UNI', severity: 'critical', title: 'Critical', description: 'x' });
const scanners = {
uni: scannerResult('unicode-scanner', 'ok', [f], 1, 10),
};
const result = envelope('/project', scanners, 10);
assert.equal(result.aggregate.verdict, 'BLOCK');
});
it('tracks scanner ok/error/skipped counts', () => {
const scanners = {
uni: scannerResult('unicode-scanner', 'ok', [], 5, 10),
ent: scannerResult('entropy-scanner', 'error', [], 0, 5, 'failed'),
net: scannerResult('network-mapper', 'skipped', [], 0, 0),
};
const result = envelope('/project', scanners, 15);
assert.equal(result.aggregate.scanners_ok, 1);
assert.equal(result.aggregate.scanners_error, 1);
assert.equal(result.aggregate.scanners_skipped, 1);
});
it('includes owasp_breakdown in aggregate', () => {
const result = envelope('/project', {}, 0);
assert.ok('owasp_breakdown' in result.aggregate);
});
it('passes through scanner results as-is in scanners field', () => {
const sr = scannerResult('unicode-scanner', 'ok', [], 3, 20);
const scanners = { uni: sr };
const result = envelope('/project', scanners, 20);
assert.deepEqual(result.scanners, scanners);
});
});