ssvc
Version:
TypeScript implementation of SSVC (Stakeholder-Specific Vulnerability Categorization). A prioritization framework to triage CVE vulnerabilities as an alternative or compliment to CVSS
148 lines (122 loc) • 4.49 kB
text/typescript
/**
* SSVC Core Library with Plugin Support
*
* This module provides the core functionality for SSVC with a plugin-based architecture.
*/
export interface SSVCOutcome {
action: string;
priority: string;
}
export abstract class SSVCPlugin {
abstract readonly name: string;
abstract readonly description: string;
abstract readonly version: string;
abstract createDecision(options: Record<string, any>): SSVCDecision;
abstract fromVector?(vectorString: string): SSVCDecision;
}
export interface SSVCDecision {
evaluate(): SSVCOutcome;
outcome?: SSVCOutcome;
toVector?(): string | undefined;
evaluationId?: string; // UUID for audit trail tracking
}
export interface AuditableDecision extends SSVCDecision {
evaluationId: string; // Required for auditable decisions
attachEvidence(decisionPoint: string, evidence: any): void;
mapDataSource(decisionPoint: string, dataSource: any): void;
getAuditEntry(): any;
generateForensicReport(): any;
getEvidence(): Record<string, any>;
getDataSources(): Record<string, any>;
getTimeline(): any[];
getWrappedDecision(): SSVCDecision;
}
export class PluginRegistry {
private plugins: Map<string, SSVCPlugin> = new Map();
private static instance: PluginRegistry;
static getInstance(): PluginRegistry {
if (!PluginRegistry.instance) {
PluginRegistry.instance = new PluginRegistry();
}
return PluginRegistry.instance;
}
register(plugin: SSVCPlugin): void {
this.plugins.set(plugin.name.toLowerCase(), plugin);
}
get(name: string): SSVCPlugin | undefined {
return this.plugins.get(name.toLowerCase());
}
list(): SSVCPlugin[] {
return Array.from(this.plugins.values());
}
has(name: string): boolean {
return this.plugins.has(name.toLowerCase());
}
}
export class Decision {
private methodology: string;
private options: Record<string, any>;
public outcome?: SSVCOutcome;
public evaluationId?: string;
constructor(methodology: string, options: Record<string, any> = {}) {
this.methodology = methodology;
this.options = options;
// Note: We don't generate evaluationId here to maintain backward compatibility
// It can be set by audit systems when needed
}
evaluate(): SSVCOutcome {
const registry = PluginRegistry.getInstance();
const plugin = registry.get(this.methodology);
if (!plugin) {
throw new Error(`Unknown methodology: ${this.methodology}. Available methodologies: ${registry.list().map(p => p.name).join(', ')}`);
}
const decision = plugin.createDecision(this.options);
this.outcome = decision.evaluate();
return this.outcome;
}
static createDecision(methodology: string, options: Record<string, any> = {}): Decision {
return new Decision(methodology, options);
}
static fromVector(vectorString: string): Decision {
const registry = PluginRegistry.getInstance();
// Parse the methodology name from vector string
const match = vectorString.match(/^([A-Z_]+)v?\d*/);
if (!match) {
throw new Error(`Invalid vector string format: ${vectorString}`);
}
const methodologyPrefix = match[1];
// Find plugin by matching vector prefix
for (const plugin of registry.list()) {
if (plugin.fromVector) {
try {
const parsedDecision = plugin.fromVector(vectorString);
// Create a Decision that wraps the parsed decision
const decision = new Decision(plugin.name.toLowerCase(), {});
decision.outcome = parsedDecision.evaluate();
// Override the evaluate method to return the parsed decision's outcome
decision.evaluate = () => parsedDecision.evaluate();
return decision;
} catch (error) {
// Try next plugin
continue;
}
}
}
throw new Error(`No plugin found that can parse vector string: ${vectorString}`);
}
toVector(): string {
const registry = PluginRegistry.getInstance();
const plugin = registry.get(this.methodology);
if (!plugin) {
throw new Error(`Unknown methodology: ${this.methodology}`);
}
const decision = plugin.createDecision(this.options);
if (decision.toVector) {
const vector = decision.toVector();
if (vector !== undefined) {
return vector;
}
}
throw new Error(`Vector string generation not supported for methodology: ${this.methodology}`);
}
}