@iota-big3/sdk-security
Version:
Advanced security features including zero trust, quantum-safe crypto, and ML threat detection
1,079 lines (1,075 loc) • 40.5 kB
JavaScript
;
/**
* Incident Response Manager
* Core incident management and response coordination
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.IncidentResponseManager = void 0;
const tslib_1 = require("tslib");
const crypto = tslib_1.__importStar(require("crypto"));
const events_1 = require("events");
const path = tslib_1.__importStar(require("path"));
const playbook_library_1 = require("./playbooks/playbook-library");
const types_1 = require("./types");
class IncidentResponseManager extends events_1.EventEmitter {
constructor(config = {}) {
super();
this.incidents = new Map();
this.activePlaybooks = new Map();
this.responderTeams = new Map();
this.notificationQueue = [];
this.isInitialized = false;
this.config = {
storagePath: config.storagePath || './incidents',
autoEscalation: config.autoEscalation ?? true,
retentionDays: config.retentionDays || 365,
...config
};
}
/**
* Initialize the incident response manager
*/
async initialize() {
if (this.isInitialized)
return;
try {
// Load existing incidents
await this.loadIncidents();
// Initialize notification system
this.initializeNotifications();
// Start background processes
this.startBackgroundProcesses();
this.isInitialized = true;
this.emit('initialized');
}
catch (error) {
this.emit('error', { error, operation: 'initialize' });
throw error;
}
}
/**
* Create a new incident
*/
async createIncident(incidentData) {
const id = this.generateIncidentId();
const incident = {
id,
title: incidentData.title || 'Untitled Incident',
description: incidentData.description || '',
type: incidentData.type || types_1.IncidentType.OTHER,
severity: incidentData.severity || types_1.IncidentSeverity.MEDIUM,
status: types_1.IncidentStatus.DETECTED,
source: incidentData.source || types_1.IncidentSource.AUTOMATED_DETECTION,
// Timing
detectedAt: new Date(),
// Impact
affectedSystems: incidentData.affectedSystems || [],
affectedUsers: incidentData.affectedUsers || [],
affectedData: incidentData.affectedData,
businessImpact: incidentData.businessImpact || this.assessInitialImpact(incidentData),
// Response team
responseTeam: [],
// Evidence and timeline
evidence: [],
artifacts: [],
timeline: [],
// Actions
containmentActions: [],
eradicationActions: [],
recoveryActions: [],
// Metadata
tags: incidentData.tags || [],
customFields: incidentData.customFields,
compliance: incidentData.compliance,
...incidentData
};
// Add initial timeline event
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.DETECTION,
title: 'Incident Detected',
description: `Incident detected from source: ${incident.source}`,
automated: true
});
// Store incident
this.incidents.set(id, incident);
// Auto-assign if configured
if (this.config.autoEscalation) {
await this.autoAssignResponders(incident);
}
// Check for applicable playbooks
const recommendedPlaybook = playbook_library_1.PlaybookLibrary.getRecommendedPlaybook(incident.type, incident.severity);
if (recommendedPlaybook) {
this.emit('playbook:recommended', {
incidentId: id,
playbook: recommendedPlaybook
});
}
// Send notifications
await this.sendIncidentNotification(incident, 'created');
// Integrate with SIEM if configured
if (this.config.integrations?.siem) {
await this.pushToSIEM(incident);
}
this.emit('incident:created', incident);
return incident;
}
/**
* Update an existing incident
*/
async updateIncident(id, updates) {
const incident = this.incidents.get(id);
if (!incident) {
throw new Error(`Incident ${id} not found`);
}
const previousStatus = incident.status;
const updatedIncident = {
...incident,
...updates
};
// Update timing based on status changes
if (updates.status && updates.status !== previousStatus) {
switch (updates.status) {
case types_1.IncidentStatus.TRIAGED:
updatedIncident.triageStartedAt = new Date();
break;
case types_1.IncidentStatus.CONTAINED:
updatedIncident.containedAt = new Date();
break;
case types_1.IncidentStatus.RESOLVED:
updatedIncident.resolvedAt = new Date();
break;
case types_1.IncidentStatus.CLOSED:
updatedIncident.closedAt = new Date();
break;
}
// Add timeline event for status change
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.STATUS_CHANGE,
title: 'Status Updated',
description: `Status changed from ${previousStatus} to ${updates.status}`,
automated: false
});
}
this.incidents.set(id, updatedIncident);
// Check escalation criteria
if (this.config.autoEscalation && updatedIncident.escalationPath) {
await this.checkEscalationCriteria(updatedIncident);
}
this.emit('incident:updated', {
incident: updatedIncident,
changes: updates
});
return updatedIncident;
}
/**
* Get incident by ID
*/
async getIncident(id) {
const incident = this.incidents.get(id);
if (!incident) {
throw new Error(`Incident ${id} not found`);
}
return incident;
}
/**
* List incidents with optional filtering
*/
async listIncidents(filter) {
let incidents = Array.from(this.incidents.values());
if (filter) {
// Apply status filter
if (filter.status && filter.status.length > 0) {
incidents = incidents.filter(i => filter.status.includes(i.status));
}
// Apply severity filter
if (filter.severity && filter.severity.length > 0) {
incidents = incidents.filter(i => filter.severity.includes(i.severity));
}
// Apply type filter
if (filter.type && filter.type.length > 0) {
incidents = incidents.filter(i => filter.type.includes(i.type));
}
// Apply assignee filter
if (filter.assignedTo) {
incidents = incidents.filter(i => i.assignedTo?.id === filter.assignedTo);
}
// Apply date range filter
if (filter.dateRange) {
incidents = incidents.filter(i => i.detectedAt >= filter.dateRange.start &&
i.detectedAt <= filter.dateRange.end);
}
// Apply tag filter
if (filter.tags && filter.tags.length > 0) {
incidents = incidents.filter(i => filter.tags.some(tag => i.tags.includes(tag)));
}
// Apply search term
if (filter.searchTerm) {
const searchLower = filter.searchTerm.toLowerCase();
incidents = incidents.filter(i => i.title.toLowerCase().includes(searchLower) ||
i.description.toLowerCase().includes(searchLower));
}
}
// Sort by detection date (newest first)
incidents.sort((a, b) => b.detectedAt.getTime() - a.detectedAt.getTime());
return incidents;
}
/**
* Triage an incident
*/
async triageIncident(id, severity) {
await this.updateIncident(id, {
severity,
status: types_1.IncidentStatus.TRIAGED
});
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.TRIAGE,
title: 'Incident Triaged',
description: `Severity set to ${severity}`,
severity,
automated: false
});
this.emit('incident:triaged', { id, severity });
}
/**
* Update incident status
*/
async updateStatus(id, status) {
await this.updateIncident(id, { status });
}
/**
* Resolve an incident
*/
async resolveIncident(id, resolution) {
const incident = await this.getIncident(id);
await this.updateIncident(id, {
status: types_1.IncidentStatus.RESOLVED,
customFields: {
...incident.customFields,
resolution
}
});
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.RECOVERY,
title: 'Incident Resolved',
description: resolution,
automated: false
});
// Send resolution notification
await this.sendIncidentNotification(incident, 'resolved');
this.emit('incident:resolved', { id, resolution });
}
/**
* Close an incident
*/
async closeIncident(id, report) {
await this.updateIncident(id, {
status: types_1.IncidentStatus.CLOSED
});
if (report) {
// Store the final report
const incident = await this.getIncident(id);
incident.customFields = {
...incident.customFields,
finalReport: report
};
}
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.STATUS_CHANGE,
title: 'Incident Closed',
description: 'Incident closed after post-incident activities',
automated: false
});
this.emit('incident:closed', { id });
}
/**
* Assign incident to responder
*/
async assignIncident(id, responder) {
await this.updateIncident(id, {
assignedTo: responder
});
await this.addResponder(id, responder);
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.STATUS_CHANGE,
title: 'Incident Assigned',
description: `Assigned to ${responder.name} (${responder.role})`,
actor: responder.name,
automated: false
});
// Notify the responder
await this.notifyResponder(responder, await this.getIncident(id), 'assigned');
this.emit('incident:assigned', { id, responder });
}
/**
* Add responder to team
*/
async addResponder(id, responder) {
const incident = await this.getIncident(id);
if (!incident.responseTeam.find(r => r.id === responder.id)) {
incident.responseTeam.push(responder);
await this.updateIncident(id, { responseTeam: incident.responseTeam });
}
}
/**
* Remove responder from team
*/
async removeResponder(id, responderId) {
const incident = await this.getIncident(id);
incident.responseTeam = incident.responseTeam.filter(r => r.id !== responderId);
await this.updateIncident(id, { responseTeam: incident.responseTeam });
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.STATUS_CHANGE,
title: 'Responder Removed',
description: `Responder ${responderId} removed from response team`,
automated: false
});
}
/**
* Escalate incident
*/
async escalateIncident(id, reason) {
const incident = await this.getIncident(id);
if (!incident.escalationPath) {
throw new Error('No escalation path defined for this incident');
}
const currentLevel = incident.escalationPath.currentLevel;
const nextLevel = currentLevel + 1;
if (nextLevel >= incident.escalationPath.levels.length) {
throw new Error('Already at highest escalation level');
}
// Update escalation level
incident.escalationPath.currentLevel = nextLevel;
await this.updateIncident(id, { escalationPath: incident.escalationPath });
// Add new responders from next level
const nextLevelConfig = incident.escalationPath.levels[nextLevel];
for (const responder of nextLevelConfig.responders) {
await this.addResponder(id, responder);
}
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.ESCALATION,
title: 'Incident Escalated',
description: reason || `Escalated to level ${nextLevel}`,
severity: incident.severity,
automated: !reason
});
// Notify escalation
await this.sendEscalationNotifications(incident, nextLevelConfig);
this.emit('incident:escalated', { id, level: nextLevel, reason });
}
/**
* Add evidence
*/
async addEvidence(id, evidence) {
const incident = await this.getIncident(id);
// Ensure chain of custody
if (!evidence.chainOfCustody || evidence.chainOfCustody.length === 0) {
evidence.chainOfCustody = [{
timestamp: new Date(),
custodian: evidence.collectedBy,
action: 'Initial Collection',
location: evidence.location
}];
}
incident.evidence.push(evidence);
await this.updateIncident(id, { evidence: incident.evidence });
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.EVIDENCE_COLLECTED,
title: 'Evidence Collected',
description: `${evidence.type}: ${evidence.title}`,
actor: evidence.collectedBy,
evidence: [evidence.id],
automated: false
});
this.emit('evidence:added', { incidentId: id, evidence });
}
/**
* Add artifact
*/
async addArtifact(id, file) {
const incident = await this.getIncident(id);
// Create artifact metadata
const artifact = {
id: crypto.randomUUID(),
name: file.name,
type: file.type,
mimeType: file.type,
size: file.size,
hash: await this.calculateFileHash(file),
uploadedAt: new Date(),
uploadedBy: 'current-user', // Would come from auth context
scanStatus: {
scanned: false,
malicious: false
}
};
// Store file (mock implementation)
const storagePath = path.join(this.config.storagePath, id, 'artifacts', artifact.id);
// In production, would actually store the file
artifact.metadata = { storagePath };
incident.artifacts.push(artifact);
await this.updateIncident(id, { artifacts: incident.artifacts });
// Queue for scanning
this.queueArtifactScan(id, artifact);
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.EVIDENCE_COLLECTED,
title: 'Artifact Uploaded',
description: `File: ${artifact.name} (${this.formatFileSize(artifact.size)})`,
automated: false
});
this.emit('artifact:added', { incidentId: id, artifact });
return artifact;
}
/**
* Get evidence
*/
async getEvidence(id, evidenceId) {
const incident = await this.getIncident(id);
const evidence = incident.evidence.find(e => e.id === evidenceId);
if (!evidence) {
throw new Error(`Evidence ${evidenceId} not found`);
}
return evidence;
}
/**
* Add timeline event
*/
async addTimelineEvent(id, event) {
const incident = await this.getIncident(id);
incident.timeline.push(event);
incident.timeline.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
await this.updateIncident(id, { timeline: incident.timeline });
this.emit('timeline:updated', { incidentId: id, event });
}
/**
* Get timeline
*/
async getTimeline(id) {
const incident = await this.getIncident(id);
return incident.timeline;
}
/**
* Execute action
*/
async executeAction(id, action) {
const incident = await this.getIncident(id);
// Set initial status
action.status = types_1.ActionStatus.IN_PROGRESS;
try {
// Execute based on action type
await this.performAction(incident, action);
action.status = types_1.ActionStatus.COMPLETED;
action.result = 'Success';
}
catch (error) {
action.status = types_1.ActionStatus.FAILED;
action.result = error.message;
}
// Store action in appropriate category
switch (action.type) {
case types_1.ActionType.ISOLATE_SYSTEM:
case types_1.ActionType.BLOCK_IP:
case types_1.ActionType.UPDATE_FIREWALL:
incident.containmentActions.push(action);
break;
case types_1.ActionType.QUARANTINE_FILE:
case types_1.ActionType.DISABLE_ACCOUNT:
case types_1.ActionType.RESET_PASSWORD:
incident.eradicationActions.push(action);
break;
case types_1.ActionType.RESTORE_BACKUP:
case types_1.ActionType.PATCH_SYSTEM:
incident.recoveryActions.push(action);
break;
}
await this.updateIncident(id, {
containmentActions: incident.containmentActions,
eradicationActions: incident.eradicationActions,
recoveryActions: incident.recoveryActions
});
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.ACTION_TAKEN,
title: `Action Executed: ${action.title}`,
description: action.description,
actor: action.executedBy,
automated: action.automated
});
this.emit('action:executed', { incidentId: id, action });
}
/**
* Execute playbook
*/
async executePlaybook(id, playbookId) {
const incident = await this.getIncident(id);
const playbook = playbook_library_1.PlaybookLibrary.getPlaybook(playbookId);
if (!playbook) {
throw new Error(`Playbook ${playbookId} not found`);
}
// Create playbook execution
const execution = {
incidentId: id,
playbook,
startedAt: new Date(),
currentPhase: 0,
currentStep: 0,
status: 'running',
results: []
};
this.activePlaybooks.set(id, execution);
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.ACTION_TAKEN,
title: 'Playbook Started',
description: `Executing playbook: ${playbook.name}`,
automated: playbook.automated
});
// Execute playbook phases
for (const phase of playbook.phases) {
execution.currentPhase = phase.order - 1;
for (const step of phase.steps) {
execution.currentStep = phase.steps.indexOf(step);
try {
await this.executePlaybookStep(incident, step, execution);
}
catch (error) {
execution.status = 'failed';
execution.error = error.message;
this.emit('playbook:failed', {
incidentId: id,
playbook,
error
});
throw error;
}
}
}
execution.status = 'completed';
execution.completedAt = new Date();
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.ACTION_TAKEN,
title: 'Playbook Completed',
description: `Successfully executed playbook: ${playbook.name}`,
automated: playbook.automated
});
this.emit('playbook:completed', {
incidentId: id,
playbook,
execution
});
}
/**
* Rollback action
*/
async rollbackAction(id, actionId) {
const incident = await this.getIncident(id);
// Find the action
let action;
let actionList;
for (const list of [
incident.containmentActions,
incident.eradicationActions,
incident.recoveryActions
]) {
action = list.find(a => a.id === actionId);
if (action) {
actionList = list;
break;
}
}
if (!action || !actionList) {
throw new Error(`Action ${actionId} not found`);
}
if (!action.rollbackable) {
throw new Error('Action is not rollbackable');
}
// Execute rollback
const rollbackAction = {
id: crypto.randomUUID(),
type: types_1.ActionType.CUSTOM,
title: `Rollback: ${action.title}`,
description: `Rolling back action: ${action.description}`,
executedAt: new Date(),
executedBy: 'current-user',
status: types_1.ActionStatus.IN_PROGRESS,
automated: false,
playbook: action.playbook
};
try {
// Perform rollback
await this.performRollback(incident, action);
rollbackAction.status = types_1.ActionStatus.COMPLETED;
action.status = types_1.ActionStatus.ROLLED_BACK;
}
catch (error) {
rollbackAction.status = types_1.ActionStatus.FAILED;
rollbackAction.result = error.message;
throw error;
}
await this.updateIncident(id, {
containmentActions: incident.containmentActions,
eradicationActions: incident.eradicationActions,
recoveryActions: incident.recoveryActions
});
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.ACTION_TAKEN,
title: 'Action Rolled Back',
description: `Rolled back: ${action.title}`,
automated: false
});
this.emit('action:rolledback', { incidentId: id, action });
}
/**
* Notify responders
*/
async notifyResponders(id, template) {
const incident = await this.getIncident(id);
for (const responder of incident.responseTeam) {
await this.sendNotification(responder, incident, template);
}
await this.addTimelineEvent(id, {
timestamp: new Date(),
type: types_1.TimelineEventType.COMMUNICATION,
title: 'Team Notified',
description: `Sent notification: ${template.subject}`,
automated: false
});
}
/**
* Broadcast update
*/
async broadcastUpdate(id, message) {
const incident = await this.getIncident(id);
// Create broadcast template
const template = {
id: 'broadcast',
name: 'Status Update',
type: 'CUSTOM',
subject: `Update: ${incident.title}`,
body: message,
variables: []
};
await this.notifyResponders(id, template);
// Also send to stakeholders if configured
if (incident.customFields?.stakeholders) {
// Send to additional stakeholders
}
this.emit('broadcast:sent', { incidentId: id, message });
}
/**
* Generate report
*/
async generateReport(id, type) {
const incident = await this.getIncident(id);
const metrics = this.calculateMetrics(incident);
const report = {
incident,
summary: this.generateSummary(incident, type),
timeline: incident.timeline,
actionsTable: [
...incident.containmentActions,
...incident.eradicationActions,
...incident.recoveryActions
],
evidenceSummary: incident.evidence,
metrics,
generatedAt: new Date(),
generatedBy: 'current-user'
};
// Add specific content based on report type
switch (type) {
case types_1.ReportType.EXECUTIVE_SUMMARY:
report.summary = this.generateExecutiveSummary(incident, metrics);
break;
case types_1.ReportType.TECHNICAL_REPORT:
report.summary = this.generateTechnicalReport(incident);
break;
case types_1.ReportType.COMPLIANCE_REPORT:
report.summary = this.generateComplianceReport(incident);
break;
case types_1.ReportType.LESSONS_LEARNED:
report.lessonsLearned = this.extractLessonsLearned(incident);
report.recommendations = this.generateRecommendations(incident);
break;
}
this.emit('report:generated', { incidentId: id, type });
return report;
}
/**
* Export incident
*/
async exportIncident(id, format) {
const incident = await this.getIncident(id);
const report = await this.generateReport(id, types_1.ReportType.FULL_REPORT);
switch (format) {
case types_1.ExportFormat.JSON:
return Buffer.from(JSON.stringify(report, null, 2));
case types_1.ExportFormat.CSV:
return this.exportToCSV(report);
case types_1.ExportFormat.HTML:
return this.exportToHTML(report);
case types_1.ExportFormat.PDF:
case types_1.ExportFormat.DOCX:
// Would use libraries like puppeteer or docx
throw new Error(`Export format ${format} not implemented`);
default:
throw new Error(`Unknown export format: ${format}`);
}
}
/**
* Private helper methods
*/
generateIncidentId() {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const random = crypto.randomBytes(3).toString('hex').toUpperCase();
return `INC-${year}${month}-${random}`;
}
assessInitialImpact(data) {
// Simple impact assessment based on available data
return {
confidentiality: data.affectedData ? types_1.ImpactLevel.HIGH : types_1.ImpactLevel.MEDIUM,
integrity: types_1.ImpactLevel.MEDIUM,
availability: data.type === types_1.IncidentType.DENIAL_OF_SERVICE ?
types_1.ImpactLevel.HIGH : types_1.ImpactLevel.LOW,
regulatory: data.compliance ? true : false,
dataLoss: data.type === types_1.IncidentType.DATA_BREACH
};
}
async autoAssignResponders(incident) {
// Auto-assign based on incident type and severity
// In production, would use responder availability and skills
const mockResponder = {
id: 'resp-001',
name: 'Security Analyst',
role: 'SECURITY_ANALYST',
contactInfo: {
email: 'analyst@example.com'
},
availability: 'AVAILABLE'
};
await this.assignIncident(incident.id, mockResponder);
}
async checkEscalationCriteria(incident) {
if (!incident.escalationPath || !incident.escalationPath.autoEscalate) {
return;
}
const currentLevel = incident.escalationPath.levels[incident.escalationPath.currentLevel];
if (!currentLevel.criteria)
return;
// Check time-based escalation
if (currentLevel.criteria.timeElapsed) {
const elapsed = Date.now() - incident.detectedAt.getTime();
const threshold = currentLevel.criteria.timeElapsed * 60 * 1000;
if (elapsed > threshold) {
await this.escalateIncident(incident.id, `Auto-escalated after ${currentLevel.criteria.timeElapsed} minutes`);
}
}
}
async performAction(incident, action) {
// Mock implementation of actions
await new Promise(resolve => setTimeout(resolve, 1000));
// In production, would integrate with actual systems
switch (action.type) {
case types_1.ActionType.ISOLATE_SYSTEM:
console.log(`Isolating system: ${action.parameters?.system}`);
break;
case types_1.ActionType.BLOCK_IP:
console.log(`Blocking IP: ${action.parameters?.ip}`);
break;
// ... other action implementations
}
}
async performRollback(incident, action) {
// Mock rollback implementation
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(`Rolling back action: ${action.id}`);
}
async executePlaybookStep(incident, step, execution) {
// Check conditions
if (step.conditions) {
const conditionsMet = this.evaluateConditions(incident, step.conditions);
if (!conditionsMet) {
execution.results.push({
stepId: step.id,
status: 'skipped',
reason: 'Conditions not met'
});
return;
}
}
// Check if approval required
if (step.requiresApproval && !step.automated) {
// Would wait for approval in production
execution.results.push({
stepId: step.id,
status: 'pending_approval'
});
return;
}
// Execute the step action
const action = {
id: crypto.randomUUID(),
type: step.action,
title: step.title,
description: step.description,
executedAt: new Date(),
executedBy: 'playbook',
status: types_1.ActionStatus.PENDING,
automated: step.automated,
playbook: execution.playbook.id,
parameters: step.parameters
};
await this.executeAction(incident.id, action);
execution.results.push({
stepId: step.id,
status: 'completed',
actionId: action.id
});
}
evaluateConditions(incident, conditions) {
// Simple condition evaluation
// In production, would implement full condition logic
return true;
}
async sendIncidentNotification(incident, type) {
// Queue notification for processing
this.notificationQueue.push({
incidentId: incident.id,
type,
timestamp: new Date()
});
}
async sendEscalationNotifications(incident, level) {
for (const method of level.notificationMethods) {
// Send via each method
console.log(`Sending ${method} notification for escalation`);
}
}
async notifyResponder(responder, incident, type) {
// Send notification based on responder preferences
if (responder.contactInfo.email) {
// Send email
}
if (responder.contactInfo.slack) {
// Send Slack message
}
if (responder.contactInfo.pagerDuty) {
// Create PagerDuty incident
}
}
async sendNotification(responder, incident, template) {
// Process template variables
const processedBody = this.processTemplate(template.body, incident);
const processedSubject = this.processTemplate(template.subject, incident);
// Send via configured channels
console.log(`Sending notification to ${responder.name}: ${processedSubject}`);
}
processTemplate(template, incident) {
// Simple template processing
return template
.replace('{{incident.id}}', incident.id)
.replace('{{incident.title}}', incident.title)
.replace('{{incident.severity}}', incident.severity)
.replace('{{incident.type}}', incident.type);
}
calculateMetrics(incident) {
const detectedAt = incident.detectedAt.getTime();
const now = Date.now();
return {
timeToDetect: 0, // Would calculate from actual detection
timeToContain: incident.containedAt ?
(incident.containedAt.getTime() - detectedAt) / 60000 : undefined,
timeToResolve: incident.resolvedAt ?
(incident.resolvedAt.getTime() - detectedAt) / 60000 : undefined,
totalDuration: (now - detectedAt) / 60000,
affectedSystemsCount: incident.affectedSystems.length,
affectedUsersCount: incident.affectedUsers.length,
actionsExecuted: incident.containmentActions.length +
incident.eradicationActions.length +
incident.recoveryActions.length,
evidenceCollected: incident.evidence.length + incident.artifacts.length
};
}
generateSummary(incident, type) {
return `${type} report for incident ${incident.id}: ${incident.title}`;
}
generateExecutiveSummary(incident, metrics) {
return `
Executive Summary - ${incident.id}
Incident: ${incident.title}
Type: ${incident.type}
Severity: ${incident.severity}
Status: ${incident.status}
Business Impact:
- ${incident.affectedUsers.length} users affected
- ${incident.affectedSystems.length} systems compromised
- Total downtime: ${metrics.totalDuration} minutes
Response Time:
- Time to contain: ${metrics.timeToContain || 'N/A'} minutes
- Time to resolve: ${metrics.timeToResolve || 'N/A'} minutes
Key Actions Taken: ${metrics.actionsExecuted} response actions executed
Evidence Collected: ${metrics.evidenceCollected} pieces of evidence
`.trim();
}
generateTechnicalReport(incident) {
return `Technical analysis of ${incident.type} incident...`;
}
generateComplianceReport(incident) {
return `Compliance report for regulatory requirements...`;
}
extractLessonsLearned(incident) {
// Extract lessons from incident data
return [
'Improve detection capabilities',
'Update incident response procedures',
'Enhance team training'
];
}
generateRecommendations(incident) {
// Generate recommendations based on incident
return [
'Implement additional monitoring',
'Update security controls',
'Review access policies'
];
}
async calculateFileHash(file) {
// Mock hash calculation
return crypto.randomBytes(32).toString('hex');
}
formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
queueArtifactScan(incidentId, artifact) {
// Queue for malware scanning
setTimeout(() => {
artifact.scanStatus = {
scanned: true,
scanDate: new Date(),
malicious: false,
scanEngine: 'Mock Scanner'
};
this.emit('artifact:scanned', { incidentId, artifact });
}, 5000);
}
async pushToSIEM(incident) {
// Push incident to SIEM
console.log(`Pushing incident ${incident.id} to SIEM`);
}
async loadIncidents() {
// Load incidents from storage
// Mock implementation
}
initializeNotifications() {
// Initialize notification channels
setInterval(() => this.processNotificationQueue(), 5000);
}
startBackgroundProcesses() {
// Start background processes
setInterval(() => this.checkAutoEscalations(), 60000); // Every minute
setInterval(() => this.cleanupOldIncidents(), 86400000); // Daily
}
async processNotificationQueue() {
while (this.notificationQueue.length > 0) {
const item = this.notificationQueue.shift();
if (item) {
// Process notification
console.log(`Processing notification for incident ${item.incidentId}`);
}
}
}
async checkAutoEscalations() {
// Check all active incidents for escalation criteria
const activeIncidents = await this.listIncidents({
status: [
types_1.IncidentStatus.DETECTED,
types_1.IncidentStatus.TRIAGED,
types_1.IncidentStatus.INVESTIGATING
]
});
for (const incident of activeIncidents) {
await this.checkEscalationCriteria(incident);
}
}
async cleanupOldIncidents() {
// Archive incidents older than retention period
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);
const oldIncidents = Array.from(this.incidents.values())
.filter(i => i.status === types_1.IncidentStatus.CLOSED &&
i.closedAt &&
i.closedAt < cutoffDate);
for (const incident of oldIncidents) {
// Archive and remove
console.log(`Archiving old incident: ${incident.id}`);
this.incidents.delete(incident.id);
}
}
exportToCSV(report) {
// Simple CSV export
const lines = [
'Incident ID,Title,Type,Severity,Status,Detected,Resolved',
`${report.incident.id},"${report.incident.title}",${report.incident.type},${report.incident.severity},${report.incident.status},${report.incident.detectedAt},${report.incident.resolvedAt || ''}`
];
return Buffer.from(lines.join('\n'));
}
exportToHTML(report) {
// Simple HTML export
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Incident Report - ${report.incident.id}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
.section { margin: 20px 0; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>Incident Report: ${report.incident.id}</h1>
<div class="section">
<h2>Summary</h2>
<p>${report.summary}</p>
</div>
<div class="section">
<h2>Details</h2>
<table>
<tr><th>Field</th><th>Value</th></tr>
<tr><td>Title</td><td>${report.incident.title}</td></tr>
<tr><td>Type</td><td>${report.incident.type}</td></tr>
<tr><td>Severity</td><td>${report.incident.severity}</td></tr>
<tr><td>Status</td><td>${report.incident.status}</td></tr>
</table>
</div>
</body>
</html>
`;
return Buffer.from(html);
}
}
exports.IncidentResponseManager = IncidentResponseManager;
//# sourceMappingURL=incident-response-manager.js.map