"use strict";
/**
 * Security Audit Logging System
 * Comprehensive logging and monitoring for security events
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityMonitor = exports.SecurityAuditLogger = void 0;
exports.getSecurityAuditLogger = getSecurityAuditLogger;
const fs_1 = require("fs");
const path_1 = require("path");
const crypto_1 = require("crypto");
const logger_1 = require("../utils/logger");
class SecurityAuditLogger {
    static instance;
    config;
    events = [];
    alertRules = new Map();
    alertCounts = new Map();
    writeStream;
    currentLogFile;
    logRotationTimer;
    constructor(config) {
        this.config = {
            enabled: true,
            logFilePath: (0, path_1.resolve)(process.cwd(), 'logs', 'security-audit.log'),
            maxFileSize: 10 * 1024 * 1024, // 10MB
            maxFiles: 10,
            minSeverity: 'medium',
            enableAlerts: true,
            alertThresholds: {
                highSeverityCount: 5,
                criticalSeverityCount: 1,
                timeWindowMinutes: 15,
            },
            encryptLogs: false,
            retentionDays: 90,
            ...config,
        };
        this.initializeLogging();
        this.setupDefaultAlertRules();
        this.startCleanupTimer();
    }
    static getInstance(config) {
        if (!SecurityAuditLogger.instance) {
            SecurityAuditLogger.instance = new SecurityAuditLogger(config);
        }
        return SecurityAuditLogger.instance;
    }
    /**
     * Log a security event
     */
    async logSecurityEvent(type, description, data = {}, options = {}) {
        const eventId = this.generateEventId();
        const timestamp = new Date().toISOString();
        const event = {
            id: eventId,
            timestamp,
            type,
            severity: options.severity || this.calculateSeverity(type, data),
            source: options.source || 'datapilot',
            userId: options.userId,
            ipAddress: this.extractIpAddress(options.context),
            filePath: options.filePath,
            operation: options.operation,
            description,
            data: this.sanitiseEventData(data),
            outcome: options.outcome || 'success',
            riskScore: options.riskScore || this.calculateRiskScore(type, options.severity || 'medium'),
        };
        try {
            // Store in memory
            this.events.push(event);
            // Check if we should log based on severity
            if (this.shouldLogEvent(event)) {
                await this.writeEventToLog(event);
            }
            // Check alert rules
            await this.checkAlertRules(event);
            // Log to application logger
            logger_1.logger.info('Security event logged', {
                eventId,
                type,
                severity: event.severity,
                outcome: event.outcome,
                riskScore: event.riskScore,
                ...options.context,
            });
            return eventId;
        }
        catch (error) {
            logger_1.logger.error('Failed to log security event', {
                eventId,
                type,
                error: error instanceof Error ? error.message : 'Unknown error',
                ...options.context,
            });
            throw error;
        }
    }
    /**
     * Log authentication event
     */
    async logAuthenticationEvent(operation, outcome, userId, data = {}, context) {
        return this.logSecurityEvent('authentication', `Authentication ${operation}`, { operation, ...data }, {
            severity: outcome === 'failure' ? 'high' : 'low',
            userId,
            operation,
            outcome,
            context,
        });
    }
    /**
     * Log file access event
     */
    async logFileAccessEvent(filePath, operation, outcome, userId, data = {}, context) {
        return this.logSecurityEvent('file_access', `File ${operation}: ${filePath}`, { fileSize: data.fileSize, permissions: data.permissions, ...data }, {
            severity: outcome === 'blocked' ? 'high' : 'medium',
            filePath,
            operation,
            outcome,
            userId,
            context,
        });
    }
    /**
     * Log policy violation
     */
    async logPolicyViolation(policyName, violationType, data = {}, context) {
        return this.logSecurityEvent('policy_violation', `Policy violation: ${policyName} - ${violationType}`, { policyName, violationType, ...data }, {
            severity: 'high',
            outcome: 'blocked',
            riskScore: 8,
            context,
        });
    }
    /**
     * Log intrusion attempt
     */
    async logIntrusionAttempt(attackType, source, data = {}, context) {
        return this.logSecurityEvent('intrusion_attempt', `Intrusion attempt detected: ${attackType}`, { attackType, attackSource: source, ...data }, {
            severity: 'critical',
            outcome: 'blocked',
            riskScore: 10,
            context,
        });
    }
    /**
     * Add custom alert rule
     */
    addAlertRule(rule) {
        this.alertRules.set(rule.id, rule);
        logger_1.logger.info('Security alert rule added', {
            ruleId: rule.id,
            ruleName: rule.name,
            eventTypes: rule.eventTypes,
        });
    }
    /**
     * Remove alert rule
     */
    removeAlertRule(ruleId) {
        const removed = this.alertRules.delete(ruleId);
        if (removed) {
            logger_1.logger.info('Security alert rule removed', { ruleId });
        }
        return removed;
    }
    /**
     * Get security events with filtering
     */
    getEvents(filter) {
        let filtered = [...this.events];
        if (filter) {
            filtered = filtered.filter((event) => {
                if (filter.type && event.type !== filter.type)
                    return false;
                if (filter.severity && event.severity !== filter.severity)
                    return false;
                if (filter.outcome && event.outcome !== filter.outcome)
                    return false;
                if (filter.userId && event.userId !== filter.userId)
                    return false;
                if (filter.filePath && event.filePath !== filter.filePath)
                    return false;
                if (filter.minRiskScore && event.riskScore < filter.minRiskScore)
                    return false;
                const eventTime = new Date(event.timestamp);
                if (filter.startTime && eventTime < filter.startTime)
                    return false;
                if (filter.endTime && eventTime > filter.endTime)
                    return false;
                return true;
            });
        }
        // Sort by timestamp (newest first)
        filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
        // Apply limit
        if (filter?.limit) {
            filtered = filtered.slice(0, filter.limit);
        }
        return filtered;
    }
    /**
     * Get security statistics
     */
    getStatistics(timeRange) {
        const events = timeRange
            ? this.getEvents({ startTime: timeRange.start, endTime: timeRange.end })
            : this.events;
        const eventsBySeverity = {
            low: 0,
            medium: 0,
            high: 0,
            critical: 0,
        };
        const eventsByType = {
            authentication: 0,
            authorization: 0,
            file_access: 0,
            input_validation: 0,
            configuration_change: 0,
            policy_violation: 0,
            intrusion_attempt: 0,
            data_integrity: 0,
            system_security: 0,
            compliance: 0,
        };
        const eventsByOutcome = {};
        let totalRiskScore = 0;
        for (const event of events) {
            eventsBySeverity[event.severity]++;
            eventsByType[event.type]++;
            eventsByOutcome[event.outcome] = (eventsByOutcome[event.outcome] || 0) + 1;
            totalRiskScore += event.riskScore;
        }
        const topRiskEvents = events.sort((a, b) => b.riskScore - a.riskScore).slice(0, 10);
        return {
            totalEvents: events.length,
            eventsBySeverity,
            eventsByType,
            eventsByOutcome,
            averageRiskScore: events.length > 0 ? totalRiskScore / events.length : 0,
            topRiskEvents,
        };
    }
    /**
     * Export audit log
     */
    async exportAuditLog(filePath, format = 'json', filter) {
        const events = this.getEvents(filter);
        try {
            if (format === 'json') {
                await fs_1.promises.writeFile(filePath, JSON.stringify(events, null, 2), 'utf8');
            }
            else if (format === 'csv') {
                const csvContent = this.eventsToCSV(events);
                await fs_1.promises.writeFile(filePath, csvContent, 'utf8');
            }
            logger_1.logger.info('Audit log exported', {
                filePath,
                format,
                eventCount: events.length,
            });
        }
        catch (error) {
            logger_1.logger.error('Failed to export audit log', {
                filePath,
                format,
                error: error instanceof Error ? error.message : 'Unknown error',
            });
            throw error;
        }
    }
    /**
     * Clear old events based on retention policy
     */
    async clearOldEvents() {
        const cutoffDate = new Date();
        cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);
        const initialCount = this.events.length;
        this.events = this.events.filter((event) => new Date(event.timestamp) > cutoffDate);
        const removedCount = initialCount - this.events.length;
        if (removedCount > 0) {
            logger_1.logger.info('Cleared old security events', {
                removedCount,
                remainingCount: this.events.length,
                cutoffDate: cutoffDate.toISOString(),
            });
        }
        return removedCount;
    }
    // Private helper methods
    async initializeLogging() {
        if (!this.config.enabled) {
            return;
        }
        try {
            // Ensure log directory exists
            const logDir = (0, path_1.dirname)(this.config.logFilePath);
            await fs_1.promises.mkdir(logDir, { recursive: true });
            // Initialize write stream
            await this.createLogStream();
            logger_1.logger.info('Security audit logging initialized', {
                logFilePath: this.config.logFilePath,
                minSeverity: this.config.minSeverity,
            });
        }
        catch (error) {
            logger_1.logger.error('Failed to initialize security audit logging', {
                error: error instanceof Error ? error.message : 'Unknown error',
            });
            throw error;
        }
    }
    async createLogStream() {
        if (this.writeStream) {
            this.writeStream.end();
        }
        this.currentLogFile = this.config.logFilePath;
        this.writeStream = (0, fs_1.createWriteStream)(this.currentLogFile, { flags: 'a' });
        this.writeStream.on('error', (error) => {
            logger_1.logger.error('Security audit log write error', {
                error: error.message,
                logFile: this.currentLogFile,
            });
        });
    }
    setupDefaultAlertRules() {
        // Critical events
        this.addAlertRule({
            id: 'critical-events',
            name: 'Critical Security Events',
            eventTypes: ['intrusion_attempt', 'policy_violation'],
            minSeverity: 'critical',
            threshold: { count: 1, timeWindowMinutes: 1 },
            action: 'escalate',
            enabled: true,
        });
        // Failed authentication attempts
        this.addAlertRule({
            id: 'failed-auth',
            name: 'Failed Authentication Attempts',
            eventTypes: ['authentication'],
            minSeverity: 'medium',
            threshold: { count: 5, timeWindowMinutes: 15 },
            action: 'notify',
            enabled: true,
        });
        // Suspicious file access
        this.addAlertRule({
            id: 'suspicious-file-access',
            name: 'Suspicious File Access Patterns',
            eventTypes: ['file_access'],
            minSeverity: 'high',
            threshold: { count: 10, timeWindowMinutes: 5 },
            action: 'block',
            enabled: true,
        });
    }
    generateEventId() {
        return (0, crypto_1.randomBytes)(8).toString('hex');
    }
    calculateSeverity(type, data) {
        // Default severity based on event type
        const typeSeverity = {
            authentication: 'medium',
            authorization: 'medium',
            file_access: 'low',
            input_validation: 'medium',
            configuration_change: 'high',
            policy_violation: 'high',
            intrusion_attempt: 'critical',
            data_integrity: 'high',
            system_security: 'high',
            compliance: 'medium',
        };
        let severity = typeSeverity[type] || 'medium';
        // Adjust based on data content
        if (data.blocked || data.quarantined) {
            severity = 'high';
        }
        if (data.attackDetected || data.maliciousContent) {
            severity = 'critical';
        }
        return severity;
    }
    calculateRiskScore(type, severity) {
        const baseScores = {
            authentication: 3,
            authorization: 4,
            file_access: 2,
            input_validation: 3,
            configuration_change: 6,
            policy_violation: 7,
            intrusion_attempt: 9,
            data_integrity: 8,
            system_security: 7,
            compliance: 5,
        };
        const severityMultiplier = {
            low: 0.5,
            medium: 1.0,
            high: 1.5,
            critical: 2.0,
        };
        const baseScore = baseScores[type] || 5;
        const multiplier = severityMultiplier[severity] || 1.0;
        return Math.min(10, Math.round(baseScore * multiplier));
    }
    shouldLogEvent(event) {
        if (!this.config.enabled) {
            return false;
        }
        const severityLevels = {
            low: 1,
            medium: 2,
            high: 3,
            critical: 4,
        };
        return severityLevels[event.severity] >= severityLevels[this.config.minSeverity];
    }
    async writeEventToLog(event) {
        if (!this.writeStream) {
            return;
        }
        const logEntry = JSON.stringify(event) + '\n';
        return new Promise((resolve, reject) => {
            this.writeStream.write(logEntry, (error) => {
                if (error) {
                    reject(error);
                }
                else {
                    resolve();
                }
            });
        });
    }
    async checkAlertRules(event) {
        for (const rule of this.alertRules.values()) {
            if (!rule.enabled || !rule.eventTypes.includes(event.type)) {
                continue;
            }
            const severityLevels = {
                low: 1,
                medium: 2,
                high: 3,
                critical: 4,
            };
            if (severityLevels[event.severity] < severityLevels[rule.minSeverity]) {
                continue;
            }
            // Check pattern if specified
            if (rule.pattern && !rule.pattern.test(JSON.stringify(event.data))) {
                continue;
            }
            // Check threshold
            const key = `${rule.id}_${event.type}`;
            const now = Date.now();
            const windowMs = rule.threshold.timeWindowMinutes * 60 * 1000;
            const countData = this.alertCounts.get(key) || { count: 0, timestamp: now };
            // Reset count if outside time window
            if (now - countData.timestamp > windowMs) {
                countData.count = 0;
                countData.timestamp = now;
            }
            countData.count++;
            this.alertCounts.set(key, countData);
            // Trigger alert if threshold exceeded
            if (countData.count >= rule.threshold.count) {
                await this.triggerAlert(rule, event, countData.count);
                // Reset counter after triggering
                countData.count = 0;
                countData.timestamp = now;
            }
        }
    }
    async triggerAlert(rule, event, count) {
        const alertData = {
            ruleId: rule.id,
            ruleName: rule.name,
            triggerEvent: event,
            count,
            action: rule.action,
            timestamp: new Date(),
        };
        logger_1.logger.warn('Security alert triggered', alertData);
        // Log the alert as a security event
        await this.logSecurityEvent('system_security', `Security alert: ${rule.name}`, alertData, {
            severity: 'high',
            outcome: 'warning',
            riskScore: 8,
        });
        // TODO: Implement additional alert actions (email, webhook, etc.)
        switch (rule.action) {
            case 'notify':
                // Send notification
                break;
            case 'block':
                // Implement blocking logic
                break;
            case 'escalate':
                // Escalate to security team
                break;
        }
    }
    sanitiseEventData(data) {
        const sanitised = { ...data };
        // Remove sensitive fields
        const sensitiveFields = ['password', 'token', 'key', 'secret', 'credential'];
        for (const field of sensitiveFields) {
            if (field in sanitised) {
                sanitised[field] = '[REDACTED]';
            }
        }
        return sanitised;
    }
    extractIpAddress(context) {
        // Extract IP from context if available
        return context?.ipAddress;
    }
    eventsToCSV(events) {
        const headers = [
            'id',
            'timestamp',
            'type',
            'severity',
            'source',
            'userId',
            'ipAddress',
            'filePath',
            'operation',
            'description',
            'outcome',
            'riskScore',
        ];
        const csvRows = [headers.join(',')];
        for (const event of events) {
            const row = headers.map((header) => {
                const value = event[header];
                if (typeof value === 'string') {
                    return `"${value.replace(/"/g, '""')}"`; // Escape quotes
                }
                return value || '';
            });
            csvRows.push(row.join(','));
        }
        return csvRows.join('\n');
    }
    startCleanupTimer() {
        // Run cleanup every hour
        this.logRotationTimer = setInterval(() => {
            this.clearOldEvents().catch((error) => {
                logger_1.logger.error('Security audit cleanup failed', {
                    error: error instanceof Error ? error.message : 'Unknown error',
                });
            });
        }, 3600000);
    }
    /**
     * Shutdown audit logger gracefully
     */
    async shutdown() {
        if (this.logRotationTimer) {
            clearInterval(this.logRotationTimer);
        }
        if (this.writeStream) {
            return new Promise((resolve) => {
                this.writeStream.end(() => {
                    logger_1.logger.info('Security audit logger shutdown complete');
                    resolve();
                });
            });
        }
    }
}
exports.SecurityAuditLogger = SecurityAuditLogger;
/**
 * Factory function for easy access
 */
function getSecurityAuditLogger() {
    return SecurityAuditLogger.getInstance();
}
/**
 * Security monitoring middleware
 */
class SecurityMonitor {
    auditLogger;
    constructor() {
        this.auditLogger = getSecurityAuditLogger();
    }
    /**
     * Monitor function execution for security events
     */
    async monitorExecution(operation, fn, context) {
        const startTime = Date.now();
        try {
            const result = await fn();
            await this.auditLogger.logSecurityEvent('system_security', `Operation completed: ${operation}`, {
                operation,
                duration: Date.now() - startTime,
            }, {
                severity: 'low',
                outcome: 'success',
                context,
            });
            return result;
        }
        catch (error) {
            await this.auditLogger.logSecurityEvent('system_security', `Operation failed: ${operation}`, {
                operation,
                error: error instanceof Error ? error.message : 'Unknown error',
                duration: Date.now() - startTime,
            }, {
                severity: 'medium',
                outcome: 'failure',
                context,
            });
            throw error;
        }
    }
}
exports.SecurityMonitor = SecurityMonitor;
//# sourceMappingURL=audit-logger.js.map