/**
* Security Monitoring and Alerting System
* Provides real-time security monitoring, threat detection, and incident response
*/
import { EventEmitter } from "events";
import { SecurityUtils } from "./SecurityConfig.js";
import { LoggerFactory } from "@/utils/logger.js";
const logger = LoggerFactory.security();
export interface SecurityEvent {
id: string;
timestamp: Date;
type: "authentication" | "authorization" | "input-validation" | "data-access" | "system" | "anomaly";
severity: "critical" | "high" | "medium" | "low";
source: string;
details: {
userId?: string;
sessionId?: string;
ipAddress?: string;
userAgent?: string;
endpoint?: string;
method?: string;
payload?: unknown;
error?: string;
metadata?: Record<string, unknown>;
};
description: string;
riskScore: number;
handled: boolean;
actions: SecurityAction[];
}
interface SecurityAction {
id: string;
type: "block" | "throttle" | "alert" | "log" | "investigate" | "escalate";
timestamp: Date;
automated: boolean;
result: "success" | "failure" | "pending";
details: string;
}
interface SecurityAlert {
id: string;
eventId: string;
timestamp: Date;
severity: "critical" | "high" | "medium" | "low";
title: string;
description: string;
category: string;
affectedSystems: string[];
indicators: {
type: string;
value: string;
confidence: number;
}[];
recommendations: string[];
status: "new" | "investigating" | "resolved" | "false-positive";
assignee?: string;
resolution?: {
timestamp: Date;
action: string;
details: string;
};
}
interface ThreatIntelligence {
id: string;
type: "ip" | "domain" | "hash" | "pattern" | "signature";
value: string;
confidence: number;
severity: "critical" | "high" | "medium" | "low";
source: string;
description: string;
indicators: string[];
lastSeen: Date;
expiry?: Date;
}
interface SecurityMetrics {
timestamp: Date;
events: {
total: number;
byType: Record<string, number>;
bySeverity: Record<string, number>;
};
threats: {
blocked: number;
detected: number;
investigated: number;
};
alerts: {
new: number;
resolved: number;
falsePositives: number;
};
performance: {
responseTime: number;
detectionRate: number;
falsePositiveRate: number;
};
}
interface AnomalyPattern {
id: string;
type: "traffic" | "authentication" | "access" | "behavior";
pattern: string;
threshold: number;
timeWindow: number;
description: string;
enabled: boolean;
}
/**
* Real-time Security Monitor
*/
export class SecurityMonitor extends EventEmitter {
private events: SecurityEvent[] = [];
private alerts: SecurityAlert[] = [];
private threatIntel: Map<string, ThreatIntelligence> = new Map();
private anomalyPatterns: AnomalyPattern[] = [];
private metrics: SecurityMetrics[] = [];
private isMonitoring = false;
private metricsInterval?: NodeJS.Timeout | undefined;
constructor() {
super();
this.initializeAnomalyPatterns();
}
/**
* Start security monitoring
*/
start(): void {
if (this.isMonitoring) {
logger.warn("Already monitoring");
return;
}
this.isMonitoring = true;
logger.info("Starting security monitoring");
// Start metrics collection
this.metricsInterval = setInterval(() => {
this.collectMetrics();
}, 60000); // Every minute
this.emit("monitoring-started");
}
/**
* Stop security monitoring
*/
stop(): void {
if (!this.isMonitoring) {
logger.warn("Not currently monitoring");
return;
}
this.isMonitoring = false;
if (this.metricsInterval) {
clearInterval(this.metricsInterval);
this.metricsInterval = undefined;
}
logger.info("Stopped security monitoring");
this.emit("monitoring-stopped");
}
/**
* Log security event
*/
async logSecurityEvent(
eventData: Omit<SecurityEvent, "id" | "timestamp" | "handled" | "actions">,
): Promise<SecurityEvent> {
const event: SecurityEvent = {
...eventData,
id: SecurityUtils.generateSecureToken(16),
timestamp: new Date(),
handled: false,
actions: [],
};
this.events.push(event);
// Process event in real-time
await this.processSecurityEvent(event);
// Check for anomalies
await this.checkForAnomalies(event);
// Emit event for real-time processing
this.emit("security-event", event);
logger.info(`Logged ${event.severity} event: ${event.type} - ${event.description}`);
return event;
}
/**
* Process security event and take automated actions
*/
private async processSecurityEvent(event: SecurityEvent): Promise<void> {
const actions: SecurityAction[] = [];
// High-severity events get immediate attention
if (event.severity === "critical" || event.severity === "high") {
actions.push(await this.createAction("alert", event, true));
if (event.type === "authentication" && event.details.ipAddress) {
// Consider blocking suspicious IPs
const suspiciousActivity = await this.checkSuspiciousActivity(event.details.ipAddress);
if (suspiciousActivity) {
actions.push(await this.createAction("block", event, true));
}
}
}
// Rate limiting for authentication events
if (event.type === "authentication" && event.details.userId) {
const failedAttempts = await this.getFailedAuthAttempts(event.details.userId);
if (failedAttempts > 5) {
actions.push(await this.createAction("throttle", event, true));
}
}
// Check against threat intelligence
if (event.details.ipAddress) {
const threat = this.threatIntel.get(event.details.ipAddress);
if (threat) {
actions.push(await this.createAction("block", event, true));
actions.push(await this.createAction("alert", event, true));
}
}
event.actions = actions;
event.handled = actions.length > 0;
// Create alert if necessary
if (event.severity === "critical" || (event.severity === "high" && event.riskScore > 7)) {
await this.createAlert(event);
}
}
/**
* Create security action
*/
private async createAction(
type: SecurityAction["type"],
event: SecurityEvent,
automated: boolean,
): Promise<SecurityAction> {
const action: SecurityAction = {
id: SecurityUtils.generateSecureToken(12),
type,
timestamp: new Date(),
automated,
result: "pending",
details: `${type} action triggered for ${event.type} event`,
};
try {
// Execute the action
switch (type) {
case "block":
await this.executeBlockAction(event);
break;
case "throttle":
await this.executeThrottleAction(event);
break;
case "alert":
await this.executeAlertAction(event);
break;
case "log":
await this.executeLogAction(event);
break;
default:
logger.info(`Action ${type} queued for manual processing`, { type });
}
action.result = "success";
action.details += " - executed successfully";
} catch (_error) {
action.result = "failure";
action.details += ` - failed: ${_error instanceof Error ? _error.message : String(_error)}`;
logger.error(`Action ${type} failed`, { type, _error });
}
return action;
}
/**
* Execute block action
*/
private async executeBlockAction(event: SecurityEvent): Promise<void> {
if (event.details.ipAddress) {
logger.info(`Blocking IP: ${event.details.ipAddress}`);
// In a real implementation, this would interface with firewall/load balancer
this.emit("ip-blocked", { ip: event.details.ipAddress, reason: event.description });
}
}
/**
* Execute throttle action
*/
private async executeThrottleAction(event: SecurityEvent): Promise<void> {
if (event.details.userId) {
logger.info(`Throttling user: ${event.details.userId}`);
// In a real implementation, this would apply rate limiting
this.emit("user-throttled", { userId: event.details.userId, reason: event.description });
}
}
/**
* Execute alert action
*/
private async executeAlertAction(event: SecurityEvent): Promise<void> {
logger.info(`Alert triggered for event: ${event.id}`);
this.emit("security-alert", event);
}
/**
* Execute log action
*/
private async executeLogAction(event: SecurityEvent): Promise<void> {
logger.info(`Enhanced logging for event: ${event.id}`);
// Additional detailed logging would go here
}
/**
* Create security alert
*/
private async createAlert(event: SecurityEvent): Promise<SecurityAlert> {
const alert: SecurityAlert = {
id: SecurityUtils.generateSecureToken(16),
eventId: event.id,
timestamp: new Date(),
severity: event.severity,
title: `Security Alert: ${event.type}`,
description: event.description,
category: event.type,
affectedSystems: [event.source],
indicators: this.extractIndicators(event),
recommendations: this.generateRecommendations(event),
status: "new",
};
this.alerts.push(alert);
logger.info(`Created ${alert.severity} alert: ${alert.title}`);
this.emit("alert-created", alert);
return alert;
}
/**
* Extract indicators from security event
*/
private extractIndicators(event: SecurityEvent): SecurityAlert["indicators"] {
const indicators: SecurityAlert["indicators"] = [];
if (event.details.ipAddress) {
indicators.push({
type: "ip-address",
value: event.details.ipAddress,
confidence: 0.8,
});
}
if (event.details.userAgent) {
indicators.push({
type: "user-agent",
value: event.details.userAgent,
confidence: 0.6,
});
}
if (event.details.endpoint) {
indicators.push({
type: "endpoint",
value: event.details.endpoint,
confidence: 0.7,
});
}
return indicators;
}
/**
* Generate recommendations for event
*/
private generateRecommendations(event: SecurityEvent): string[] {
const recommendations: string[] = [];
switch (event.type) {
case "authentication":
recommendations.push("Review authentication logs for patterns");
recommendations.push("Consider implementing multi-factor authentication");
if (event.severity === "high" || event.severity === "critical") {
recommendations.push("Temporarily block suspicious IP addresses");
}
break;
case "authorization":
recommendations.push("Review user permissions and access controls");
recommendations.push("Audit privilege escalation attempts");
break;
case "input-validation":
recommendations.push("Review input validation rules");
recommendations.push("Update WAF rules if applicable");
break;
case "data-access":
recommendations.push("Review data access patterns");
recommendations.push("Implement data loss prevention measures");
break;
case "system":
recommendations.push("Check system integrity");
recommendations.push("Review system logs for correlating events");
break;
case "anomaly":
recommendations.push("Investigate unusual patterns");
recommendations.push("Tune anomaly detection thresholds");
break;
}
return recommendations;
}
/**
* Check for suspicious activity from IP
*/
private async checkSuspiciousActivity(ipAddress: string): Promise<boolean> {
const recentEvents = this.events.filter(
(event) => event.details.ipAddress === ipAddress && event.timestamp.getTime() > Date.now() - 3600000, // Last hour
);
// Multiple failed authentication attempts
const failedAuth = recentEvents.filter((event) => event.type === "authentication" && event.severity === "high");
if (failedAuth.length > 5) {
return true;
}
// Multiple different user attempts from same IP
const userIds = new Set(recentEvents.map((event) => event.details.userId).filter(Boolean));
if (userIds.size > 10) {
return true;
}
return false;
}
/**
* Get failed authentication attempts for user
*/
private async getFailedAuthAttempts(userId: string): Promise<number> {
const recentEvents = this.events.filter(
(event) =>
event.type === "authentication" &&
event.details.userId === userId &&
event.severity === "high" &&
event.timestamp.getTime() > Date.now() - 900000, // Last 15 minutes
);
return recentEvents.length;
}
/**
* Check for anomalies
*/
private async checkForAnomalies(event: SecurityEvent): Promise<void> {
for (const pattern of this.anomalyPatterns) {
if (!pattern.enabled) continue;
if (await this.matchesAnomalyPattern(event, pattern)) {
await this.logSecurityEvent({
type: "anomaly",
severity: "medium",
source: "anomaly-detection",
description: `Anomaly detected: ${pattern.description}`,
riskScore: 5,
details: {
metadata: { pattern: pattern.pattern, patternId: pattern.id, originalEvent: event.id },
},
});
}
}
}
/**
* Check if event matches anomaly pattern
*/
private async matchesAnomalyPattern(event: SecurityEvent, pattern: AnomalyPattern): Promise<boolean> {
// Simplified pattern matching - in practice this would be more sophisticated
const timeWindow = Date.now() - pattern.timeWindow;
const relatedEvents = this.events.filter((e) => e.timestamp.getTime() > timeWindow && e.type === event.type);
switch (pattern.type) {
case "traffic":
return relatedEvents.length > pattern.threshold;
case "authentication":
return event.type === "authentication" && relatedEvents.length > pattern.threshold;
case "access":
return event.type === "data-access" && relatedEvents.length > pattern.threshold;
case "behavior":
// Check for unusual user behavior patterns
if (event.details.userId) {
const userEvents = relatedEvents.filter((e) => e.details.userId === event.details.userId);
return userEvents.length > pattern.threshold;
}
return false;
default:
return false;
}
}
/**
* Initialize default anomaly patterns
*/
private initializeAnomalyPatterns(): void {
this.anomalyPatterns = [
{
id: "high-auth-failures",
type: "authentication",
pattern: "failed-auth-attempts",
threshold: 10,
timeWindow: 300000, // 5 minutes
description: "High number of authentication failures",
enabled: true,
},
{
id: "unusual-access-pattern",
type: "access",
pattern: "data-access-spike",
threshold: 50,
timeWindow: 900000, // 15 minutes
description: "Unusual data access pattern",
enabled: true,
},
{
id: "traffic-spike",
type: "traffic",
pattern: "request-volume",
threshold: 100,
timeWindow: 300000, // 5 minutes
description: "Traffic volume spike",
enabled: true,
},
{
id: "user-behavior-anomaly",
type: "behavior",
pattern: "user-activity",
threshold: 20,
timeWindow: 3600000, // 1 hour
description: "Unusual user behavior pattern",
enabled: true,
},
];
}
/**
* Collect security metrics
*/
private collectMetrics(): void {
const now = new Date();
const hourAgo = new Date(now.getTime() - 3600000);
const recentEvents = this.events.filter((event) => event.timestamp > hourAgo);
const recentAlerts = this.alerts.filter((alert) => alert.timestamp > hourAgo);
const metrics: SecurityMetrics = {
timestamp: now,
events: {
total: recentEvents.length,
byType: this.groupBy(recentEvents, "type"),
bySeverity: this.groupBy(recentEvents, "severity"),
},
threats: {
blocked: recentEvents.filter((e) => e.actions.some((a) => a.type === "block")).length,
detected: recentEvents.filter((e) => e.riskScore > 5).length,
investigated: recentAlerts.filter((a) => a.status === "investigating").length,
},
alerts: {
new: recentAlerts.filter((a) => a.status === "new").length,
resolved: recentAlerts.filter((a) => a.status === "resolved").length,
falsePositives: recentAlerts.filter((a) => a.status === "false-positive").length,
},
performance: {
responseTime: this.calculateAverageResponseTime(recentEvents),
detectionRate: this.calculateDetectionRate(recentEvents),
falsePositiveRate: this.calculateFalsePositiveRate(recentAlerts),
},
};
this.metrics.push(metrics);
// Keep only last 24 hours of metrics
const dayAgo = new Date(now.getTime() - 86400000);
this.metrics = this.metrics.filter((m) => m.timestamp > dayAgo);
this.emit("metrics-collected", metrics);
}
/**
* Group array by property
*/
private groupBy(array: SecurityEvent[], property: string): Record<string, number> {
return array.reduce<Record<string, number>>((acc, item) => {
const key = String((item as unknown as Record<string, unknown>)[property]) || "unknown";
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {});
}
/**
* Calculate average response time for security events
*/
private calculateAverageResponseTime(events: SecurityEvent[]): number {
const handledEvents = events.filter((e) => e.handled && e.actions.length > 0);
if (handledEvents.length === 0) return 0;
const totalTime = handledEvents.reduce((sum, event) => {
const firstAction = event.actions[0];
if (firstAction) {
return sum + (firstAction.timestamp.getTime() - event.timestamp.getTime());
}
return sum;
}, 0);
return totalTime / handledEvents.length;
}
/**
* Calculate detection rate
*/
private calculateDetectionRate(events: SecurityEvent[]): number {
const totalEvents = events.length;
if (totalEvents === 0) return 1;
const detectedEvents = events.filter((e) => e.handled || e.riskScore > 3).length;
return detectedEvents / totalEvents;
}
/**
* Calculate false positive rate
*/
private calculateFalsePositiveRate(alerts: SecurityAlert[]): number {
const totalAlerts = alerts.length;
if (totalAlerts === 0) return 0;
const falsePositives = alerts.filter((a) => a.status === "false-positive").length;
return falsePositives / totalAlerts;
}
/**
* Add threat intelligence
*/
addThreatIntelligence(threat: ThreatIntelligence): void {
this.threatIntel.set(threat.value, threat);
logger.info(`Added threat intelligence: ${threat.type} - ${threat.value}`);
}
/**
* Remove threat intelligence
*/
removeThreatIntelligence(value: string): boolean {
const removed = this.threatIntel.delete(value);
if (removed) {
logger.info(`Removed threat intelligence: ${value}`);
}
return removed;
}
/**
* Update alert status
*/
updateAlertStatus(alertId: string, status: SecurityAlert["status"], assignee?: string): boolean {
const alert = this.alerts.find((a) => a.id === alertId);
if (!alert) {
return false;
}
alert.status = status;
if (assignee) {
alert.assignee = assignee;
}
if (status === "resolved") {
alert.resolution = {
timestamp: new Date(),
action: "manual-resolution",
details: "Alert marked as resolved",
};
}
logger.info(`Updated alert ${alertId} status to ${status}`);
this.emit("alert-updated", alert);
return true;
}
/**
* Get security events
*/
getEvents(
options: {
limit?: number;
offset?: number;
severity?: string;
type?: string;
since?: Date;
} = {},
): SecurityEvent[] {
let events = [...this.events];
if (options.since) {
events = events.filter((e) => e.timestamp >= options.since!);
}
if (options.severity) {
events = events.filter((e) => e.severity === options.severity);
}
if (options.type) {
events = events.filter((e) => e.type === options.type);
}
// Sort by timestamp (newest first)
events.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
if (options.offset) {
events = events.slice(options.offset);
}
if (options.limit) {
events = events.slice(0, options.limit);
}
return events;
}
/**
* Get security alerts
*/
getAlerts(
options: {
limit?: number;
offset?: number;
severity?: string;
status?: string;
since?: Date;
} = {},
): SecurityAlert[] {
let alerts = [...this.alerts];
if (options.since) {
alerts = alerts.filter((a) => a.timestamp >= options.since!);
}
if (options.severity) {
alerts = alerts.filter((a) => a.severity === options.severity);
}
if (options.status) {
alerts = alerts.filter((a) => a.status === options.status);
}
// Sort by timestamp (newest first)
alerts.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
if (options.offset) {
alerts = alerts.slice(options.offset);
}
if (options.limit) {
alerts = alerts.slice(0, options.limit);
}
return alerts;
}
/**
* Get security metrics
*/
getMetrics(
options: {
since?: Date;
until?: Date;
} = {},
): SecurityMetrics[] {
let metrics = [...this.metrics];
if (options.since) {
metrics = metrics.filter((m) => m.timestamp >= options.since!);
}
if (options.until) {
metrics = metrics.filter((m) => m.timestamp <= options.until!);
}
return metrics.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
}
/**
* Get system status
*/
getStatus(): {
monitoring: boolean;
eventsToday: number;
alertsOpen: number;
threatsBlocked: number;
systemHealth: "healthy" | "degraded" | "critical";
} {
const today = new Date();
today.setHours(0, 0, 0, 0);
const eventsToday = this.events.filter((e) => e.timestamp >= today).length;
const alertsOpen = this.alerts.filter((a) => a.status === "new" || a.status === "investigating").length;
const threatsBlocked = this.events.filter(
(e) => e.timestamp >= today && e.actions.some((a) => a.type === "block"),
).length;
let systemHealth: "healthy" | "degraded" | "critical" = "healthy";
if (alertsOpen > 10) systemHealth = "critical";
else if (alertsOpen > 5 || eventsToday > 100) systemHealth = "degraded";
return {
monitoring: this.isMonitoring,
eventsToday,
alertsOpen,
threatsBlocked,
systemHealth,
};
}
}