/** * Response Protocol Manager * Automated response protocols for security incident handling * CWE-778 Compliance: Logs all automated responses and protocol executions */ const logger = require('./logger'); const logAggregator = require('./logAggregator'); const { db } = require('../database/db'); const EventEmitter = require('events'); class ResponseProtocolManager extends EventEmitter { constructor() { super(); this.protocols = new Map(); this.executionHistory = new Map(); this.initialize(); } /** * Initialize response protocol manager */ async initialize() { await this.createProtocolsTable(); await this.createExecutionHistoryTable(); await this.loadProtocols(); logger.info('[ResponseProtocolManager] Initialized with automated response protocols'); // Log initialization (CWE-778) logAggregator.aggregate('response_protocol_manager', 'info', 'security', 'Response protocol manager initialized', { totalProtocols: this.protocols.size }); } /** * Create protocols table */ async createProtocolsTable() { return new Promise((resolve, reject) => { db.run(` CREATE TABLE IF NOT EXISTS response_protocols ( id INTEGER PRIMARY KEY AUTOINCREMENT, protocol_id TEXT UNIQUE NOT NULL, name TEXT NOT NULL, description TEXT, trigger_type TEXT NOT NULL, trigger_condition TEXT NOT NULL, actions TEXT NOT NULL, severity TEXT NOT NULL, enabled INTEGER DEFAULT 1, auto_execute INTEGER DEFAULT 0, cooldown_minutes INTEGER DEFAULT 60, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `, async (err) => { if (err) reject(err); else { db.run(`CREATE INDEX IF NOT EXISTS idx_protocols_trigger ON response_protocols(trigger_type, enabled)`); db.run(`CREATE INDEX IF NOT EXISTS idx_protocols_severity ON response_protocols(severity, enabled)`); await this.createDefaultProtocols(); resolve(); } }); }); } /** * Create execution history table */ async createExecutionHistoryTable() { return new Promise((resolve, reject) => { db.run(` CREATE TABLE IF NOT EXISTS protocol_executions ( id INTEGER PRIMARY KEY AUTOINCREMENT, execution_id TEXT UNIQUE NOT NULL, protocol_id TEXT NOT NULL, trigger_event TEXT NOT NULL, actions_executed TEXT NOT NULL, execution_status TEXT NOT NULL, execution_result TEXT, executed_by TEXT DEFAULT 'system', executed_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `, (err) => { if (err) reject(err); else { db.run(`CREATE INDEX IF NOT EXISTS idx_executions_protocol ON protocol_executions(protocol_id)`); db.run(`CREATE INDEX IF NOT EXISTS idx_executions_status ON protocol_executions(execution_status)`); resolve(); } }); }); } /** * Create default response protocols */ async createDefaultProtocols() { const defaultProtocols = [ { protocol_id: 'PROTOCOL-BRUTE-FORCE-RESPONSE', name: 'Brute Force Attack Response', description: 'Automated response to brute force attacks', trigger_type: 'anomaly', trigger_condition: JSON.stringify({ anomaly_type: 'brute_force_attack', severity: 'critical' }), actions: JSON.stringify([ { action: 'block_ip', duration_minutes: 60, reason: 'brute_force_attack' }, { action: 'notify_admin', channel: 'email', priority: 'high' }, { action: 'log_incident', category: 'security_breach' } ]), severity: 'critical', auto_execute: 1, cooldown_minutes: 30 }, { protocol_id: 'PROTOCOL-CREDENTIAL-STUFFING-RESPONSE', name: 'Credential Stuffing Response', description: 'Automated response to credential stuffing attacks', trigger_type: 'anomaly', trigger_condition: JSON.stringify({ anomaly_type: 'credential_stuffing', severity: 'critical' }), actions: JSON.stringify([ { action: 'block_ip', duration_minutes: 120, reason: 'credential_stuffing' }, { action: 'require_2fa', target: 'affected_accounts' }, { action: 'notify_admin', channel: 'email', priority: 'high' }, { action: 'log_incident', category: 'account_compromise' } ]), severity: 'critical', auto_execute: 1, cooldown_minutes: 60 }, { protocol_id: 'PROTOCOL-PRIVILEGE-ESC-RESPONSE', name: 'Privilege Escalation Response', description: 'Automated response to privilege escalation attempts', trigger_type: 'anomaly', trigger_condition: JSON.stringify({ anomaly_type: 'privilege_escalation', severity: 'critical' }), actions: JSON.stringify([ { action: 'lock_account', target: 'attacker', duration_minutes: 240 }, { action: 'revoke_sessions', target: 'attacker' }, { action: 'notify_admin', channel: 'email', priority: 'critical' }, { action: 'escalate_incident', level: 'security_team' }, { action: 'log_incident', category: 'privilege_violation' } ]), severity: 'critical', auto_execute: 1, cooldown_minutes: 15 }, { protocol_id: 'PROTOCOL-SUSPICIOUS-IP-RESPONSE', name: 'Suspicious IP Response', description: 'Automated response to suspicious IP activity', trigger_type: 'anomaly', trigger_condition: JSON.stringify({ anomaly_type: 'suspicious_ip', severity: 'high' }), actions: JSON.stringify([ { action: 'rate_limit_ip', limit: 10, window_minutes: 10 }, { action: 'notify_admin', channel: 'in_app', priority: 'medium' }, { action: 'log_incident', category: 'suspicious_activity' } ]), severity: 'high', auto_execute: 1, cooldown_minutes: 60 }, { protocol_id: 'PROTOCOL-DATA-EXFIL-RESPONSE', name: 'Data Exfiltration Response', description: 'Automated response to data exfiltration attempts', trigger_type: 'anomaly', trigger_condition: JSON.stringify({ anomaly_type: 'data_exfiltration', severity: 'high' }), actions: JSON.stringify([ { action: 'block_ip', duration_minutes: 180, reason: 'data_exfiltration' }, { action: 'lock_account', target: 'attacker', duration_minutes: 360 }, { action: 'notify_admin', channel: 'email', priority: 'critical' }, { action: 'escalate_incident', level: 'data_protection_team' }, { action: 'log_incident', category: 'data_breach' } ]), severity: 'high', auto_execute: 0, cooldown_minutes: 120 }, { protocol_id: 'PROTOCOL-MALICIOUS-SIGNATURE-RESPONSE', name: 'Malicious Signature Response', description: 'Automated response to malicious signature matches', trigger_type: 'signature', trigger_condition: JSON.stringify({ signature_type: 'attack_pattern', threat_level: 'critical', auto_block: true }), actions: JSON.stringify([ { action: 'block_ip', duration_minutes: 240, reason: 'malicious_signature' }, { action: 'notify_admin', channel: 'email', priority: 'high' }, { action: 'log_incident', category: 'attack_detected' } ]), severity: 'critical', auto_execute: 1, cooldown_minutes: 30 }, { protocol_id: 'PROTOCOL-THREAT-SCORE-CRITICAL', name: 'Critical Threat Score Response', description: 'Automated response when threat score reaches critical level', trigger_type: 'threshold', trigger_condition: JSON.stringify({ metric: 'threat_score', operator: '>=', value: 80 }), actions: JSON.stringify([ { action: 'notify_admin', channel: 'email', priority: 'critical' }, { action: 'escalate_incident', level: 'security_team' }, { action: 'enable_enhanced_monitoring', duration_minutes: 120 }, { action: 'log_incident', category: 'threat_escalation' } ]), severity: 'critical', auto_execute: 1, cooldown_minutes: 60 } ]; for (const protocol of defaultProtocols) { await new Promise((resolve, reject) => { db.run( `INSERT OR IGNORE INTO response_protocols (protocol_id, name, description, trigger_type, trigger_condition, actions, severity, auto_execute, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ protocol.protocol_id, protocol.name, protocol.description, protocol.trigger_type, protocol.trigger_condition, protocol.actions, protocol.severity, protocol.auto_execute, protocol.cooldown_minutes ], (err) => { if (err) reject(err); else resolve(); } ); }); } logger.info(`[ResponseProtocolManager] Created ${defaultProtocols.length} default protocols`); } /** * Load protocols from database into memory */ async loadProtocols() { return new Promise((resolve, reject) => { db.all( `SELECT * FROM response_protocols WHERE enabled = 1`, [], (err, rows) => { if (err) { reject(err); } else { this.protocols.clear(); rows.forEach(row => { row.trigger_condition = JSON.parse(row.trigger_condition); row.actions = JSON.parse(row.actions); this.protocols.set(row.protocol_id, row); }); logger.info(`[ResponseProtocolManager] Loaded ${rows.length} active protocols`); resolve(); } } ); }); } /** * Execute protocols based on trigger event * CWE-778: Logs all protocol executions */ async executeProtocols(triggerType, triggerEvent, context = {}) { const matchingProtocols = Array.from(this.protocols.values()).filter( p => p.trigger_type === triggerType && this.matchesTriggerCondition(p.trigger_condition, triggerEvent) ); if (matchingProtocols.length === 0) { return { executed: false, protocols: [] }; } const executedProtocols = []; for (const protocol of matchingProtocols) { // Check cooldown if (this.isInCooldown(protocol.protocol_id)) { logger.info(`[ResponseProtocolManager] Protocol ${protocol.protocol_id} in cooldown, skipping`); continue; } // Check if auto-execute is enabled if (!protocol.auto_execute) { logger.info(`[ResponseProtocolManager] Protocol ${protocol.protocol_id} requires manual execution, skipping`); continue; } // Execute protocol const executionResult = await this.executeProtocolActions(protocol, triggerEvent, context); executedProtocols.push(executionResult); // Set cooldown this.setCooldown(protocol.protocol_id, protocol.cooldown_minutes); // Log protocol execution (CWE-778) logAggregator.aggregate('response_protocol_manager', 'warn', 'security', 'Response protocol executed', { protocolId: protocol.protocol_id, protocolName: protocol.name, triggerType, triggerEvent: JSON.stringify(triggerEvent).substring(0, 200), actionsExecuted: executionResult.actionsExecuted.length, executionStatus: executionResult.status, context }); logger.warn(`[ResponseProtocolManager] Protocol executed: ${protocol.name} (${executionResult.status})`); } return { executed: executedProtocols.length > 0, protocols: executedProtocols }; } /** * Execute protocol actions */ async executeProtocolActions(protocol, triggerEvent, context) { const executionId = `EXEC-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`; const actionsExecuted = []; const actionResults = []; for (const action of protocol.actions) { try { const result = await this.executeAction(action, triggerEvent, context); actionsExecuted.push(action.action); actionResults.push({ action: action.action, status: 'success', result }); // Emit event for action execution this.emit('action_executed', { protocolId: protocol.protocol_id, action: action.action, result }); } catch (error) { logger.error(`[ResponseProtocolManager] Action execution failed: ${action.action}`, error); actionResults.push({ action: action.action, status: 'failed', error: error.message }); } } const executionStatus = actionResults.every(r => r.status === 'success') ? 'success' : 'partial'; // Save execution history await this.saveExecutionHistory({ executionId, protocolId: protocol.protocol_id, triggerEvent: JSON.stringify(triggerEvent), actionsExecuted: JSON.stringify(actionsExecuted), executionStatus, executionResult: JSON.stringify(actionResults) }); return { executionId, protocolId: protocol.protocol_id, protocolName: protocol.name, actionsExecuted, actionResults, status: executionStatus }; } /** * Execute individual action */ async executeAction(action, triggerEvent, context) { switch (action.action) { case 'block_ip': return await this.actionBlockIP(action, triggerEvent, context); case 'lock_account': return await this.actionLockAccount(action, triggerEvent, context); case 'revoke_sessions': return await this.actionRevokeSessions(action, triggerEvent, context); case 'require_2fa': return await this.actionRequire2FA(action, triggerEvent, context); case 'rate_limit_ip': return await this.actionRateLimitIP(action, triggerEvent, context); case 'notify_admin': return await this.actionNotifyAdmin(action, triggerEvent, context); case 'escalate_incident': return await this.actionEscalateIncident(action, triggerEvent, context); case 'log_incident': return await this.actionLogIncident(action, triggerEvent, context); case 'enable_enhanced_monitoring': return await this.actionEnableEnhancedMonitoring(action, triggerEvent, context); default: throw new Error(`Unknown action: ${action.action}`); } } /** * Action: Block IP address */ async actionBlockIP(action, triggerEvent, context) { const ipAddress = context.ip_address || triggerEvent.ip_address; const duration = action.duration_minutes || 60; const reason = action.reason || 'security_violation'; if (!ipAddress) { throw new Error('No IP address provided for blocking'); } // TODO: Implement actual IP blocking (firewall rules, rate limiter, etc.) logger.warn(`[ResponseProtocolManager] Action: Block IP ${ipAddress} for ${duration} minutes (reason: ${reason})`); return { action: 'block_ip', ipAddress, duration, reason, expiresAt: new Date(Date.now() + duration * 60 * 1000).toISOString() }; } /** * Action: Lock user account */ async actionLockAccount(action, triggerEvent, context) { const userId = context.user_id || triggerEvent.user_id; const duration = action.duration_minutes || 120; if (!userId) { throw new Error('No user ID provided for account locking'); } // TODO: Implement actual account locking logger.warn(`[ResponseProtocolManager] Action: Lock account ${userId} for ${duration} minutes`); return { action: 'lock_account', userId, duration, lockedUntil: new Date(Date.now() + duration * 60 * 1000).toISOString() }; } /** * Action: Revoke all sessions */ async actionRevokeSessions(action, triggerEvent, context) { const userId = context.user_id || triggerEvent.user_id; if (!userId) { throw new Error('No user ID provided for session revocation'); } // TODO: Implement actual session revocation logger.warn(`[ResponseProtocolManager] Action: Revoke all sessions for user ${userId}`); return { action: 'revoke_sessions', userId, revokedAt: new Date().toISOString() }; } /** * Action: Require 2FA for affected accounts */ async actionRequire2FA(action, triggerEvent, context) { const target = action.target || 'affected_accounts'; // TODO: Implement 2FA requirement logger.warn(`[ResponseProtocolManager] Action: Require 2FA for ${target}`); return { action: 'require_2fa', target, enabledAt: new Date().toISOString() }; } /** * Action: Rate limit IP */ async actionRateLimitIP(action, triggerEvent, context) { const ipAddress = context.ip_address || triggerEvent.ip_address; const limit = action.limit || 10; const windowMinutes = action.window_minutes || 10; if (!ipAddress) { throw new Error('No IP address provided for rate limiting'); } // TODO: Implement actual rate limiting logger.warn(`[ResponseProtocolManager] Action: Rate limit IP ${ipAddress} to ${limit} requests per ${windowMinutes} minutes`); return { action: 'rate_limit_ip', ipAddress, limit, windowMinutes }; } /** * Action: Notify administrator */ async actionNotifyAdmin(action, triggerEvent, context) { const channel = action.channel || 'in_app'; const priority = action.priority || 'medium'; // TODO: Implement actual admin notification (email, SMS, webhook) logger.warn(`[ResponseProtocolManager] Action: Notify admin via ${channel} (priority: ${priority})`); return { action: 'notify_admin', channel, priority, notifiedAt: new Date().toISOString() }; } /** * Action: Escalate incident */ async actionEscalateIncident(action, triggerEvent, context) { const level = action.level || 'security_team'; // TODO: Implement actual incident escalation logger.warn(`[ResponseProtocolManager] Action: Escalate incident to ${level}`); return { action: 'escalate_incident', level, escalatedAt: new Date().toISOString() }; } /** * Action: Log incident */ async actionLogIncident(action, triggerEvent, context) { const category = action.category || 'security_incident'; logAggregator.aggregate('response_protocol_manager', 'error', category, 'Security incident logged', { triggerEvent, context }); return { action: 'log_incident', category, loggedAt: new Date().toISOString() }; } /** * Action: Enable enhanced monitoring */ async actionEnableEnhancedMonitoring(action, triggerEvent, context) { const duration = action.duration_minutes || 120; // TODO: Implement enhanced monitoring mode logger.warn(`[ResponseProtocolManager] Action: Enable enhanced monitoring for ${duration} minutes`); return { action: 'enable_enhanced_monitoring', duration, expiresAt: new Date(Date.now() + duration * 60 * 1000).toISOString() }; } /** * Check if trigger condition matches event */ matchesTriggerCondition(condition, event) { for (const [key, value] of Object.entries(condition)) { if (event[key] !== value) { return false; } } return true; } /** * Check if protocol is in cooldown */ isInCooldown(protocolId) { const lastExecution = this.executionHistory.get(protocolId); if (!lastExecution) return false; const cooldownEnd = new Date(lastExecution.cooldownUntil); return Date.now() < cooldownEnd.getTime(); } /** * Set cooldown for protocol */ setCooldown(protocolId, cooldownMinutes) { this.executionHistory.set(protocolId, { lastExecuted: new Date().toISOString(), cooldownUntil: new Date(Date.now() + cooldownMinutes * 60 * 1000).toISOString() }); } /** * Save execution history to database */ async saveExecutionHistory(data) { return new Promise((resolve, reject) => { db.run( `INSERT INTO protocol_executions (execution_id, protocol_id, trigger_event, actions_executed, execution_status, execution_result) VALUES (?, ?, ?, ?, ?, ?)`, [ data.executionId, data.protocolId, data.triggerEvent, data.actionsExecuted, data.executionStatus, data.executionResult ], (err) => { if (err) reject(err); else resolve(); } ); }); } /** * Get all protocols */ async getProtocols(filters = {}) { const { triggerType, severity, enabled, limit = 100 } = filters; let whereClause = []; let params = []; if (triggerType) { whereClause.push('trigger_type = ?'); params.push(triggerType); } if (severity) { whereClause.push('severity = ?'); params.push(severity); } if (enabled !== undefined) { whereClause.push('enabled = ?'); params.push(enabled ? 1 : 0); } const where = whereClause.length > 0 ? `WHERE ${whereClause.join(' AND ')}` : ''; params.push(limit); return new Promise((resolve, reject) => { db.all( `SELECT * FROM response_protocols ${where} ORDER BY severity DESC, trigger_type LIMIT ?`, params, (err, rows) => { if (err) reject(err); else resolve(rows); } ); }); } /** * Get protocol by ID */ async getProtocolById(protocolId) { return new Promise((resolve, reject) => { db.get( `SELECT * FROM response_protocols WHERE protocol_id = ?`, [protocolId], (err, row) => { if (err) reject(err); else resolve(row); } ); }); } /** * Get execution history */ async getExecutionHistory(filters = {}) { const { protocolId, status, limit = 100 } = filters; let whereClause = []; let params = []; if (protocolId) { whereClause.push('protocol_id = ?'); params.push(protocolId); } if (status) { whereClause.push('execution_status = ?'); params.push(status); } const where = whereClause.length > 0 ? `WHERE ${whereClause.join(' AND ')}` : ''; params.push(limit); return new Promise((resolve, reject) => { db.all( `SELECT * FROM protocol_executions ${where} ORDER BY executed_at DESC LIMIT ?`, params, (err, rows) => { if (err) reject(err); else resolve(rows); } ); }); } /** * Create new protocol * CWE-778: Logs protocol creation */ async createProtocol(data, userId) { const protocolId = `PROTOCOL-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`; return new Promise((resolve, reject) => { db.run( `INSERT INTO response_protocols (protocol_id, name, description, trigger_type, trigger_condition, actions, severity, enabled, auto_execute, cooldown_minutes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ protocolId, data.name, data.description || '', data.trigger_type, JSON.stringify(data.trigger_condition), JSON.stringify(data.actions), data.severity, data.enabled !== undefined ? (data.enabled ? 1 : 0) : 1, data.auto_execute !== undefined ? (data.auto_execute ? 1 : 0) : 0, data.cooldown_minutes || 60 ], async (err) => { if (err) { reject(err); } else { await this.loadProtocols(); // Log protocol creation (CWE-778) logAggregator.aggregate('response_protocol_manager', 'info', 'security', 'Response protocol created', { protocolId, userId, name: data.name, triggerType: data.trigger_type, severity: data.severity, autoExecute: data.auto_execute === 1 }); logger.info(`[ResponseProtocolManager] Protocol created: ${protocolId} by user ${userId}`); resolve({ protocolId }); } } ); }); } /** * Update protocol * CWE-778: Logs protocol modifications */ async updateProtocol(protocolId, updates, userId) { const allowedFields = ['name', 'description', 'trigger_condition', 'actions', 'severity', 'enabled', 'auto_execute', 'cooldown_minutes']; const setClause = []; const params = []; for (const [key, value] of Object.entries(updates)) { if (allowedFields.includes(key)) { setClause.push(`${key} = ?`); if (key === 'trigger_condition' || key === 'actions') { params.push(JSON.stringify(value)); } else if (key === 'enabled' || key === 'auto_execute') { params.push(value ? 1 : 0); } else { params.push(value); } } } if (setClause.length === 0) { throw new Error('No valid fields to update'); } setClause.push('updated_at = CURRENT_TIMESTAMP'); params.push(protocolId); return new Promise((resolve, reject) => { db.run( `UPDATE response_protocols SET ${setClause.join(', ')} WHERE protocol_id = ?`, params, async (err) => { if (err) { reject(err); } else { await this.loadProtocols(); // Log protocol update (CWE-778) logAggregator.aggregate('response_protocol_manager', 'info', 'security', 'Response protocol updated', { protocolId, userId, updates }); logger.info(`[ResponseProtocolManager] Protocol updated: ${protocolId} by user ${userId}`); resolve({ success: true }); } } ); }); } /** * Delete protocol * CWE-778: Logs protocol deletion */ async deleteProtocol(protocolId, userId) { return new Promise((resolve, reject) => { db.run( `DELETE FROM response_protocols WHERE protocol_id = ?`, [protocolId], async (err) => { if (err) { reject(err); } else { await this.loadProtocols(); // Log protocol deletion (CWE-778) logAggregator.aggregate('response_protocol_manager', 'warn', 'security', 'Response protocol deleted', { protocolId, userId }); logger.info(`[ResponseProtocolManager] Protocol deleted: ${protocolId} by user ${userId}`); resolve({ success: true }); } } ); }); } /** * Get protocol statistics */ async getStatistics() { return new Promise((resolve, reject) => { db.get( `SELECT COUNT(*) as total, SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as enabled, SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) as disabled, SUM(CASE WHEN auto_execute = 1 THEN 1 ELSE 0 END) as auto_execute_enabled, COUNT(DISTINCT trigger_type) as unique_triggers, COUNT(DISTINCT severity) as unique_severities FROM response_protocols`, [], (err, row) => { if (err) reject(err); else resolve(row); } ); }); } } // Create singleton instance const responseProtocolManager = new ResponseProtocolManager(); module.exports = responseProtocolManager;