Files
boc/EOS/evidence-quality.mjs
T

150 lines
5.0 KiB
JavaScript
Raw Normal View History

#!/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 };