UNPKG

@ordojs/security

Version:

Security package for OrdoJS with XSS, CSRF, and injection protection

245 lines 8.22 kB
export class RuntimeSecurityMonitor { events = []; options; alertCounts = {}; startTime; constructor(options = {}) { this.options = { enableLogging: true, logLevel: 'warn', maxEvents: 10000, alertThresholds: { critical: 1, high: 5, medium: 10, }, ...options, }; this.startTime = new Date(); } recordEvent(event) { const securityEvent = { id: this.generateEventId(), timestamp: new Date(), ...event, }; this.events.push(securityEvent); // Maintain max events limit if (this.events.length > this.options.maxEvents) { this.events.shift(); } // Log event if enabled if (this.options.enableLogging) { this.logEvent(securityEvent); } // Check alert thresholds this.checkAlertThresholds(securityEvent); // Update metrics if (this.options.onMetricsUpdate) { this.options.onMetricsUpdate(this.getMetrics()); } } recordXSSAttempt(details) { this.recordEvent({ type: 'xss_attempt', severity: 'high', source: details.source, details: { payload: details.payload, context: details.context, }, blocked: details.blocked, }); } recordCSRFViolation(details) { this.recordEvent({ type: 'csrf_violation', severity: 'high', source: details.source, details: { endpoint: details.endpoint, hasExpectedToken: !!details.expectedToken, hasReceivedToken: !!details.receivedToken, }, blocked: true, }); } recordInjectionAttempt(details) { this.recordEvent({ type: 'injection_attempt', severity: 'critical', source: details.source, details: { injectionType: details.type, payload: details.payload, query: details.query, }, blocked: details.blocked, }); } recordRateLimitExceeded(details) { this.recordEvent({ type: 'rate_limit_exceeded', severity: 'medium', source: details.source, details: { limit: details.limit, current: details.current, window: details.window, endpoint: details.endpoint, }, blocked: true, }); } recordAuthFailure(details) { this.recordEvent({ type: 'auth_failure', severity: details.reason === 'insufficient_permissions' ? 'medium' : 'high', source: details.source, details: { reason: details.reason, username: details.username, endpoint: details.endpoint, }, blocked: true, }); } recordSuspiciousActivity(details) { const severity = details.riskScore >= 80 ? 'critical' : details.riskScore >= 60 ? 'high' : details.riskScore >= 40 ? 'medium' : 'low'; this.recordEvent({ type: 'suspicious_activity', severity, source: details.source, details: { activity: details.activity, riskScore: details.riskScore, context: details.context, }, blocked: details.riskScore >= 80, }); } getMetrics() { const now = new Date(); const eventsByType = {}; const eventsBySeverity = {}; const sourceIpCounts = {}; let totalEvents = 0; let blockedEvents = 0; this.events.forEach(event => { totalEvents++; if (event.blocked) blockedEvents++; eventsByType[event.type] = (eventsByType[event.type] || 0) + 1; eventsBySeverity[event.severity] = (eventsBySeverity[event.severity] || 0) + 1; if (event.source.ip) { sourceIpCounts[event.source.ip] = (sourceIpCounts[event.source.ip] || 0) + 1; } }); const topSources = Object.entries(sourceIpCounts) .map(([ip, count]) => ({ ip, count })) .sort((a, b) => b.count - a.count) .slice(0, 10); return { totalEvents, blockedEvents, eventsByType, eventsBySeverity, topSources, timeRange: { start: this.startTime, end: now, }, }; } getEvents(filter) { let filteredEvents = [...this.events]; if (filter?.type) { filteredEvents = filteredEvents.filter(e => e.type === filter.type); } if (filter?.severity) { filteredEvents = filteredEvents.filter(e => e.severity === filter.severity); } if (filter?.since) { filteredEvents = filteredEvents.filter(e => e.timestamp >= filter.since); } if (filter?.limit) { filteredEvents = filteredEvents.slice(-filter.limit); } return filteredEvents.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } clearEvents() { this.events = []; this.alertCounts = {}; } exportEvents(format = 'json') { if (format === 'csv') { const headers = ['id', 'type', 'severity', 'timestamp', 'blocked', 'source_ip', 'user_agent', 'details']; const rows = this.events.map(event => [ event.id, event.type, event.severity, event.timestamp.toISOString(), event.blocked.toString(), event.source.ip || '', event.source.userAgent || '', JSON.stringify(event.details), ]); return [headers, ...rows].map(row => row.join(',')).join('\n'); } return JSON.stringify(this.events, null, 2); } generateEventId() { return `sec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } logEvent(event) { const logLevel = this.getLogLevel(event.severity); if (!this.shouldLog(logLevel)) return; const message = `Security Event [${event.type}]: ${event.severity.toUpperCase()} - ${JSON.stringify(event.details)}`; switch (logLevel) { case 'error': console.error(message); break; case 'warn': console.warn(message); break; case 'info': console.info(message); break; case 'debug': console.debug(message); break; } } getLogLevel(severity) { switch (severity) { case 'critical': return 'error'; case 'high': return 'error'; case 'medium': return 'warn'; case 'low': return 'info'; default: return 'debug'; } } shouldLog(level) { const levels = ['debug', 'info', 'warn', 'error']; const currentLevelIndex = levels.indexOf(this.options.logLevel); const eventLevelIndex = levels.indexOf(level); return eventLevelIndex >= currentLevelIndex; } checkAlertThresholds(event) { const thresholds = this.options.alertThresholds; const threshold = event.severity === 'low' ? undefined : thresholds[event.severity]; if (!threshold) return; const key = `${event.severity}_${event.type}`; this.alertCounts[key] = (this.alertCounts[key] || 0) + 1; if (this.alertCounts[key] >= threshold && this.options.onAlert) { this.options.onAlert(event); } } } // Singleton instance for global use export const runtimeMonitor = new RuntimeSecurityMonitor(); //# sourceMappingURL=runtime-monitor.js.map