UNPKG

trojanhorse-js

Version:

A comprehensive JavaScript library for fetching, managing, and analyzing global threat intelligence from multiple open-source feeds and security news sources. Unlike its mythological namesake, this Trojan protects your digital fortress.

523 lines (451 loc) 16.3 kB
/** * TrojanHorse.js Real-Time Analytics Engine * Enterprise-grade threat analytics and monitoring */ import { EventEmitter } from 'events'; // WebSocket analytics implementation ready - uncomment when needed // import { WebSocket } from 'ws'; import nodemailer from 'nodemailer'; // ===== ANALYTICS INTERFACES ===== export interface MetricPoint { timestamp: Date; metric: string; value: number; labels: Record<string, string>; unit: 'count' | 'rate' | 'gauge' | 'histogram' | 'bytes' | 'milliseconds'; metadata?: Record<string, any>; } export interface Alert { id: string; title: string; description: string; severity: 'info' | 'warning' | 'critical' | 'emergency'; category: 'performance' | 'security' | 'system' | 'business'; source: string; timestamp: Date; resolved: boolean; metadata?: Record<string, any>; } export interface NotificationChannel { type: 'email' | 'slack' | 'webhook' | 'pagerduty'; config: any; enabled: boolean; } export interface AnalyticsConfig { retention: { metrics: number; // days alerts: number; // days logs: number; // days maxDataPoints?: number; // max data points to keep in memory }; notifications: NotificationChannel[]; aggregation: { intervals: string[]; defaultInterval: string; }; alerting?: { maxAlerts?: number; // max alerts to keep in memory }; } // ===== METRICS COLLECTOR ===== class MetricsCollector extends EventEmitter { private metrics: Map<string, MetricPoint[]> = new Map(); private config: AnalyticsConfig; constructor(config: AnalyticsConfig) { super(); this.config = config; } public recordMetric(point: MetricPoint): void { const key = `${point.metric}:${JSON.stringify(point.labels)}`; if (!this.metrics.has(key)) { this.metrics.set(key, []); } // Use config to check retention settings const maxDataPoints = this.config.retention?.maxDataPoints || 1000; const points = this.metrics.get(key)!; points.push(point); // Trim old data points if we exceed the limit if (points.length > maxDataPoints) { points.splice(0, points.length - maxDataPoints); } this.emit('metric_recorded', point); } public queryMetrics(query: { metric: string; from: Date; to: Date; aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count'; groupBy?: string[]; filters?: Record<string, string>; }): MetricPoint[] { const key = `${query.metric}:${JSON.stringify(query.filters || {})}`; const points = this.metrics.get(key) || []; return points.filter(p => p.timestamp >= query.from && p.timestamp <= query.to ); } public getMetrics(): Map<string, MetricPoint[]> { return this.metrics; } } // ===== ALERTING ENGINE ===== class AlertingEngine extends EventEmitter { private alerts: Map<string, Alert> = new Map(); private config: AnalyticsConfig; constructor(config: AnalyticsConfig) { super(); this.config = config; } public createAlert(alert: Omit<Alert, 'id' | 'timestamp' | 'resolved'>): string { const id = `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const fullAlert: Alert = { id, timestamp: new Date(), resolved: false, ...alert }; this.alerts.set(id, fullAlert); this.emit('alert_created', fullAlert); this.sendNotifications(fullAlert); // Use config for alert retention this.trimOldAlerts(); return id; } private trimOldAlerts(): void { const maxAlerts = this.config.alerting?.maxAlerts || 1000; if (this.alerts.size > maxAlerts) { const sortedAlerts = Array.from(this.alerts.entries()) .sort((a, b) => a[1].timestamp.getTime() - b[1].timestamp.getTime()); const toDelete = sortedAlerts.slice(0, this.alerts.size - maxAlerts); toDelete.forEach(([id]) => this.alerts.delete(id)); } } public resolveAlert(alertId: string): boolean { const alert = this.alerts.get(alertId); if (alert) { alert.resolved = true; this.emit('alert_resolved', alert); return true; } return false; } public getAlerts(): Alert[] { return Array.from(this.alerts.values()); } private async sendNotifications(alert: Alert): Promise<void> { for (const channel of this.config.notifications) { if (!channel.enabled) { continue; } try { switch (channel.type) { case 'email': await this.sendEmailNotification(alert, channel.config); break; case 'slack': await this.sendSlackNotification(alert, channel.config); break; case 'webhook': await this.sendWebhookNotification(alert, channel.config); break; case 'pagerduty': await this.sendPagerDutyNotification(alert, channel.config); break; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.emit('notification_error', { channel: channel.type, error: errorMessage }); } } } private async sendEmailNotification(alert: Alert, config: any): Promise<void> { try { const transporter = nodemailer.createTransport({ host: config.smtp?.host || 'localhost', port: config.smtp?.port || 587, secure: config.smtp?.secure || false, auth: config.smtp?.auth ? { user: config.smtp.auth.user, pass: config.smtp.auth.pass } : undefined }); const mailOptions = { from: config.from || 'alerts@trojanhorse.enterprise.com', to: config.to || 'security@company.com', subject: `🚨 TrojanHorse Alert: ${alert.title}`, html: this.generateAlertEmailHTML(alert), text: this.generateAlertEmailText(alert) }; const result = await transporter.sendMail(mailOptions); if (!result.messageId) { throw new Error('Email sending failed - no message ID returned'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Email notification failed: ${errorMessage}`); } } private generateAlertEmailHTML(alert: Alert): string { const severityColor = this.getSeverityColor(alert.severity); return ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>TrojanHorse Security Alert</title> <style> body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } .container { max-width: 600px; margin: 0 auto; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .header { background-color: ${severityColor}; color: white; padding: 20px; border-radius: 8px 8px 0 0; } .content { padding: 20px; } .severity { display: inline-block; padding: 4px 12px; border-radius: 4px; color: white; font-weight: bold; background-color: ${severityColor}; } .metadata { background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin: 15px 0; } .footer { background-color: #f8f9fa; padding: 15px; border-radius: 0 0 8px 8px; text-align: center; font-size: 12px; color: #666; } </style> </head> <body> <div class="container"> <div class="header"> <h1>🚨 Security Alert</h1> <h2>${alert.title}</h2> </div> <div class="content"> <p><strong>Severity:</strong> <span class="severity">${alert.severity.toUpperCase()}</span></p> <p><strong>Category:</strong> ${alert.category}</p> <p><strong>Source:</strong> ${alert.source}</p> <p><strong>Timestamp:</strong> ${alert.timestamp.toISOString()}</p> <div class="metadata"> <h3>Description</h3> <p>${alert.description}</p> ${alert.metadata ? ` <h3>Additional Details</h3> <pre>${JSON.stringify(alert.metadata, null, 2)}</pre> ` : ''} </div> </div> <div class="footer"> <p>This alert was generated by TrojanHorse.js Enterprise Security Platform</p> <p>Alert ID: ${alert.id}</p> </div> </div> </body> </html> `; } private generateAlertEmailText(alert: Alert): string { return ` TrojanHorse Security Alert Title: ${alert.title} Severity: ${alert.severity.toUpperCase()} Category: ${alert.category} Source: ${alert.source} Timestamp: ${alert.timestamp.toISOString()} Description: ${alert.description} ${alert.metadata ? ` Additional Details: ${JSON.stringify(alert.metadata, null, 2)} ` : ''} Alert ID: ${alert.id} This alert was generated by TrojanHorse.js Enterprise Security Platform. `.trim(); } private getSeverityColor(severity: string): string { const colors = { 'info': '#17a2b8', 'warning': '#ffc107', 'critical': '#dc3545', 'emergency': '#6f42c1' }; return colors[severity as keyof typeof colors] || '#6c757d'; } private async sendSlackNotification(alert: Alert, config: any): Promise<void> { if (!config.webhook) { throw new Error('Slack webhook URL not configured'); } try { const payload = { text: `🚨 *${alert.title}*`, attachments: [{ color: this.getSeverityColor(alert.severity), fields: [ { title: 'Severity', value: alert.severity.toUpperCase(), short: true }, { title: 'Category', value: alert.category, short: true }, { title: 'Source', value: alert.source, short: true }, { title: 'Alert ID', value: alert.id, short: true }, { title: 'Description', value: alert.description, short: false } ], footer: 'TrojanHorse.js Enterprise', ts: Math.floor(alert.timestamp.getTime() / 1000) }] }; const response = await fetch(config.webhook, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`Slack API error: ${response.status} ${response.statusText}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Slack notification failed: ${errorMessage}`); } } private async sendWebhookNotification(alert: Alert, config: any): Promise<void> { if (!config.url) { throw new Error('Webhook URL not configured'); } try { const payload = { event: 'alert_created', alert: { id: alert.id, title: alert.title, description: alert.description, severity: alert.severity, category: alert.category, source: alert.source, timestamp: alert.timestamp.toISOString(), metadata: alert.metadata }, system: { platform: 'TrojanHorse.js Enterprise', version: '1.0.1' } }; const headers: Record<string, string> = { 'Content-Type': 'application/json', 'User-Agent': 'TrojanHorse.js/1.0.1' }; if (config.auth?.type === 'bearer' && config.auth.token) { headers.Authorization = `Bearer ${config.auth.token}`; } else if (config.auth?.type === 'api_key' && config.auth.key) { headers['X-API-Key'] = config.auth.key; } const response = await fetch(config.url, { method: 'POST', headers, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`Webhook error: ${response.status} ${response.statusText}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Webhook notification failed: ${errorMessage}`); } } private async sendPagerDutyNotification(alert: Alert, config: any): Promise<void> { if (!config.routingKey) { throw new Error('PagerDuty routing key not configured'); } try { const payload = { routing_key: config.routingKey, event_action: 'trigger', dedup_key: `trojanhorse-${alert.id}`, payload: { summary: alert.title, severity: this.mapSeverityToPagerDuty(alert.severity), source: alert.source, component: 'TrojanHorse.js', group: alert.category, class: 'security_alert', custom_details: { description: alert.description, alert_id: alert.id, category: alert.category, timestamp: alert.timestamp.toISOString(), metadata: alert.metadata } }, client: 'TrojanHorse.js Enterprise', client_url: config.clientUrl || 'https://trojanhorse.enterprise.com' }; const response = await fetch('https://events.pagerduty.com/v2/enqueue', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`PagerDuty API error: ${response.status} ${errorText}`); } const result = await response.json(); if (!result.dedup_key) { throw new Error('PagerDuty response missing dedup_key'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`PagerDuty notification failed: ${errorMessage}`); } } private mapSeverityToPagerDuty(severity: string): string { const mapping = { 'info': 'info', 'warning': 'warning', 'critical': 'critical', 'emergency': 'critical' }; return mapping[severity as keyof typeof mapping] || 'warning'; } } // ===== ANALYTICS MANAGER ===== export class AnalyticsManager extends EventEmitter { private config: AnalyticsConfig; private metricsCollector: MetricsCollector; private alertingEngine: AlertingEngine; constructor(config: AnalyticsConfig) { super(); this.config = config; this.metricsCollector = new MetricsCollector(config); this.alertingEngine = new AlertingEngine(config); this.setupEventHandlers(); } private setupEventHandlers(): void { this.metricsCollector.on('metric_recorded', (metric: MetricPoint) => { this.emit('metric_recorded', metric); }); this.alertingEngine.on('alert_created', (alert: Alert) => { this.emit('alert_created', alert); }); this.alertingEngine.on('alert_resolved', (alert: Alert) => { this.emit('alert_resolved', alert); }); } public recordMetric(metric: MetricPoint): void { this.metricsCollector.recordMetric(metric); } public createAlert(alert: Omit<Alert, 'id' | 'timestamp' | 'resolved'>): string { return this.alertingEngine.createAlert(alert); } public queryMetrics(query: { metric: string; from: Date; to: Date; aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count'; groupBy?: string[]; filters?: Record<string, string>; }): MetricPoint[] { return this.metricsCollector.queryMetrics(query); } public getAlerts(): Alert[] { return this.alertingEngine.getAlerts(); } public getMetricsCollector(): MetricsCollector { return this.metricsCollector; } public getAlertingEngine(): AlertingEngine { return this.alertingEngine; } public getConfig(): AnalyticsConfig { return this.config; } } // Export types and classes export { MetricsCollector, AlertingEngine };