streamflow/backend/utils/responseProtocolManager.js

859 lines
27 KiB
JavaScript
Raw Permalink Normal View History

/**
* 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;