#!/usr/bin/env node // ═══════════════════════════════════════════════════════════════════════════ // EOS Integration — Gör EOS obligatoriskt i agent-flödet // ═══════════════════════════════════════════════════════════════════════════ import { readFileSync } from 'fs'; /** * EOS Integration säkerställer att: * 1. Ingen agent kan hoppa över EOS-policyer * 2. Varje förändring loggas * 3. Blockeringar är tydliga och motiverade * 4. Undantag kräver mänsklig granskning */ class EOSIntegration { constructor() { this.violations = []; this.exemptions = []; } /** * Kör EOS-policyer för en förändring */ async check(change) { const checks = [ this.checkNoHardcodedSecrets(change), this.checkNoDirectProd(change), this.checkTestsRequired(change), this.checkGitRequired(change), this.checkNoUncommittedChanges(change), this.checkIaCRequired(change) ]; const results = await Promise.all(checks); const failed = results.filter(r => !r.passed); if (failed.length > 0) { return { passed: false, blocked: true, reason: 'EOS Policy Check failed', failures: failed, action: 'Fix failures or request exemption' }; } return { passed: true, blocked: false, reason: 'All checks passed', results }; } async checkNoHardcodedSecrets(change) { const secrets = this.findSecrets(change); return { rule: 'no-hardcoded-secrets', passed: secrets.length === 0, evidence: secrets.length > 0 ? `Found secrets: ${secrets.join(', ')}` : 'No secrets found', severity: 'CRITICAL' }; } async checkNoDirectProd(change) { const isProd = change.target?.includes('production'); const hasPipeline = change.pipeline !== undefined; return { rule: 'no-direct-prod', passed: !isProd || hasPipeline, evidence: isProd && !hasPipeline ? 'Production change without pipeline' : 'OK', severity: 'HIGH' }; } async checkTestsRequired(change) { const hasTests = change.tests?.length > 0; const isConfig = change.type === 'config'; return { rule: 'test-required', passed: hasTests || isConfig, evidence: !hasTests && !isConfig ? 'No tests for code change' : 'OK', severity: 'MEDIUM' }; } async checkGitRequired(change) { const hasGit = change.git?.commit !== undefined; return { rule: 'git-required', passed: hasGit, evidence: !hasGit ? 'No git commit' : 'OK', severity: 'HIGH' }; } async checkNoUncommittedChanges(change) { // TODO: Kolla git status return { rule: 'no-uncommitted-changes', passed: true, // TODO: Verklig kontroll evidence: 'TODO', severity: 'MEDIUM' }; } async checkIaCRequired(change) { const isInfrastructure = change.type === 'infrastructure'; const hasTerraform = change.terraform !== undefined; return { rule: 'iac-required', passed: !isInfrastructure || hasTerraform, evidence: isInfrastructure && !hasTerraform ? 'Infrastructure change without Terraform' : 'OK', severity: 'HIGH' }; } findSecrets(change) { const secrets = []; const patterns = [ /password\s*=\s*["'][^"']+["']/i, /secret\s*=\s*["'][^"']+["']/i, /token\s*=\s*["'][^"']+["']/i, /api_key\s*=\s*["'][^"']+["']/i, /JWT_SECRET\s*=\s*["'][^"']+["']/i ]; for (const file of change.files || []) { for (const pattern of patterns) { if (pattern.test(file.content)) { secrets.push(file.path); break; } } } return [...new Set(secrets)]; } /** * Begär undantag från EOS-regel */ requestExemption(rule, reason, requester) { const exemption = { id: `exemption-${Date.now()}`, rule, reason, requester, requestedAt: new Date().toISOString(), status: 'pending', approvedBy: null, approvedAt: null }; this.exemptions.push(exemption); return exemption; } /** * Godkänn undantag */ approveExemption(exemptionId, approver) { const exemption = this.exemptions.find(e => e.id === exemptionId); if (exemption) { exemption.status = 'approved'; exemption.approvedBy = approver; exemption.approvedAt = new Date().toISOString(); } return exemption; } } // CLI if (process.argv[1] === new URL(import.meta.url).pathname) { const eos = new EOSIntegration(); // Testa med en förändring eos.check({ id: 'test-change', type: 'code', files: [ { path: 'test.mjs', content: 'const x = 1;' } ], git: { commit: 'abc123' }, tests: ['test.mjs'] }).then(result => { console.log(JSON.stringify(result, null, 2)); }); } export { EOSIntegration };