/** * Severity constants, risk scoring, and verdict logic for config-audit scanners. * Zero external dependencies. */ export const SEVERITY = Object.freeze({ critical: 'critical', high: 'high', medium: 'medium', low: 'low', info: 'info', }); const WEIGHTS = { critical: 25, high: 10, medium: 4, low: 1, info: 0 }; /** * Calculate a 0-100 risk score from severity counts. * @param {{ critical?: number, high?: number, medium?: number, low?: number, info?: number }} counts * @returns {number} */ export function riskScore(counts) { let score = 0; for (const [sev, weight] of Object.entries(WEIGHTS)) { score += (counts[sev] || 0) * weight; } return Math.min(score, 100); } /** * Determine overall verdict from severity counts. * @param {{ critical?: number, high?: number, medium?: number, low?: number, info?: number }} counts * @returns {'FAIL' | 'WARNING' | 'PASS'} */ export function verdict(counts) { const score = riskScore(counts); if ((counts.critical || 0) >= 1 || score >= 61) return 'FAIL'; if ((counts.high || 0) >= 1 || score >= 21) return 'WARNING'; return 'PASS'; } /** * Map a risk score to a human-readable band. * @param {number} score * @returns {'Low' | 'Medium' | 'High' | 'Critical' | 'Extreme'} */ export function riskBand(score) { if (score <= 10) return 'Low'; if (score <= 30) return 'Medium'; if (score <= 60) return 'High'; if (score <= 80) return 'Critical'; return 'Extreme'; } /** * Grade from a quality pass rate (0-100%). * @param {number} passRate - 0-100 * @returns {'A' | 'B' | 'C' | 'D' | 'F'} */ export function gradeFromPassRate(passRate) { if (passRate >= 90) return 'A'; if (passRate >= 75) return 'B'; if (passRate >= 60) return 'C'; if (passRate >= 40) return 'D'; return 'F'; } /** Config audit quality categories */ export const QUALITY_CATEGORIES = Object.freeze({ STRUCTURE: 'Structure & Format', CONTENT: 'Content Quality', HIERARCHY: 'Hierarchy & Scope', SECURITY: 'Security', FEATURES: 'Feature Utilization', COHERENCE: 'Cross-file Coherence', });