#!/usr/bin/env node // ═══════════════════════════════════════════════════════════════════════════ // Evidence Quality — Normaliserad och uppdelad i delkomponenter // ═══════════════════════════════════════════════════════════════════════════ /** * Evidence Quality Metrics * * Delas upp i: * - Completeness: Har vi all nödvändig evidens? * - Relevance: Är evidensen relevant för operationen? * - Consistency: Stämmer evidensen överens? * - Freshness: Hur aktuell är evidensen? * * Alla mäts på skalan 0-100 och viktas till ett totalt score. */ const WEIGHTS = { completeness: 0.35, relevance: 0.35, consistency: 0.20, freshness: 0.10 }; /** * Beräkna Evidence Completeness * * Hur många av de nödvändiga evidenspunkterna har vi? */ function calculateCompleteness(evidence, operation) { const requirements = { REMOTE_PRODUCTION_ACCESS: ['targetEnvironment', 'accessMethod'], MODIFY_PERSISTENT_PRODUCTION_DATA: ['targetEnvironment', 'dataOperation'], MODIFY_PRODUCTION_INFRASTRUCTURE: ['targetEnvironment', 'infraOperation'], PRODUCTION_RELEASE: ['targetEnvironment', 'releaseOperation'], EXPOSE_OR_CREATE_SECRET: ['secretPattern'], READ_PRODUCTION_DATA: ['readOperation'], OBSERVE_INFRASTRUCTURE: ['observeOperation'], DEVELOPMENT_TASK: [], UNKNOWN_OPERATION: [] }; const required = requirements[operation] || []; if (required.length === 0) return 100; // Ingen evidens krävs let found = 0; for (const req of required) { if (evidence[req] !== null && evidence[req] !== false && evidence[req] !== undefined) { found++; } } return Math.round((found / required.length) * 100); } /** * Beräkna Evidence Relevance * * Hur relevant är evidensen för den identifierade operationen? */ function calculateRelevance(evidence, operation, candidates) { let score = 0; // Om vi har en stark kandidat som matchar operationen const matchingCandidate = candidates.find(c => c.operation === operation); if (matchingCandidate) { score += Math.round(matchingCandidate.confidence * 50); } // Om targetEnvironment är produktion och operation är produktionsrelaterad const productionOps = ['REMOTE_PRODUCTION_ACCESS', 'MODIFY_PERSISTENT_PRODUCTION_DATA', 'MODIFY_PRODUCTION_INFRASTRUCTURE', 'PRODUCTION_RELEASE']; if (evidence.targetEnvironment === 'production' && productionOps.includes(operation)) { score += 30; } // Om vi har direkta kommandon som matchar if (evidence.accessMethod && operation === 'REMOTE_PRODUCTION_ACCESS') score += 20; if (evidence.dataOperation && operation === 'MODIFY_PERSISTENT_PRODUCTION_DATA') score += 20; if (evidence.infraOperation && operation === 'MODIFY_PRODUCTION_INFRASTRUCTURE') score += 20; if (evidence.releaseOperation && operation === 'PRODUCTION_RELEASE') score += 20; if (evidence.secretPattern && operation === 'EXPOSE_OR_CREATE_SECRET') score += 20; return Math.min(score, 100); } /** * Beräkna Evidence Consistency * * Stämmer evidensen överens med sig själv? */ function calculateConsistency(evidence, candidates) { let score = 100; // Om vi har flera kandidater med liknande konfidens, minska score if (candidates.length >= 2) { const diff = candidates[0].confidence - candidates[1].confidence; if (diff < 0.2) { score -= 30; // Osäkerhet mellan toppkandidater } } // Om evidensen är motstridig if (evidence.targetEnvironment === 'production' && evidence.readOperation) { score -= 20; // Produktion + läsning kan vara OK men är värt att flagga } return Math.max(score, 0); } /** * Beräkna Evidence Freshness * * Hur aktuell är evidensen? (Placeholder — kan utökas med tidsstämplar) */ function calculateFreshness(evidence) { // För närvarande: anta att all evidens är färsk // I framtiden: jämför med tidsstämplar från git, deployment, etc. return 100; } /** * Beräkna total Evidence Quality */ function calculateEvidenceQuality(evidence, operation, candidates) { const completeness = calculateCompleteness(evidence, operation); const relevance = calculateRelevance(evidence, operation, candidates); const consistency = calculateConsistency(evidence, candidates); const freshness = calculateFreshness(evidence); const total = Math.round( completeness * WEIGHTS.completeness + relevance * WEIGHTS.relevance + consistency * WEIGHTS.consistency + freshness * WEIGHTS.freshness ); return { total: Math.min(total, 100), // Normalisera till max 100 components: { completeness, relevance, consistency, freshness } }; } export { calculateEvidenceQuality, calculateCompleteness, calculateRelevance, calculateConsistency, calculateFreshness };