UNPKG

claude-flow-tbowman01

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

910 lines 37.7 kB
import { EventEmitter } from 'events'; import { writeFile, readFile, mkdir, readdir } from 'fs/promises'; import { join } from 'path'; import { spawn } from 'child_process'; import { Logger } from '../core/logger.js'; import { ConfigManager } from '../core/config.js'; export class SecurityManager extends EventEmitter { scans = new Map(); policies = new Map(); incidents = new Map(); vulnerabilityDatabases = new Map(); securityPath; logger; config; constructor(securityPath = './security', logger, config) { super(); this.securityPath = securityPath; this.logger = logger || new Logger({ level: 'info', format: 'text', destination: 'console' }); this.config = config || ConfigManager.getInstance(); } async initialize() { try { await mkdir(this.securityPath, { recursive: true }); await mkdir(join(this.securityPath, 'scans'), { recursive: true }); await mkdir(join(this.securityPath, 'policies'), { recursive: true }); await mkdir(join(this.securityPath, 'incidents'), { recursive: true }); await mkdir(join(this.securityPath, 'reports'), { recursive: true }); await mkdir(join(this.securityPath, 'databases'), { recursive: true }); await this.loadConfigurations(); await this.initializeDefaultPolicies(); await this.initializeVulnerabilityDatabases(); this.logger.info('Security Manager initialized successfully'); } catch (error) { this.logger.error('Failed to initialize Security Manager', { error }); throw error; } } async createSecurityScan(scanData) { const scan = { id: `scan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, name: scanData.name, type: scanData.type, status: 'pending', projectId: scanData.projectId, target: scanData.target, configuration: { scanner: this.getDefaultScanner(scanData.type), rules: [], excludes: [], severity: ['critical', 'high', 'medium', 'low'], formats: ['json', 'html'], outputPath: join(this.securityPath, 'reports'), ...scanData.configuration, }, results: [], metrics: { totalFindings: 0, criticalFindings: 0, highFindings: 0, mediumFindings: 0, lowFindings: 0, falsePositives: 0, suppressed: 0, scanDuration: 0, filesScanned: 0, linesScanned: 0, }, compliance: { frameworks: [], requirements: [], overallScore: 0, passedChecks: 0, failedChecks: 0, }, remediation: { autoFixAvailable: [], manualReview: [], recommendations: [], }, schedule: scanData.schedule, notifications: { channels: [], thresholds: { critical: 1, high: 5, medium: 10, }, }, createdAt: new Date(), updatedAt: new Date(), createdBy: 'system', auditLog: [], }; this.addAuditEntry(scan, 'system', 'scan_created', 'scan', { scanId: scan.id, scanName: scan.name, scanType: scan.type, }); this.scans.set(scan.id, scan); await this.saveScan(scan); this.emit('scan:created', scan); this.logger.info(`Security scan created: ${scan.name} (${scan.id})`); return scan; } async executeScan(scanId) { const scan = this.scans.get(scanId); if (!scan) { throw new Error(`Scan not found: ${scanId}`); } if (scan.status !== 'pending') { throw new Error(`Scan ${scanId} is not in pending status`); } scan.status = 'running'; scan.updatedAt = new Date(); this.addAuditEntry(scan, 'system', 'scan_started', 'scan', { scanId, target: scan.target, }); await this.saveScan(scan); this.emit('scan:started', scan); try { const startTime = Date.now(); // Execute the appropriate scanner const findings = await this.executeScanEngine(scan); const endTime = Date.now(); scan.metrics.scanDuration = endTime - startTime; scan.results = findings; scan.status = 'completed'; // Calculate metrics this.calculateScanMetrics(scan); // Run compliance checks await this.runComplianceChecks(scan); // Generate remediation recommendations await this.generateRemediationRecommendations(scan); // Check notification thresholds await this.checkNotificationThresholds(scan); scan.updatedAt = new Date(); this.addAuditEntry(scan, 'system', 'scan_completed', 'scan', { scanId, duration: scan.metrics.scanDuration, findingsCount: scan.results.length, }); await this.saveScan(scan); this.emit('scan:completed', scan); this.logger.info(`Security scan completed: ${scan.name} (${scan.id}) - ${scan.results.length} findings`); } catch (error) { scan.status = 'failed'; scan.updatedAt = new Date(); this.addAuditEntry(scan, 'system', 'scan_failed', 'scan', { scanId, error: error instanceof Error ? error.message : String(error), }); await this.saveScan(scan); this.emit('scan:failed', { scan, error }); this.logger.error(`Security scan failed: ${scan.name} (${scanId})`, { error }); throw error; } } async createSecurityIncident(incidentData) { const incident = { id: `incident-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, title: incidentData.title, description: incidentData.description, severity: incidentData.severity, status: 'open', type: incidentData.type, source: incidentData.source, affected: { systems: [], data: [], users: [], ...incidentData.affected, }, timeline: { detected: new Date(), reported: new Date(), acknowledged: new Date(), }, response: { assignedTo: [], actions: [], communications: [], lessons: [], }, evidence: { logs: [], files: [], screenshots: [], forensics: [], }, impact: { confidentiality: 'none', integrity: 'none', availability: 'none', }, rootCause: { primary: '', contributing: [], analysis: '', }, remediation: { immediate: [], shortTerm: [], longTerm: [], preventive: [], }, createdAt: new Date(), updatedAt: new Date(), createdBy: 'system', auditLog: [], }; this.addAuditEntry(incident, 'system', 'incident_created', 'incident', { incidentId: incident.id, severity: incident.severity, type: incident.type, }); this.incidents.set(incident.id, incident); await this.saveIncident(incident); // Auto-assign based on severity and type await this.autoAssignIncident(incident); // Send immediate notifications for high/critical incidents if (incident.severity === 'critical' || incident.severity === 'high') { await this.sendIncidentNotification(incident); } this.emit('incident:created', incident); this.logger.info(`Security incident created: ${incident.title} (${incident.id})`); return incident; } async updateIncident(incidentId, updates, userId = 'system') { const incident = this.incidents.get(incidentId); if (!incident) { throw new Error(`Incident not found: ${incidentId}`); } const oldStatus = incident.status; Object.assign(incident, updates); incident.updatedAt = new Date(); // Update timeline based on status changes if (updates.status && updates.status !== oldStatus) { this.updateIncidentTimeline(incident, updates.status); } this.addAuditEntry(incident, userId, 'incident_updated', 'incident', { incidentId, changes: Object.keys(updates), oldStatus, newStatus: incident.status, }); await this.saveIncident(incident); this.emit('incident:updated', { incident, updates }); this.logger.info(`Security incident updated: ${incident.title} (${incidentId})`); return incident; } async runComplianceAssessment(frameworks, scope) { const checks = []; for (const framework of frameworks) { const frameworkChecks = await this.runFrameworkChecks(framework, scope); checks.push(...frameworkChecks); } this.logger.info(`Compliance assessment completed: ${checks.length} checks across ${frameworks.length} frameworks`); this.emit('compliance:assessed', { frameworks, checks, scope }); return checks; } async createSecurityPolicy(policyData) { const policy = { id: `policy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, name: policyData.name, description: policyData.description, type: policyData.type, version: '1.0.0', status: 'draft', rules: policyData.rules.map((rule) => ({ id: `rule-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, ...rule, })), enforcement: { level: 'warning', exceptions: [], approvers: [], ...policyData.enforcement, }, applicability: { projects: [], environments: [], resources: [], ...policyData.applicability, }, schedule: { reviewFrequency: 'annually', nextReview: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year reviewer: 'security-team', }, metrics: { violations: 0, compliance: 100, exceptions: 0, }, createdAt: new Date(), updatedAt: new Date(), createdBy: 'system', }; this.policies.set(policy.id, policy); await this.savePolicy(policy); this.emit('policy:created', policy); this.logger.info(`Security policy created: ${policy.name} (${policy.id})`); return policy; } async getSecurityMetrics(filters) { let scans = Array.from(this.scans.values()); let incidents = Array.from(this.incidents.values()); // Apply filters if (filters) { if (filters.timeRange) { scans = scans.filter((s) => s.createdAt >= filters.timeRange.start && s.createdAt <= filters.timeRange.end); incidents = incidents.filter((i) => i.createdAt >= filters.timeRange.start && i.createdAt <= filters.timeRange.end); } if (filters.projectId) { scans = scans.filter((s) => s.projectId === filters.projectId); } } // Calculate scan metrics const scanMetrics = { total: scans.length, completed: scans.filter((s) => s.status === 'completed').length, failed: scans.filter((s) => s.status === 'failed').length, inProgress: scans.filter((s) => s.status === 'running').length, byType: this.groupBy(scans, 'type'), averageDuration: scans.length > 0 ? scans.reduce((sum, s) => sum + s.metrics.scanDuration, 0) / scans.length : 0, }; // Calculate finding metrics const allFindings = scans.flatMap((s) => s.results); const findingMetrics = { total: allFindings.length, open: allFindings.filter((f) => f.status === 'open').length, resolved: allFindings.filter((f) => f.status === 'resolved').length, suppressed: allFindings.filter((f) => f.status === 'suppressed').length, bySeverity: this.groupBy(allFindings, 'severity'), byCategory: this.groupBy(allFindings, 'category'), meanTimeToResolution: this.calculateMTTR(allFindings), }; // Calculate compliance metrics const allComplianceChecks = scans.flatMap((s) => s.compliance.requirements); const complianceFrameworks = {}; for (const check of allComplianceChecks) { if (!complianceFrameworks[check.framework]) { complianceFrameworks[check.framework] = { total: 0, passed: 0, failed: 0, score: 0, }; } complianceFrameworks[check.framework].total++; if (check.status === 'passed') { complianceFrameworks[check.framework].passed++; } else if (check.status === 'failed') { complianceFrameworks[check.framework].failed++; } } // Calculate scores for (const framework in complianceFrameworks) { const fw = complianceFrameworks[framework]; fw.score = fw.total > 0 ? (fw.passed / fw.total) * 100 : 0; } const overallComplianceScore = Object.values(complianceFrameworks).length > 0 ? Object.values(complianceFrameworks).reduce((sum, fw) => sum + fw.score, 0) / Object.values(complianceFrameworks).length : 0; // Calculate incident metrics const incidentMetrics = { total: incidents.length, open: incidents.filter((i) => i.status === 'open' || i.status === 'investigating').length, resolved: incidents.filter((i) => i.status === 'resolved' || i.status === 'closed').length, bySeverity: this.groupBy(incidents, 'severity'), meanTimeToDetection: this.calculateMTTD(incidents), meanTimeToResponse: this.calculateMTTResponse(incidents), meanTimeToResolution: this.calculateIncidentMTTR(incidents), }; // Policy metrics const policies = Array.from(this.policies.values()); const policyMetrics = { total: policies.length, active: policies.filter((p) => p.status === 'active').length, violations: policies.reduce((sum, p) => sum + p.metrics.violations, 0), compliance: policies.length > 0 ? policies.reduce((sum, p) => sum + p.metrics.compliance, 0) / policies.length : 0, }; return { scans: scanMetrics, findings: findingMetrics, compliance: { frameworks: complianceFrameworks, overallScore: overallComplianceScore, trending: 'stable', // Would be calculated from historical data }, incidents: incidentMetrics, policies: policyMetrics, trends: { findingsTrend: [], // Would be calculated from historical data complianceTrend: [], // Would be calculated from historical data incidentsTrend: [], // Would be calculated from historical data }, }; } // Private helper methods async loadConfigurations() { try { // Load scans const scanFiles = await readdir(join(this.securityPath, 'scans')); for (const file of scanFiles.filter((f) => f.endsWith('.json'))) { const content = await readFile(join(this.securityPath, 'scans', file), 'utf-8'); const scan = JSON.parse(content); this.scans.set(scan.id, scan); } // Load policies const policyFiles = await readdir(join(this.securityPath, 'policies')); for (const file of policyFiles.filter((f) => f.endsWith('.json'))) { const content = await readFile(join(this.securityPath, 'policies', file), 'utf-8'); const policy = JSON.parse(content); this.policies.set(policy.id, policy); } // Load incidents const incidentFiles = await readdir(join(this.securityPath, 'incidents')); for (const file of incidentFiles.filter((f) => f.endsWith('.json'))) { const content = await readFile(join(this.securityPath, 'incidents', file), 'utf-8'); const incident = JSON.parse(content); this.incidents.set(incident.id, incident); } this.logger.info(`Loaded ${this.scans.size} scans, ${this.policies.size} policies, ${this.incidents.size} incidents`); } catch (error) { this.logger.warn('Failed to load some security configurations', { error }); } } async initializeDefaultPolicies() { const defaultPolicies = [ { name: 'Critical Vulnerability Policy', description: 'Immediate action required for critical vulnerabilities', type: 'scanning', rules: [ { name: 'Critical CVSS Score', description: 'Alert on vulnerabilities with CVSS score >= 9.0', condition: 'cvss.score >= 9.0', action: 'alert', severity: 'critical', parameters: { threshold: 9.0 }, enabled: true, }, ], enforcement: { level: 'blocking', exceptions: [], approvers: ['security-lead'], }, }, { name: 'Secret Detection Policy', description: 'Detect exposed secrets and credentials', type: 'scanning', rules: [ { name: 'API Key Detection', description: 'Detect exposed API keys', condition: 'category == "secret" && type == "api-key"', action: 'deny', severity: 'high', parameters: {}, enabled: true, }, ], }, ]; for (const policyData of defaultPolicies) { if (!Array.from(this.policies.values()).some((p) => p.name === policyData.name)) { await this.createSecurityPolicy(policyData); } } } async initializeVulnerabilityDatabases() { const databases = [ { id: 'nvd', name: 'National Vulnerability Database', type: 'nvd', url: 'https://nvd.nist.gov/feeds/json/cve/1.1/', updateFrequency: 'daily', lastUpdate: new Date(), status: 'active', configuration: {}, }, { id: 'github-advisories', name: 'GitHub Security Advisories', type: 'github', url: 'https://api.github.com/advisories', updateFrequency: 'daily', lastUpdate: new Date(), status: 'active', configuration: {}, }, ]; for (const db of databases) { this.vulnerabilityDatabases.set(db.id, db); } } getDefaultScanner(type) { const scanners = { vulnerability: 'trivy', dependency: 'npm-audit', 'code-quality': 'sonarqube', secrets: 'gitleaks', compliance: 'inspec', infrastructure: 'checkov', container: 'clair', }; return scanners[type] || 'generic'; } async executeScanEngine(scan) { const findings = []; switch (scan.configuration.scanner) { case 'trivy': return this.executeTrivyScan(scan); case 'npm-audit': return this.executeNpmAuditScan(scan); case 'gitleaks': return this.executeGitleaksScan(scan); case 'checkov': return this.executeCheckovScan(scan); default: return this.executeGenericScan(scan); } } async executeTrivyScan(scan) { return new Promise((resolve, reject) => { const findings = []; // Mock Trivy execution const mockFindings = [ { id: `finding-${Date.now()}-1`, title: 'CVE-2023-12345: Remote Code Execution in libxml2', description: 'A buffer overflow vulnerability in libxml2 allows remote code execution', severity: 'critical', category: 'vulnerability', cve: 'CVE-2023-12345', cvss: { score: 9.8, vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H', version: '3.1', }, location: { file: 'package-lock.json', line: 125, component: 'libxml2@2.9.10', }, evidence: { snippet: '"libxml2": "2.9.10"', context: 'Dependency declaration', references: ['https://nvd.nist.gov/vuln/detail/CVE-2023-12345'], }, impact: 'Remote attackers could execute arbitrary code', remediation: { description: 'Update libxml2 to version 2.9.14 or later', effort: 'low', priority: 'critical', autoFixable: true, steps: ['npm update libxml2'], references: ['https://github.com/GNOME/libxml2/releases'], }, status: 'open', tags: ['cve', 'rce', 'dependency'], metadata: {}, firstSeen: new Date(), lastSeen: new Date(), occurrences: 1, }, ]; // Simulate scan delay setTimeout(() => { resolve(mockFindings); }, 2000); }); } async executeNpmAuditScan(scan) { return new Promise((resolve, reject) => { const command = 'npm'; const args = ['audit', '--json']; const child = spawn(command, args, { cwd: scan.target.path, stdio: ['pipe', 'pipe', 'pipe'], }); let stdout = ''; let stderr = ''; child.stdout?.on('data', (data) => { stdout += data.toString(); }); child.stderr?.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { try { const auditResult = JSON.parse(stdout); const findings = this.parseNpmAuditResults(auditResult); resolve(findings); } catch (error) { reject(new Error(`Failed to parse npm audit results: ${error instanceof Error ? error.message : String(error)}`)); } }); child.on('error', (error) => { reject(error); }); }); } async executeGitleaksScan(scan) { // Mock Gitleaks scan for secrets detection return [ { id: `finding-${Date.now()}-2`, title: 'Exposed AWS Access Key', description: 'AWS access key found in source code', severity: 'high', category: 'secret', location: { file: 'config/aws.js', line: 12, column: 20, }, evidence: { snippet: 'const accessKey = "AKIA123456789..."', context: 'Hardcoded AWS credentials', }, impact: 'Unauthorized access to AWS resources', remediation: { description: 'Remove hardcoded credentials and use environment variables or IAM roles', effort: 'medium', priority: 'high', autoFixable: false, steps: [ 'Remove hardcoded credentials', 'Use environment variables', 'Rotate compromised keys', ], references: ['https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'], }, status: 'open', tags: ['secret', 'aws', 'credentials'], metadata: {}, firstSeen: new Date(), lastSeen: new Date(), occurrences: 1, }, ]; } async executeCheckovScan(scan) { // Mock Checkov scan for infrastructure as code return []; } async executeGenericScan(scan) { // Generic scan implementation return []; } parseNpmAuditResults(auditResult) { const findings = []; if (auditResult.vulnerabilities) { for (const [packageName, vulnData] of Object.entries(auditResult.vulnerabilities)) { const vuln = vulnData; findings.push({ id: `finding-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, title: `${vuln.severity} vulnerability in ${packageName}`, description: vuln.title || 'Vulnerability detected', severity: vuln.severity, category: 'vulnerability', cve: vuln.cve, location: { file: 'package.json', component: packageName, }, evidence: { snippet: `"${packageName}": "${vuln.range}"`, references: vuln.url ? [vuln.url] : [], }, impact: vuln.overview || 'Security vulnerability', remediation: { description: vuln.recommendation || 'Update to a secure version', effort: 'low', priority: vuln.severity === 'info' ? 'low' : vuln.severity, autoFixable: true, steps: [`npm update ${packageName}`], references: vuln.url ? [vuln.url] : [], }, status: 'open', tags: ['npm', 'dependency'], metadata: { packageName, range: vuln.range }, firstSeen: new Date(), lastSeen: new Date(), occurrences: 1, }); } } return findings; } calculateScanMetrics(scan) { const findings = scan.results; scan.metrics.totalFindings = findings.length; scan.metrics.criticalFindings = findings.filter((f) => f.severity === 'critical').length; scan.metrics.highFindings = findings.filter((f) => f.severity === 'high').length; scan.metrics.mediumFindings = findings.filter((f) => f.severity === 'medium').length; scan.metrics.lowFindings = findings.filter((f) => f.severity === 'low').length; scan.metrics.falsePositives = findings.filter((f) => f.status === 'false-positive').length; scan.metrics.suppressed = findings.filter((f) => f.status === 'suppressed').length; } async runComplianceChecks(scan) { // Mock compliance checks const frameworks = ['SOC2', 'GDPR', 'PCI-DSS']; for (const framework of frameworks) { const checks = await this.runFrameworkChecks(framework, { projectId: scan.projectId }); scan.compliance.requirements.push(...checks); } scan.compliance.frameworks = frameworks; scan.compliance.passedChecks = scan.compliance.requirements.filter((r) => r.status === 'passed').length; scan.compliance.failedChecks = scan.compliance.requirements.filter((r) => r.status === 'failed').length; scan.compliance.overallScore = scan.compliance.requirements.length > 0 ? (scan.compliance.passedChecks / scan.compliance.requirements.length) * 100 : 0; } async runFrameworkChecks(framework, scope) { // Mock compliance checks for different frameworks const mockChecks = [ { id: `check-${Date.now()}-1`, framework, control: 'CC6.1', description: 'Encryption in transit', status: 'passed', severity: 'high', evidence: 'TLS 1.2+ configured', lastChecked: new Date(), }, { id: `check-${Date.now()}-2`, framework, control: 'CC6.7', description: 'Encryption at rest', status: 'failed', severity: 'medium', remediation: 'Enable database encryption', lastChecked: new Date(), }, ]; return mockChecks; } async generateRemediationRecommendations(scan) { const autoFixable = scan.results.filter((f) => f.remediation.autoFixable); const manualReview = scan.results.filter((f) => !f.remediation.autoFixable); scan.remediation.autoFixAvailable = autoFixable; scan.remediation.manualReview = manualReview; // Generate general recommendations scan.remediation.recommendations = [ { id: `rec-${Date.now()}-1`, title: 'Implement Automated Dependency Updates', description: 'Set up automated dependency updates to reduce vulnerability exposure', category: 'vulnerability-management', priority: 'high', effort: 'medium', impact: 'Reduces time to patch vulnerabilities', implementation: { steps: [ 'Configure Dependabot or Renovate', 'Set up automated testing pipeline', 'Enable auto-merge for low-risk updates', ], tools: ['Dependabot', 'Renovate', 'GitHub Actions'], timeEstimate: '2-4 hours', cost: 'Free', }, references: [ 'https://docs.github.com/en/code-security/dependabot', 'https://renovatebot.com/', ], applicableFrameworks: ['SOC2', 'ISO27001'], }, ]; } async checkNotificationThresholds(scan) { const thresholds = scan.notifications.thresholds; if (scan.metrics.criticalFindings >= thresholds.critical || scan.metrics.highFindings >= thresholds.high || scan.metrics.mediumFindings >= thresholds.medium) { await this.sendScanNotification(scan); } } async sendScanNotification(scan) { const message = `Security scan '${scan.name}' completed with ${scan.metrics.totalFindings} findings (${scan.metrics.criticalFindings} critical, ${scan.metrics.highFindings} high)`; this.emit('notification:scan', { scan, message, severity: scan.metrics.criticalFindings > 0 ? 'critical' : scan.metrics.highFindings > 0 ? 'high' : 'medium', }); this.logger.warn(message); } async autoAssignIncident(incident) { // Auto-assign based on severity and type const assignmentRules = { critical: ['security-lead', 'ciso'], high: ['security-team'], medium: ['security-analyst'], low: ['security-analyst'], }; incident.response.assignedTo = assignmentRules[incident.severity] || ['security-team']; } async sendIncidentNotification(incident) { const message = `SECURITY INCIDENT: ${incident.title} (${incident.severity.toUpperCase()})`; this.emit('notification:incident', { incident, message, urgency: incident.severity === 'critical' ? 'immediate' : 'high', }); this.logger.error(message); } updateIncidentTimeline(incident, newStatus) { const now = new Date(); switch (newStatus) { case 'investigating': incident.timeline.acknowledged = now; break; case 'contained': incident.timeline.contained = now; break; case 'resolved': incident.timeline.resolved = now; break; case 'closed': incident.timeline.closed = now; break; } } async saveScan(scan) { const filePath = join(this.securityPath, 'scans', `${scan.id}.json`); await writeFile(filePath, JSON.stringify(scan, null, 2)); } async savePolicy(policy) { const filePath = join(this.securityPath, 'policies', `${policy.id}.json`); await writeFile(filePath, JSON.stringify(policy, null, 2)); } async saveIncident(incident) { const filePath = join(this.securityPath, 'incidents', `${incident.id}.json`); await writeFile(filePath, JSON.stringify(incident, null, 2)); } addAuditEntry(target, userId, action, targetType, details) { const entry = { id: `audit-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, timestamp: new Date(), userId, action, target: targetType, details, }; target.auditLog.push(entry); } groupBy(array, key) { return array.reduce((groups, item) => { const value = String(item[key]); groups[value] = (groups[value] || 0) + 1; return groups; }, {}); } calculateMTTR(findings) { const resolvedFindings = findings.filter((f) => f.status === 'resolved' && f.firstSeen && f.lastSeen); if (resolvedFindings.length === 0) return 0; const totalTime = resolvedFindings.reduce((sum, f) => sum + (f.lastSeen.getTime() - f.firstSeen.getTime()), 0); return totalTime / resolvedFindings.length; } calculateMTTD(incidents) { const detectedIncidents = incidents.filter((i) => i.timeline.detected && i.timeline.reported); if (detectedIncidents.length === 0) return 0; const totalTime = detectedIncidents.reduce((sum, i) => sum + (i.timeline.reported.getTime() - i.timeline.detected.getTime()), 0); return totalTime / detectedIncidents.length; } calculateMTTResponse(incidents) { const respondedIncidents = incidents.filter((i) => i.timeline.reported && i.timeline.acknowledged); if (respondedIncidents.length === 0) return 0; const totalTime = respondedIncidents.reduce((sum, i) => sum + (i.timeline.acknowledged.getTime() - i.timeline.reported.getTime()), 0); return totalTime / respondedIncidents.length; } calculateIncidentMTTR(incidents) { const resolvedIncidents = incidents.filter((i) => i.timeline.reported && i.timeline.resolved); if (resolvedIncidents.length === 0) return 0; const totalTime = resolvedIncidents.reduce((sum, i) => sum + (i.timeline.resolved.getTime() - i.timeline.reported.getTime()), 0); return totalTime / resolvedIncidents.length; } } //# sourceMappingURL=security-manager.js.map