UNPKG

recoder-security

Version:

Enterprise-grade security and compliance layer for CodeCraft CLI

922 lines 33.9 kB
"use strict"; /** * Comprehensive Audit Trail System * Provides complete security event logging, forensic analysis, and compliance reporting * with tamper-proof logging and real-time monitoring capabilities */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuditTrail = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const crypto_1 = __importDefault(require("crypto")); const events_1 = require("events"); const shared_1 = require("@recoder/shared"); class AuditTrail extends events_1.EventEmitter { constructor(config) { super(); this.logger = new shared_1.Logger('AuditTrail'); this.eventBuffer = []; this.alertQueue = []; this.lastEventHash = ''; this.config = { enabled: true, realTimeAlerts: true, storageBackend: 'file', retention: { default: 2555, // ~7 years security: 3650, // 10 years compliance: 2555, // 7 years admin: 1825, // 5 years }, encryption: { enabled: true, algorithm: 'aes-256-gcm', keyRotationDays: 90, }, integrity: { enabled: true, hashChain: true, digitalSignature: true, }, alertThresholds: { criticalEvents: 5, failureRate: 0.1, // 10% suspiciousActivity: 10, }, export: { formats: ['json', 'csv'], compression: true, encryption: true, }, ...config, }; this.initializeSecurityKeys(); this.setupEventProcessing(); this.startPeriodicTasks(); } /** * Log security event to audit trail */ async logEvent(event) { if (!this.config.enabled) { return ''; } const eventId = this.generateEventId(); const timestamp = Date.now(); // Create complete audit event const auditEvent = { id: eventId, timestamp, integrity: { hash: '', previousHash: this.lastEventHash, }, ...event, }; // Calculate integrity hash auditEvent.integrity.hash = this.calculateEventHash(auditEvent); this.lastEventHash = auditEvent.integrity.hash; // Add digital signature if enabled if (this.config.integrity.digitalSignature && this.signingKey) { auditEvent.integrity.signature = this.signEvent(auditEvent); } // Add to buffer for batch processing this.eventBuffer.push(auditEvent); // Emit event for real-time processing this.emit('auditEvent', auditEvent); // Check for immediate threats await this.analyzeEventForThreats(auditEvent); // Flush buffer if it's getting full if (this.eventBuffer.length >= 100) { await this.flushEventBuffer(); } this.logger.debug(`Logged audit event: ${event.action} by ${event.actor.id}`); return eventId; } /** * Query audit events with filtering and pagination */ async queryEvents(query) { this.logger.info('Querying audit events', { query }); // For file-based storage, implement file scanning // For database storage, this would use SQL queries // For Elasticsearch, this would use search queries const events = await this.searchEvents(query); const total = events.length; const limit = query.limit || 100; const offset = query.offset || 0; const paginatedEvents = events.slice(offset, offset + limit); const hasMore = offset + limit < total; return { events: paginatedEvents, total, hasMore, }; } /** * Generate comprehensive audit report */ async generateReport(timeframe, title, description, generatedBy) { this.logger.info(`Generating audit report: ${title}`); const reportId = this.generateEventId(); const events = await this.queryEvents({ startTime: timeframe.start, endTime: timeframe.end, limit: 10000, // Large limit for comprehensive reports }); const summary = this.calculateEventSummary(events.events); const insights = this.generateEventInsights(events.events); const recommendations = this.generateRecommendations(events.events, insights); const complianceStatus = await this.assessComplianceStatus(events.events); const report = { id: reportId, title, description, timeframe, generatedAt: Date.now(), generatedBy, events: events.events, summary, insights, recommendations, complianceStatus, }; // Save report await this.saveReport(report); this.logger.info(`Generated audit report with ${events.events.length} events`); return report; } /** * Export audit data in various formats */ async exportData(query, format, options) { this.logger.info(`Exporting audit data in ${format} format`); const { events } = await this.queryEvents(query); let exportData; switch (format) { case 'json': exportData = JSON.stringify(events, null, 2); break; case 'csv': exportData = this.convertToCSV(events); break; case 'syslog': exportData = this.convertToSyslog(events); break; case 'cef': exportData = this.convertToCEF(events); break; default: throw new Error(`Unsupported export format: ${format}`); } // Apply compression if requested if (options?.compress) { exportData = await this.compressData(exportData); } // Apply encryption if requested if (options?.encrypt && this.encryptionKey) { exportData = await this.encryptData(exportData); } // Save to file const filename = options?.filename || `audit-export-${Date.now()}.${format}`; const exportDir = './audit-exports'; await fs_1.promises.mkdir(exportDir, { recursive: true }); const filePath = path_1.default.join(exportDir, filename); await fs_1.promises.writeFile(filePath, exportData); this.logger.info(`Exported ${events.length} events to ${filePath}`); return filePath; } /** * Verify audit trail integrity */ async verifyIntegrity(startTime, endTime) { this.logger.info('Verifying audit trail integrity'); const { events } = await this.queryEvents({ startTime, endTime, orderBy: 'timestamp', orderDirection: 'asc', limit: 10000, }); const issues = []; let validCount = 0; let previousHash = ''; for (const event of events) { // Verify hash const calculatedHash = this.calculateEventHash(event); if (calculatedHash !== event.integrity.hash) { issues.push({ eventId: event.id, issue: 'Hash mismatch - potential tampering detected', }); continue; } // Verify hash chain if (this.config.integrity.hashChain && previousHash) { if (event.integrity.previousHash !== previousHash) { issues.push({ eventId: event.id, issue: 'Hash chain broken - events may be out of order or missing', }); continue; } } // Verify digital signature if (this.config.integrity.digitalSignature && event.integrity.signature) { if (!this.verifyEventSignature(event)) { issues.push({ eventId: event.id, issue: 'Digital signature verification failed', }); continue; } } validCount++; previousHash = event.integrity.hash; } const result = { valid: issues.length === 0, issues, summary: { total: events.length, valid: validCount, invalid: issues.length, }, }; this.logger.info(`Integrity verification completed: ${validCount}/${events.length} events valid`); return result; } /** * Create security alert */ async createAlert(type, severity, title, description, events, indicators = []) { const alertId = this.generateEventId(); const alert = { id: alertId, timestamp: Date.now(), type, severity, title, description, events, indicators, mitigation: { automated: false, actions: [], status: 'pending', }, status: 'open', }; this.alertQueue.push(alert); this.emit('securityAlert', alert); // Log the alert as an audit event await this.logEvent({ eventType: 'security_incident', category: 'security', severity, source: 'audit-trail', actor: { type: 'system', id: 'audit-trail' }, action: 'security_alert_created', resource: { type: 'system', id: 'audit-trail' }, outcome: 'success', details: { description: `Security alert created: ${title}`, metadata: { alertId, type, indicators: indicators.length }, }, compliance: { frameworks: ['SOC2', 'ISO27001'], retention: this.config.retention.security, classification: 'sensitive', }, }); this.logger.warn(`Security alert created: ${title} (${severity})`, { alertId, type }); return alertId; } /** * Get audit statistics */ getStatistics(timeframe) { // This would calculate statistics from stored events // For now, return placeholder data return { totalEvents: 0, eventsToday: 0, criticalEvents: 0, failureRate: 0, activeAlerts: this.alertQueue.filter(a => a.status === 'open').length, topActors: [], topActions: [], complianceScore: 95, }; } /** * Initialize security keys for encryption and signing */ initializeSecurityKeys() { if (this.config.encryption.enabled) { // In production, load from secure key management service this.encryptionKey = crypto_1.default.randomBytes(32); } if (this.config.integrity.digitalSignature) { // In production, load from secure key management service this.signingKey = crypto_1.default.randomBytes(32); } } /** * Setup event processing pipeline */ setupEventProcessing() { // Process events in batches every 5 seconds setInterval(async () => { if (this.eventBuffer.length > 0) { await this.flushEventBuffer(); } }, 5000); // Process security alerts this.on('securityAlert', (alert) => { if (this.config.realTimeAlerts) { this.processSecurityAlert(alert); } }); } /** * Start periodic maintenance tasks */ startPeriodicTasks() { // Daily cleanup of old events setInterval(async () => { await this.cleanupOldEvents(); }, 24 * 60 * 60 * 1000); // Weekly integrity verification setInterval(async () => { await this.verifyIntegrity(); }, 7 * 24 * 60 * 60 * 1000); // Monthly key rotation setInterval(async () => { await this.rotateEncryptionKeys(); }, 30 * 24 * 60 * 60 * 1000); } /** * Flush event buffer to storage */ async flushEventBuffer() { if (this.eventBuffer.length === 0) { return; } const events = [...this.eventBuffer]; this.eventBuffer.length = 0; try { await this.persistEvents(events); this.logger.debug(`Flushed ${events.length} events to storage`); } catch (error) { this.logger.error('Failed to flush events to storage:', error); // Put events back in buffer this.eventBuffer.unshift(...events); } } /** * Persist events to storage backend */ async persistEvents(events) { switch (this.config.storageBackend) { case 'file': await this.persistToFile(events); break; case 'database': await this.persistToDatabase(events); break; case 'elasticsearch': await this.persistToElasticsearch(events); break; case 'cloudwatch': await this.persistToCloudWatch(events); break; default: throw new Error(`Unsupported storage backend: ${this.config.storageBackend}`); } } /** * Persist events to file system */ async persistToFile(events) { const auditDir = './audit-logs'; await fs_1.promises.mkdir(auditDir, { recursive: true }); // Group events by date for efficient storage const eventsByDate = new Map(); for (const event of events) { const date = new Date(event.timestamp).toISOString().split('T')[0]; const dayEvents = eventsByDate.get(date) || []; dayEvents.push(event); eventsByDate.set(date, dayEvents); } // Write events to daily log files for (const [date, dayEvents] of eventsByDate) { const filename = `audit-${date}.jsonl`; const filePath = path_1.default.join(auditDir, filename); let logData = ''; for (const event of dayEvents) { let eventData = JSON.stringify(event); // Encrypt if enabled if (this.config.encryption.enabled && this.encryptionKey) { eventData = await this.encryptData(eventData); } logData += eventData + '\n'; } await fs_1.promises.appendFile(filePath, logData); } } /** * Persist events to database */ async persistToDatabase(events) { // Implement database persistence // This would use SQL queries to insert events this.logger.debug(`Would persist ${events.length} events to database`); } /** * Persist events to Elasticsearch */ async persistToElasticsearch(events) { // Implement Elasticsearch persistence // This would use Elasticsearch bulk API this.logger.debug(`Would persist ${events.length} events to Elasticsearch`); } /** * Persist events to CloudWatch */ async persistToCloudWatch(events) { // Implement CloudWatch Logs persistence // This would use AWS SDK this.logger.debug(`Would persist ${events.length} events to CloudWatch`); } /** * Search events based on query */ async searchEvents(query) { // For file-based storage, scan log files // For database/Elasticsearch, use appropriate query mechanisms const auditDir = './audit-logs'; const events = []; try { const files = await fs_1.promises.readdir(auditDir); const logFiles = files.filter(f => f.startsWith('audit-') && f.endsWith('.jsonl')); for (const file of logFiles) { const filePath = path_1.default.join(auditDir, file); const content = await fs_1.promises.readFile(filePath, 'utf8'); const lines = content.trim().split('\n'); for (const line of lines) { if (!line) continue; try { let eventData = line; // Decrypt if encrypted if (this.config.encryption.enabled && this.encryptionKey) { eventData = await this.decryptData(eventData); } const event = JSON.parse(eventData); // Apply filters if (this.matchesQuery(event, query)) { events.push(event); } } catch (error) { this.logger.warn(`Failed to parse audit event: ${error}`); } } } } catch (error) { this.logger.error('Failed to search events:', error); } // Sort events if (query.orderBy) { events.sort((a, b) => { let aValue; let bValue; switch (query.orderBy) { case 'timestamp': aValue = a.timestamp; bValue = b.timestamp; break; case 'severity': const severityOrder = { info: 1, warning: 2, error: 3, critical: 4 }; aValue = severityOrder[a.severity]; bValue = severityOrder[b.severity]; break; case 'actor': aValue = a.actor.id; bValue = b.actor.id; break; case 'resource': aValue = a.resource.id; bValue = b.resource.id; break; default: return 0; } const result = aValue < bValue ? -1 : aValue > bValue ? 1 : 0; return query.orderDirection === 'desc' ? -result : result; }); } return events; } /** * Check if event matches query filters */ matchesQuery(event, query) { // Time range filter if (query.startTime && event.timestamp < query.startTime) return false; if (query.endTime && event.timestamp > query.endTime) return false; // Array filters if (query.eventTypes && !query.eventTypes.includes(event.eventType)) return false; if (query.categories && !query.categories.includes(event.category)) return false; if (query.severities && !query.severities.includes(event.severity)) return false; if (query.outcomes && !query.outcomes.includes(event.outcome)) return false; if (query.sources && !query.sources.includes(event.source)) return false; // Actor filter if (query.actors && !query.actors.includes(event.actor.id)) return false; // Resource filter if (query.resources && !query.resources.includes(event.resource.id)) return false; // Text search if (query.searchText) { const searchText = query.searchText.toLowerCase(); const searchableText = [ event.action, event.details.description, event.actor.name || '', event.resource.name || '', ].join(' ').toLowerCase(); if (!searchableText.includes(searchText)) return false; } return true; } /** * Calculate event hash for integrity */ calculateEventHash(event) { // Create deterministic string representation const hashData = [ event.id, event.timestamp, event.eventType, event.actor.id, event.action, event.resource.id, event.outcome, JSON.stringify(event.details), event.integrity.previousHash || '', ].join('|'); return crypto_1.default.createHash('sha256').update(hashData).digest('hex'); } /** * Sign event with digital signature */ signEvent(event) { if (!this.signingKey) { throw new Error('Signing key not available'); } const eventData = JSON.stringify(event); return crypto_1.default.createHmac('sha256', this.signingKey).update(eventData).digest('hex'); } /** * Verify event digital signature */ verifyEventSignature(event) { if (!this.signingKey || !event.integrity.signature) { return false; } const eventCopy = { ...event }; delete eventCopy.integrity.signature; const eventData = JSON.stringify(eventCopy); const expectedSignature = crypto_1.default.createHmac('sha256', this.signingKey).update(eventData).digest('hex'); return expectedSignature === event.integrity.signature; } /** * Analyze event for potential threats */ async analyzeEventForThreats(event) { // Implement threat detection logic // This would analyze patterns, anomalies, and known indicators // Example: Detect failed authentication attempts if (event.eventType === 'authentication' && event.outcome === 'failure') { // Check for brute force attacks // This would maintain counters and create alerts } // Example: Detect privilege escalation if (event.action.includes('privilege') || event.action.includes('admin')) { // Check if actor has appropriate permissions } // Example: Detect data exfiltration if (event.eventType === 'data_access' && event.resource.classification === 'confidential') { // Check access patterns and volume } } /** * Process security alert */ async processSecurityAlert(alert) { this.logger.warn(`Processing security alert: ${alert.title}`); // Implement automated response based on alert type switch (alert.type) { case 'brute_force': // Implement IP blocking or account lockout break; case 'privilege_escalation': // Alert administrators immediately break; case 'data_exfiltration': // Block access and investigate break; } } /** * Calculate event summary statistics */ calculateEventSummary(events) { const summary = { totalEvents: events.length, successfulEvents: 0, failedEvents: 0, criticalEvents: 0, uniqueActors: new Set(), uniqueResources: new Set(), securityIncidents: 0, complianceViolations: 0, }; for (const event of events) { if (event.outcome === 'success') summary.successfulEvents++; if (event.outcome === 'failure') summary.failedEvents++; if (event.severity === 'critical') summary.criticalEvents++; if (event.eventType === 'security_incident') summary.securityIncidents++; summary.uniqueActors.add(event.actor.id); summary.uniqueResources.add(event.resource.id); } return { ...summary, uniqueActors: summary.uniqueActors.size, uniqueResources: summary.uniqueResources.size, }; } /** * Generate insights from events */ generateEventInsights(events) { const actorCounts = new Map(); const resourceCounts = new Map(); const failurePatterns = new Map(); const locationCounts = new Map(); for (const event of events) { // Count actors actorCounts.set(event.actor.id, (actorCounts.get(event.actor.id) || 0) + 1); // Count resources resourceCounts.set(event.resource.id, (resourceCounts.get(event.resource.id) || 0) + 1); // Track failure patterns if (event.outcome === 'failure') { const pattern = `${event.eventType}:${event.action}`; failurePatterns.set(pattern, (failurePatterns.get(pattern) || 0) + 1); } // Track geographic distribution if (event.details.geolocation) { const location = event.details.geolocation.country; locationCounts.set(location, (locationCounts.get(location) || 0) + 1); } } // Convert to sorted arrays const topActors = Array.from(actorCounts.entries()) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([actor, count]) => ({ actor, count })); const topResources = Array.from(resourceCounts.entries()) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([resource, count]) => ({ resource, count })); const failurePatternsList = Array.from(failurePatterns.entries()) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([pattern, count]) => ({ pattern, count })); const geographicDistribution = Array.from(locationCounts.entries()) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([location, count]) => ({ location, count })); return { topActors, topResources, failurePatterns: failurePatternsList, riskIndicators: [], // Would analyze for risk patterns geographicDistribution, }; } /** * Generate recommendations based on analysis */ generateRecommendations(events, insights) { const recommendations = []; // Check failure rate const totalEvents = events.length; const failedEvents = events.filter(e => e.outcome === 'failure').length; const failureRate = failedEvents / totalEvents; if (failureRate > 0.1) { recommendations.push('High failure rate detected - review system configuration and user training'); } // Check for critical events const criticalEvents = events.filter(e => e.severity === 'critical').length; if (criticalEvents > 0) { recommendations.push(`${criticalEvents} critical events detected - immediate investigation recommended`); } // Check for unusual patterns if (insights.failurePatterns.length > 0) { recommendations.push('Recurring failure patterns detected - implement preventive measures'); } // Standard recommendations recommendations.push('Implement automated threat detection'); recommendations.push('Regular security awareness training for users'); recommendations.push('Periodic access review and privilege cleanup'); return recommendations; } /** * Assess compliance status */ async assessComplianceStatus(events) { // Implement compliance assessment logic return [ { framework: 'GDPR', status: 'compliant', violations: [], }, { framework: 'SOC2', status: 'compliant', violations: [], }, ]; } /** * Save audit report */ async saveReport(report) { const reportsDir = './audit-reports'; await fs_1.promises.mkdir(reportsDir, { recursive: true }); const filename = `audit-report-${report.id}.json`; const filePath = path_1.default.join(reportsDir, filename); await fs_1.promises.writeFile(filePath, JSON.stringify(report, null, 2)); } /** * Convert events to CSV format */ convertToCSV(events) { const headers = [ 'ID', 'Timestamp', 'Event Type', 'Category', 'Severity', 'Source', 'Actor ID', 'Actor Type', 'Action', 'Resource ID', 'Resource Type', 'Outcome', 'Description' ]; const rows = events.map(event => [ event.id, new Date(event.timestamp).toISOString(), event.eventType, event.category, event.severity, event.source, event.actor.id, event.actor.type, event.action, event.resource.id, event.resource.type, event.outcome, event.details.description, ]); return [headers, ...rows].map(row => row.join(',')).join('\n'); } /** * Convert events to Syslog format */ convertToSyslog(events) { return events.map(event => { const priority = this.getSyslogPriority(event.severity); const timestamp = new Date(event.timestamp).toISOString(); const hostname = 'codecraft-audit'; const tag = 'audit'; return `<${priority}>${timestamp} ${hostname} ${tag}: ${event.action} by ${event.actor.id} on ${event.resource.id} - ${event.outcome}`; }).join('\n'); } /** * Convert events to CEF format */ convertToCEF(events) { return events.map(event => { const version = '0'; const deviceVendor = 'CodeCraft'; const deviceProduct = 'Audit Trail'; const deviceVersion = '1.0'; const signatureId = event.eventType; const name = event.action; const severity = this.getCEFSeverity(event.severity); return `CEF:${version}|${deviceVendor}|${deviceProduct}|${deviceVersion}|${signatureId}|${name}|${severity}|src=${event.actor.ip || 'unknown'} suser=${event.actor.id} act=${event.action} outcome=${event.outcome}`; }).join('\n'); } /** * Get Syslog priority from severity */ getSyslogPriority(severity) { const priorities = { info: 6, warning: 4, error: 3, critical: 2 }; return priorities[severity] || 6; } /** * Get CEF severity from severity */ getCEFSeverity(severity) { const severities = { info: 2, warning: 5, error: 8, critical: 10 }; return severities[severity] || 2; } /** * Compress data using gzip */ async compressData(data) { const zlib = require('zlib'); const compressed = zlib.gzipSync(Buffer.from(data)); return compressed.toString('base64'); } /** * Encrypt data */ async encryptData(data) { if (!this.encryptionKey) { throw new Error('Encryption key not available'); } const iv = crypto_1.default.randomBytes(12); const cipher = crypto_1.default.createCipheriv(this.config.encryption.algorithm, this.encryptionKey, iv); const encrypted = Buffer.concat([ cipher.update(data, 'utf8'), cipher.final(), ]); const tag = cipher.getAuthTag(); return JSON.stringify({ iv: iv.toString('base64'), encrypted: encrypted.toString('base64'), tag: tag.toString('base64'), }); } /** * Decrypt data */ async decryptData(encryptedData) { if (!this.encryptionKey) { throw new Error('Encryption key not available'); } const { iv, encrypted, tag } = JSON.parse(encryptedData); const decipher = crypto_1.default.createDecipheriv(this.config.encryption.algorithm, this.encryptionKey, Buffer.from(iv, 'base64')); decipher.setAuthTag(Buffer.from(tag, 'base64')); const decrypted = Buffer.concat([ decipher.update(Buffer.from(encrypted, 'base64')), decipher.final(), ]); return decrypted.toString('utf8'); } /** * Cleanup old events based on retention policies */ async cleanupOldEvents() { this.logger.info('Starting cleanup of old audit events'); // Implement cleanup logic based on retention policies // This would delete events older than retention period } /** * Rotate encryption keys */ async rotateEncryptionKeys() { this.logger.info('Rotating encryption keys'); // Generate new keys this.encryptionKey = crypto_1.default.randomBytes(32); this.signingKey = crypto_1.default.randomBytes(32); // In production, this would: // 1. Store old keys for decrypting existing data // 2. Use new keys for new data // 3. Gradually re-encrypt old data with new keys } /** * Generate unique event ID */ generateEventId() { return crypto_1.default.randomBytes(16).toString('hex'); } } exports.AuditTrail = AuditTrail; //# sourceMappingURL=audit-trail.js.map