Initial commit: StreamFlow IPTV platform
This commit is contained in:
commit
73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions
858
backend/utils/responseProtocolManager.js
Normal file
858
backend/utils/responseProtocolManager.js
Normal file
|
|
@ -0,0 +1,858 @@
|
|||
/**
|
||||
* 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue