ssvc
Version:
TypeScript implementation of SSVC (Stakeholder-Specific Vulnerability Categorization). A prioritization framework to triage CVE vulnerabilities as an alternative or compliment to CVSS
249 lines (218 loc) • 8.89 kB
text/typescript
import {
createDecision,
listMethodologies,
PluginRegistry,
CISAPlugin,
CoordinatorTriagePlugin,
Decision,
CISAExploitationStatus,
CISAAutomatableStatus,
CoordinatorUtilityLevel,
ReportPublicStatus
} from './index';
describe('Public API', () => {
describe('createDecision', () => {
it('should create CISA decisions', () => {
const decision = createDecision('CISA', {
exploitation: 'active',
automatable: 'yes',
technical_impact: 'total',
mission_wellbeing: 'high'
});
const outcome = decision.evaluate();
expect(outcome.action).toBe('ACT');
expect(outcome.priority).toBe('IMMEDIATE');
});
it('should create Coordinator Triage decisions', () => {
const decision = createDecision('Coordinator Triage', {
report_public: 'yes',
supplier_contacted: 'yes',
report_credibility: 'credible',
supplier_cardinality: 'multiple',
supplier_engagement: 'active',
utility: 'super_effective',
public_safety_impact: 'significant'
});
const outcome = decision.evaluate();
expect(outcome.action).toBe('COORDINATE');
expect(outcome.priority).toBe('HIGH');
});
it('should create decisions with empty options', () => {
const decision = createDecision('CISA');
expect(decision).toBeInstanceOf(Decision);
});
it('should throw error for unknown methodology', () => {
expect(() => {
const decision = createDecision('UnknownMethodology', {});
decision.evaluate();
}).toThrow('Unknown methodology: UnknownMethodology');
});
});
describe('listMethodologies', () => {
it('should list available methodologies', () => {
const methodologies = listMethodologies();
expect(methodologies).toContain('CISA');
expect(methodologies).toContain('Coordinator Triage');
expect(methodologies).toContain('Coordinator Publication');
expect(methodologies).toContain('Supplier');
expect(methodologies).toContain('Deployer');
expect(methodologies).toContain('Engineer Triage');
expect(methodologies).toContain('AI/LLM Triage');
expect(methodologies).toHaveLength(7);
});
it('should return array of strings', () => {
const methodologies = listMethodologies();
expect(Array.isArray(methodologies)).toBe(true);
methodologies.forEach(methodology => {
expect(typeof methodology).toBe('string');
});
});
});
describe('Plugin exports', () => {
it('should export PluginRegistry', () => {
expect(PluginRegistry).toBeDefined();
expect(typeof PluginRegistry).toBe('function');
});
it('should export CISAPlugin', () => {
expect(CISAPlugin).toBeDefined();
expect(typeof CISAPlugin).toBe('function');
const plugin = new CISAPlugin();
expect(plugin.name).toBe('CISA');
});
it('should export CoordinatorTriagePlugin', () => {
expect(CoordinatorTriagePlugin).toBeDefined();
expect(typeof CoordinatorTriagePlugin).toBe('function');
const plugin = new CoordinatorTriagePlugin();
expect(plugin.name).toBe('Coordinator Triage');
});
it('should export Decision class', () => {
expect(Decision).toBeDefined();
expect(typeof Decision).toBe('function');
});
});
describe('Auto-registration', () => {
it('should auto-register built-in plugins', () => {
const registry = PluginRegistry.getInstance();
expect(registry.has('CISA')).toBe(true);
expect(registry.has('Coordinator Triage')).toBe(true);
expect(registry.has('Coordinator Publication')).toBe(true);
expect(registry.has('Supplier')).toBe(true);
expect(registry.has('Deployer')).toBe(true);
expect(registry.has('Engineer Triage')).toBe(true);
expect(registry.has('AI/LLM Triage')).toBe(true);
const plugins = registry.list();
expect(plugins).toHaveLength(7);
const cisaPlugin = registry.get('CISA');
const coordinatorPlugin = registry.get('Coordinator Triage');
expect(cisaPlugin).toBeInstanceOf(CISAPlugin);
expect(coordinatorPlugin).toBeInstanceOf(CoordinatorTriagePlugin);
});
});
describe('Integration tests', () => {
it('should work end-to-end for CISA methodology', () => {
// Test various CISA scenarios
const testCases = [
{
params: { exploitation: 'active', automatable: 'yes', technical_impact: 'total', mission_wellbeing: 'high' },
expected: { action: 'ACT', priority: 'IMMEDIATE' }
},
{
params: { exploitation: 'poc', automatable: 'no', technical_impact: 'partial', mission_wellbeing: 'high' },
expected: { action: 'TRACK_STAR', priority: 'MEDIUM' }
},
{
params: { exploitation: 'none', automatable: 'no', technical_impact: 'partial', mission_wellbeing: 'low' },
expected: { action: 'TRACK', priority: 'LOW' }
}
];
testCases.forEach(({ params, expected }) => {
const decision = createDecision('CISA', params);
const outcome = decision.evaluate();
expect(outcome.action).toBe(expected.action);
expect(outcome.priority).toBe(expected.priority);
});
});
it('should work end-to-end for Coordinator Triage methodology', () => {
// Test various Coordinator Triage scenarios
const testCases = [
{
params: {
report_public: 'yes', supplier_contacted: 'yes', report_credibility: 'credible',
supplier_cardinality: 'multiple', utility: 'super_effective', public_safety_impact: 'significant'
},
expected: { action: 'COORDINATE', priority: 'HIGH' }
},
{
params: {
report_public: 'yes', supplier_contacted: 'yes', report_credibility: 'credible',
supplier_cardinality: 'one', utility: 'super_effective', public_safety_impact: 'minimal'
},
expected: { action: 'DECLINE', priority: 'LOW' }
},
{
params: {
report_public: 'yes', supplier_contacted: 'yes', report_credibility: 'not_credible'
},
expected: { action: 'DECLINE', priority: 'LOW' }
}
];
testCases.forEach(({ params, expected }) => {
const decision = createDecision('Coordinator Triage', params);
const outcome = decision.evaluate();
expect(outcome.action).toBe(expected.action);
expect(outcome.priority).toBe(expected.priority);
});
});
it('should maintain decision state properly', () => {
const decision1 = createDecision('CISA', {
exploitation: 'active',
automatable: 'yes',
technical_impact: 'total',
mission_wellbeing: 'high'
});
const decision2 = createDecision('CISA', {
exploitation: 'none',
automatable: 'no',
technical_impact: 'partial',
mission_wellbeing: 'low'
});
const outcome1 = decision1.evaluate();
const outcome2 = decision2.evaluate();
// Verify each decision maintains its own state
expect(outcome1.action).toBe('ACT');
expect(outcome2.action).toBe('TRACK');
expect(decision1.outcome?.action).toBe('ACT');
expect(decision2.outcome?.action).toBe('TRACK');
});
});
describe('Type exports', () => {
it('should export CISA types with aliases', () => {
// Verify CISA enum values are accessible
expect(CISAExploitationStatus.ACTIVE).toBe('active');
expect(CISAExploitationStatus.POC).toBe('poc');
expect(CISAExploitationStatus.NONE).toBe('none');
expect(CISAAutomatableStatus.YES).toBe('yes');
expect(CISAAutomatableStatus.NO).toBe('no');
});
it('should export Coordinator Triage types', () => {
// Verify Coordinator Triage enum values are accessible
expect(CoordinatorUtilityLevel.LABORIOUS).toBe('laborious');
expect(CoordinatorUtilityLevel.EFFICIENT).toBe('efficient');
expect(CoordinatorUtilityLevel.SUPER_EFFECTIVE).toBe('super_effective');
expect(ReportPublicStatus.YES).toBe('yes');
expect(ReportPublicStatus.NO).toBe('no');
});
});
describe('Backward compatibility', () => {
it('should still export legacy Decision class', () => {
// The legacy Decision class should still be available
expect(Decision).toBeDefined();
expect(typeof Decision).toBe('function');
});
it('should support legacy imports', () => {
// Test that the old decision.ts exports are still available
// This is tested implicitly by the existing decision.test.ts file
expect(true).toBe(true);
});
});
});