platformservice/ts/security/classes.securitylogger.ts
2025-05-07 20:20:17 +00:00

294 lines
8.3 KiB
TypeScript

import * as plugins from '../plugins.js';
import { logger } from '../logger.js';
/**
* Log level for security events
*/
export enum SecurityLogLevel {
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
CRITICAL = 'critical'
}
/**
* Security event types for categorization
*/
export enum SecurityEventType {
AUTHENTICATION = 'authentication',
ACCESS_CONTROL = 'access_control',
EMAIL_VALIDATION = 'email_validation',
DKIM = 'dkim',
SPF = 'spf',
DMARC = 'dmarc',
RATE_LIMIT = 'rate_limit',
SPAM = 'spam',
MALWARE = 'malware',
CONNECTION = 'connection',
DATA_EXPOSURE = 'data_exposure',
CONFIGURATION = 'configuration',
IP_REPUTATION = 'ip_reputation'
}
/**
* Security event interface
*/
export interface ISecurityEvent {
timestamp: number;
level: SecurityLogLevel;
type: SecurityEventType;
message: string;
details?: any;
ipAddress?: string;
userId?: string;
sessionId?: string;
emailId?: string;
domain?: string;
action?: string;
result?: string;
success?: boolean;
}
/**
* Security logger for enhanced security monitoring
*/
export class SecurityLogger {
private static instance: SecurityLogger;
private securityEvents: ISecurityEvent[] = [];
private maxEventHistory: number;
private enableNotifications: boolean;
private constructor(options?: {
maxEventHistory?: number;
enableNotifications?: boolean;
}) {
this.maxEventHistory = options?.maxEventHistory || 1000;
this.enableNotifications = options?.enableNotifications || false;
}
/**
* Get singleton instance
*/
public static getInstance(options?: {
maxEventHistory?: number;
enableNotifications?: boolean;
}): SecurityLogger {
if (!SecurityLogger.instance) {
SecurityLogger.instance = new SecurityLogger(options);
}
return SecurityLogger.instance;
}
/**
* Log a security event
* @param event The security event to log
*/
public logEvent(event: Omit<ISecurityEvent, 'timestamp'>): void {
const fullEvent: ISecurityEvent = {
...event,
timestamp: Date.now()
};
// Store in memory buffer
this.securityEvents.push(fullEvent);
// Trim history if needed
if (this.securityEvents.length > this.maxEventHistory) {
this.securityEvents.shift();
}
// Log to regular logger with appropriate level
switch (event.level) {
case SecurityLogLevel.INFO:
logger.log('info', `[SECURITY:${event.type}] ${event.message}`, event.details);
break;
case SecurityLogLevel.WARN:
logger.log('warn', `[SECURITY:${event.type}] ${event.message}`, event.details);
break;
case SecurityLogLevel.ERROR:
case SecurityLogLevel.CRITICAL:
logger.log('error', `[SECURITY:${event.type}] ${event.message}`, event.details);
// Send notification for critical events if enabled
if (event.level === SecurityLogLevel.CRITICAL && this.enableNotifications) {
this.sendNotification(fullEvent);
}
break;
}
}
/**
* Get recent security events
* @param limit Maximum number of events to return
* @param filter Filter for specific event types
* @returns Recent security events
*/
public getRecentEvents(limit: number = 100, filter?: {
level?: SecurityLogLevel;
type?: SecurityEventType;
fromTimestamp?: number;
toTimestamp?: number;
}): ISecurityEvent[] {
let filteredEvents = this.securityEvents;
// Apply filters
if (filter) {
if (filter.level) {
filteredEvents = filteredEvents.filter(event => event.level === filter.level);
}
if (filter.type) {
filteredEvents = filteredEvents.filter(event => event.type === filter.type);
}
if (filter.fromTimestamp) {
filteredEvents = filteredEvents.filter(event => event.timestamp >= filter.fromTimestamp);
}
if (filter.toTimestamp) {
filteredEvents = filteredEvents.filter(event => event.timestamp <= filter.toTimestamp);
}
}
// Return most recent events up to limit
return filteredEvents
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
}
/**
* Get events by security level
* @param level The security level to filter by
* @param limit Maximum number of events to return
* @returns Security events matching the level
*/
public getEventsByLevel(level: SecurityLogLevel, limit: number = 100): ISecurityEvent[] {
return this.getRecentEvents(limit, { level });
}
/**
* Get events by security type
* @param type The event type to filter by
* @param limit Maximum number of events to return
* @returns Security events matching the type
*/
public getEventsByType(type: SecurityEventType, limit: number = 100): ISecurityEvent[] {
return this.getRecentEvents(limit, { type });
}
/**
* Get security events for a specific IP address
* @param ipAddress The IP address to filter by
* @param limit Maximum number of events to return
* @returns Security events for the IP address
*/
public getEventsByIP(ipAddress: string, limit: number = 100): ISecurityEvent[] {
return this.securityEvents
.filter(event => event.ipAddress === ipAddress)
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
}
/**
* Get security events for a specific domain
* @param domain The domain to filter by
* @param limit Maximum number of events to return
* @returns Security events for the domain
*/
public getEventsByDomain(domain: string, limit: number = 100): ISecurityEvent[] {
return this.securityEvents
.filter(event => event.domain === domain)
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
}
/**
* Send a notification for critical security events
* @param event The security event to notify about
* @private
*/
private sendNotification(event: ISecurityEvent): void {
// In a production environment, this would integrate with a notification service
// For now, we'll just log that we would send a notification
logger.log('error', `[SECURITY NOTIFICATION] ${event.message}`, {
...event,
notificationSent: true
});
// Future integration with alerting systems would go here
}
/**
* Clear event history
*/
public clearEvents(): void {
this.securityEvents = [];
}
/**
* Get statistical summary of security events
* @param timeWindow Optional time window in milliseconds
* @returns Summary of security events
*/
public getEventsSummary(timeWindow?: number): {
total: number;
byLevel: Record<SecurityLogLevel, number>;
byType: Record<SecurityEventType, number>;
topIPs: Array<{ ip: string; count: number }>;
topDomains: Array<{ domain: string; count: number }>;
} {
// Filter by time window if provided
let events = this.securityEvents;
if (timeWindow) {
const cutoff = Date.now() - timeWindow;
events = events.filter(e => e.timestamp >= cutoff);
}
// Count by level
const byLevel = Object.values(SecurityLogLevel).reduce((acc, level) => {
acc[level] = events.filter(e => e.level === level).length;
return acc;
}, {} as Record<SecurityLogLevel, number>);
// Count by type
const byType = Object.values(SecurityEventType).reduce((acc, type) => {
acc[type] = events.filter(e => e.type === type).length;
return acc;
}, {} as Record<SecurityEventType, number>);
// Count by IP
const ipCounts = new Map<string, number>();
events.forEach(e => {
if (e.ipAddress) {
ipCounts.set(e.ipAddress, (ipCounts.get(e.ipAddress) || 0) + 1);
}
});
// Count by domain
const domainCounts = new Map<string, number>();
events.forEach(e => {
if (e.domain) {
domainCounts.set(e.domain, (domainCounts.get(e.domain) || 0) + 1);
}
});
// Sort and limit top entries
const topIPs = Array.from(ipCounts.entries())
.map(([ip, count]) => ({ ip, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
const topDomains = Array.from(domainCounts.entries())
.map(([domain, count]) => ({ domain, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
return {
total: events.length,
byLevel,
byType,
topIPs,
topDomains
};
}
}