114 lines
4.6 KiB
TypeScript
114 lines
4.6 KiB
TypeScript
import { describe, test, expect } from 'bun:test';
|
|
import { shouldBlockUpdate, combineScanSummaries } from '../src/lib/server/scheduler/tasks/update-utils';
|
|
|
|
const clean = { critical: 0, high: 0, medium: 0, low: 0, negligible: 0, unknown: 0 };
|
|
|
|
describe('shouldBlockUpdate', () => {
|
|
test('never blocks with "never" criteria', () => {
|
|
const vulny = { ...clean, critical: 5, high: 10 };
|
|
expect(shouldBlockUpdate('never', vulny).blocked).toBe(false);
|
|
});
|
|
|
|
test('"any" blocks when any vulnerabilities exist', () => {
|
|
expect(shouldBlockUpdate('any', { ...clean, low: 1 }).blocked).toBe(true);
|
|
expect(shouldBlockUpdate('any', clean).blocked).toBe(false);
|
|
});
|
|
|
|
test('"critical_high" blocks on critical or high only', () => {
|
|
expect(shouldBlockUpdate('critical_high', { ...clean, critical: 1 }).blocked).toBe(true);
|
|
expect(shouldBlockUpdate('critical_high', { ...clean, high: 1 }).blocked).toBe(true);
|
|
expect(shouldBlockUpdate('critical_high', { ...clean, medium: 5 }).blocked).toBe(false);
|
|
expect(shouldBlockUpdate('critical_high', { ...clean, low: 10 }).blocked).toBe(false);
|
|
});
|
|
|
|
test('"critical" blocks on critical only', () => {
|
|
expect(shouldBlockUpdate('critical', { ...clean, critical: 1 }).blocked).toBe(true);
|
|
expect(shouldBlockUpdate('critical', { ...clean, high: 10 }).blocked).toBe(false);
|
|
});
|
|
|
|
test('"more_than_current" blocks when new > current', () => {
|
|
const current = { ...clean, low: 5 };
|
|
const newMore = { ...clean, low: 6 };
|
|
const newLess = { ...clean, low: 4 };
|
|
const newEqual = { ...clean, low: 5 };
|
|
|
|
expect(shouldBlockUpdate('more_than_current', newMore, current).blocked).toBe(true);
|
|
expect(shouldBlockUpdate('more_than_current', newLess, current).blocked).toBe(false);
|
|
expect(shouldBlockUpdate('more_than_current', newEqual, current).blocked).toBe(false);
|
|
});
|
|
|
|
test('"more_than_current" does not block without current scan', () => {
|
|
expect(shouldBlockUpdate('more_than_current', { ...clean, critical: 100 }).blocked).toBe(false);
|
|
});
|
|
|
|
test('returns reason string when blocked', () => {
|
|
const result = shouldBlockUpdate('critical', { ...clean, critical: 3 });
|
|
expect(result.blocked).toBe(true);
|
|
expect(result.reason).toContain('3 critical');
|
|
});
|
|
|
|
// --- M7 regression ---
|
|
// The 'more_than_current' comparison is gated on a current scan summary; the
|
|
// env-wide update path used to pass undefined, so it silently never blocked.
|
|
// These pin the contract that env-update-check.ts now relies on.
|
|
test('M7: passing undefined current scan never blocks, even for a very vulnerable image', () => {
|
|
const veryVulnerable = { ...clean, critical: 50, high: 50, medium: 50, low: 50 };
|
|
expect(shouldBlockUpdate('more_than_current', veryVulnerable, undefined).blocked).toBe(false);
|
|
});
|
|
|
|
test('M7: blocks when current scan is supplied and new has more vulns', () => {
|
|
const current = { ...clean, critical: 1, high: 1 }; // 2 total
|
|
const newMore = { ...clean, critical: 2, high: 1 }; // 3 total
|
|
const { blocked, reason } = shouldBlockUpdate('more_than_current', newMore, current);
|
|
expect(blocked).toBe(true);
|
|
expect(reason).toContain('3');
|
|
expect(reason).toContain('2');
|
|
});
|
|
|
|
test('returns empty reason when not blocked', () => {
|
|
expect(shouldBlockUpdate('never', { ...clean, critical: 5 }).reason).toBe('');
|
|
});
|
|
|
|
test('"any" reason includes all severity counts', () => {
|
|
const scan = { ...clean, critical: 1, high: 2, medium: 3, low: 4 };
|
|
const result = shouldBlockUpdate('any', scan);
|
|
expect(result.reason).toContain('1 critical');
|
|
expect(result.reason).toContain('2 high');
|
|
expect(result.reason).toContain('3 medium');
|
|
expect(result.reason).toContain('4 low');
|
|
});
|
|
});
|
|
|
|
describe('combineScanSummaries', () => {
|
|
test('returns zeros for empty input', () => {
|
|
expect(combineScanSummaries([])).toEqual(clean);
|
|
});
|
|
|
|
test('returns single result as-is', () => {
|
|
const summary = { ...clean, critical: 3, high: 5 };
|
|
expect(combineScanSummaries([{ summary }])).toEqual(summary);
|
|
});
|
|
|
|
test('takes maximum of each severity across scanners', () => {
|
|
const grype = { summary: { critical: 2, high: 5, medium: 0, low: 1, negligible: 0, unknown: 0 } };
|
|
const trivy = { summary: { critical: 3, high: 1, medium: 4, low: 0, negligible: 2, unknown: 1 } };
|
|
|
|
expect(combineScanSummaries([grype, trivy])).toEqual({
|
|
critical: 3,
|
|
high: 5,
|
|
medium: 4,
|
|
low: 1,
|
|
negligible: 2,
|
|
unknown: 1
|
|
});
|
|
});
|
|
|
|
test('handles three scanners', () => {
|
|
const a = { summary: { ...clean, critical: 1 } };
|
|
const b = { summary: { ...clean, critical: 5 } };
|
|
const c = { summary: { ...clean, critical: 3 } };
|
|
|
|
expect(combineScanSummaries([a, b, c]).critical).toBe(5);
|
|
});
|
|
});
|