const fs = require('fs'); const path = require('path'); class SecurityLogger { constructor() { // Default to logs directory, but allow override via environment this.logDir = process.env.SECURITY_LOG_DIR || path.join(__dirname, '..', 'logs'); this.logFile = path.join(this.logDir, 'security-audit.log'); // Ensure log directory exists this.ensureLogDirectory(); } ensureLogDirectory() { try { if (!fs.existsSync(this.logDir)) { fs.mkdirSync(this.logDir, { recursive: true }); } } catch (error) { console.error('Failed to create log directory:', error.message); // Fallback to console logging only this.logFile = null; } } logSecurityEvent(level, message, metadata = {}) { const timestamp = new Date().toISOString(); const logEntry = { timestamp, level: level.toUpperCase(), message, ...metadata }; // Always log to console for immediate visibility console.log(`[SECURITY AUDIT] ${timestamp} - ${message}`); // Also log to file if available if (this.logFile) { try { const logLine = JSON.stringify(logEntry) + '\n'; fs.appendFileSync(this.logFile, logLine); } catch (error) { console.error('Failed to write to security log file:', error.message); } } } logIPRestriction(ip, tenant, userAgent, denied = true) { const action = denied ? 'denied access to' : 'granted access to'; this.logSecurityEvent('WARNING', `IP ${ip} ${action} tenant ${tenant}`, { type: 'IP_RESTRICTION', ip, tenant, userAgent: userAgent || 'unknown', denied }); } logAuthFailure(reason, metadata = {}) { this.logSecurityEvent('ERROR', `Authentication failure: ${reason}`, { type: 'AUTH_FAILURE', reason, ...metadata }); } logSuspiciousActivity(activity, metadata = {}) { this.logSecurityEvent('CRITICAL', `Suspicious activity detected: ${activity}`, { type: 'SUSPICIOUS_ACTIVITY', activity, ...metadata }); } // Get recent security events for monitoring getRecentEvents(count = 100) { if (!this.logFile || !fs.existsSync(this.logFile)) { return []; } try { const content = fs.readFileSync(this.logFile, 'utf8'); const lines = content.trim().split('\n').filter(line => line); return lines .slice(-count) .map(line => { try { return JSON.parse(line); } catch { return null; } }) .filter(Boolean); } catch (error) { console.error('Failed to read security log file:', error.message); return []; } } } // Singleton instance const securityLogger = new SecurityLogger(); module.exports = { SecurityLogger, securityLogger };