UNPKG

secure-kit

Version:

Production-grade security + performance toolkit for backend frameworks with OWASP Top 10 compliance

335 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityMonitor = exports.SecurityEventType = void 0; const events_1 = require("events"); var SecurityEventType; (function (SecurityEventType) { SecurityEventType["RATE_LIMIT_EXCEEDED"] = "rate_limit_exceeded"; SecurityEventType["SUSPICIOUS_INPUT"] = "suspicious_input"; SecurityEventType["AUTHENTICATION_FAILURE"] = "authentication_failure"; SecurityEventType["UNAUTHORIZED_ACCESS"] = "unauthorized_access"; SecurityEventType["SQL_INJECTION_ATTEMPT"] = "sql_injection_attempt"; SecurityEventType["XSS_ATTEMPT"] = "xss_attempt"; SecurityEventType["CSRF_TOKEN_MISMATCH"] = "csrf_token_mismatch"; SecurityEventType["FILE_UPLOAD_VIOLATION"] = "file_upload_violation"; SecurityEventType["SECURITY_HEADER_MISSING"] = "security_header_missing"; SecurityEventType["MALFORMED_REQUEST"] = "malformed_request"; SecurityEventType["BRUTE_FORCE_ATTEMPT"] = "brute_force_attempt"; SecurityEventType["ANOMALOUS_BEHAVIOR"] = "anomalous_behavior"; })(SecurityEventType || (exports.SecurityEventType = SecurityEventType = {})); class SecurityMonitor extends events_1.EventEmitter { constructor(config = {}) { super(); this.events = []; this.threatRules = new Map(); this.lastRuleTrigger = new Map(); this.maxEventsHistory = config.maxEventsHistory || 10000; this.metrics = this.initializeMetrics(); // Load default threat detection rules this.loadDefaultThreatRules(); // Load custom rules if provided if (config.threatDetectionRules) { config.threatDetectionRules.forEach(rule => this.addThreatRule(rule)); } } /** * Record a security event */ recordEvent(event) { const fullEvent = { id: this.generateEventId(), timestamp: Date.now(), ...event, }; // Add to events history this.events.push(fullEvent); // Maintain history size limit if (this.events.length > this.maxEventsHistory) { this.events.shift(); } // Update metrics this.updateMetrics(fullEvent); // Check threat detection rules this.checkThreatRules(fullEvent); // Emit event for external listeners this.emit('securityEvent', fullEvent); return fullEvent; } /** * Get current security metrics */ getMetrics() { return { ...this.metrics }; } /** * Get recent security events */ getRecentEvents(limit = 100) { return this.events.slice(-limit); } /** * Get events by type */ getEventsByType(type, limit = 100) { return this.events.filter(event => event.type === type).slice(-limit); } /** * Get events by severity */ getEventsBySeverity(severity, limit = 100) { return this.events .filter(event => event.severity === severity) .slice(-limit); } /** * Get events in time range */ getEventsInRange(startTime, endTime) { return this.events.filter(event => event.timestamp >= startTime && event.timestamp <= endTime); } /** * Add a threat detection rule */ addThreatRule(rule) { this.threatRules.set(rule.id, rule); } /** * Remove a threat detection rule */ removeThreatRule(ruleId) { this.threatRules.delete(ruleId); this.lastRuleTrigger.delete(ruleId); } /** * Check if IP/source is currently exhibiting suspicious behavior */ isSuspiciousSource(source, timeWindow = 300000) { const cutoff = Date.now() - timeWindow; const recentEvents = this.events.filter(event => event.source === source && event.timestamp > cutoff && (event.severity === 'high' || event.severity === 'critical')); return recentEvents.length > 5; // Threshold for suspicious behavior } /** * Generate security report for a time period */ generateReport(startTime, endTime) { const events = this.getEventsInRange(startTime, endTime); const summary = { totalEvents: events.length, criticalEvents: events.filter(e => e.severity === 'critical').length, highSeverityEvents: events.filter(e => e.severity === 'high').length, uniqueSources: new Set(events.map(e => e.source)).size, }; const threatCounts = new Map(); const sourceCounts = new Map(); events.forEach(event => { threatCounts.set(event.type, (threatCounts.get(event.type) || 0) + 1); sourceCounts.set(event.source, (sourceCounts.get(event.source) || 0) + 1); }); const topThreats = Array.from(threatCounts.entries()) .map(([type, count]) => ({ type, count })) .sort((a, b) => b.count - a.count) .slice(0, 10); const suspiciousSources = Array.from(sourceCounts.entries()) .filter(([_, count]) => count > 10) .map(([source, _]) => source); const recommendations = this.generateRecommendations(events); return { summary, topThreats, suspiciousSources, recommendations, }; } /** * Clear all events and reset metrics */ clear() { this.events = []; this.metrics = this.initializeMetrics(); this.lastRuleTrigger.clear(); } initializeMetrics() { return { totalEvents: 0, eventsByType: Object.values(SecurityEventType).reduce((acc, type) => { acc[type] = 0; return acc; }, {}), eventsBySeverity: { low: 0, medium: 0, high: 0, critical: 0, }, recentEvents: [], topSources: [], alertThresholds: { rateLimit: 100, authFailures: 10, injectionAttempts: 5, }, }; } updateMetrics(event) { this.metrics.totalEvents++; this.metrics.eventsByType[event.type]++; this.metrics.eventsBySeverity[event.severity]++; // Update recent events (last 10) this.metrics.recentEvents.push(event); if (this.metrics.recentEvents.length > 10) { this.metrics.recentEvents.shift(); } // Update top sources this.updateTopSources(event.source); } updateTopSources(source) { const existing = this.metrics.topSources.find(s => s.source === source); if (existing) { existing.count++; } else { this.metrics.topSources.push({ source, count: 1 }); } // Sort and keep top 10 this.metrics.topSources.sort((a, b) => b.count - a.count); this.metrics.topSources = this.metrics.topSources.slice(0, 10); } generateEventId() { return `sec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } checkThreatRules(event) { for (const [ruleId, rule] of this.threatRules) { if (!rule.eventTypes.includes(event.type)) continue; // Check cooldown const lastTrigger = this.lastRuleTrigger.get(ruleId); if (lastTrigger && Date.now() - lastTrigger < rule.cooldown) { continue; } // Check rule condition if (rule.condition(this.events)) { this.lastRuleTrigger.set(ruleId, Date.now()); this.handleThreatDetection(rule, event); } } } handleThreatDetection(rule, triggerEvent) { const threatEvent = { id: this.generateEventId(), timestamp: Date.now(), type: SecurityEventType.ANOMALOUS_BEHAVIOR, severity: rule.severity, source: triggerEvent.source, details: { ruleName: rule.name, ruleDescription: rule.description, triggerEvent: triggerEvent.id, action: rule.action, }, metadata: triggerEvent.metadata, }; this.events.push(threatEvent); this.updateMetrics(threatEvent); // Emit threat detection event this.emit('threatDetected', { rule, triggerEvent, threatEvent, }); // Take action based on rule switch (rule.action) { case 'block': this.emit('blockRequest', { source: triggerEvent.source, rule }); break; case 'alert': this.emit('securityAlert', { rule, triggerEvent }); break; case 'log': // Already logged by adding to events break; } } loadDefaultThreatRules() { const defaultRules = [ { id: 'brute_force_detection', name: 'Brute Force Attack Detection', description: 'Detects multiple authentication failures from the same source', eventTypes: [SecurityEventType.AUTHENTICATION_FAILURE], condition: events => { const recent = events.filter(e => e.type === SecurityEventType.AUTHENTICATION_FAILURE && Date.now() - e.timestamp < 300000 // 5 minutes ); const sourceGroups = new Map(); recent.forEach(e => { sourceGroups.set(e.source, (sourceGroups.get(e.source) || 0) + 1); }); return Array.from(sourceGroups.values()).some(count => count >= 5); }, action: 'block', severity: 'high', cooldown: 600000, // 10 minutes }, { id: 'injection_attack_pattern', name: 'Injection Attack Pattern', description: 'Detects patterns of SQL/NoSQL injection attempts', eventTypes: [SecurityEventType.SQL_INJECTION_ATTEMPT], condition: events => { const recent = events.filter(e => e.type === SecurityEventType.SQL_INJECTION_ATTEMPT && Date.now() - e.timestamp < 600000 // 10 minutes ); return recent.length >= 3; }, action: 'alert', severity: 'high', cooldown: 300000, // 5 minutes }, { id: 'rate_limit_abuse', name: 'Rate Limit Abuse', description: 'Detects persistent rate limit violations', eventTypes: [SecurityEventType.RATE_LIMIT_EXCEEDED], condition: events => { const recent = events.filter(e => e.type === SecurityEventType.RATE_LIMIT_EXCEEDED && Date.now() - e.timestamp < 3600000 // 1 hour ); const sourceGroups = new Map(); recent.forEach(e => { sourceGroups.set(e.source, (sourceGroups.get(e.source) || 0) + 1); }); return Array.from(sourceGroups.values()).some(count => count >= 10); }, action: 'block', severity: 'medium', cooldown: 1800000, // 30 minutes }, ]; defaultRules.forEach(rule => this.addThreatRule(rule)); } generateRecommendations(events) { const recommendations = []; const criticalCount = events.filter(e => e.severity === 'critical').length; const highCount = events.filter(e => e.severity === 'high').length; if (criticalCount > 0) { recommendations.push(`${criticalCount} critical security events detected - immediate action required`); } if (highCount > 10) { recommendations.push('High volume of high-severity events - consider tightening security policies'); } const injectionAttempts = events.filter(e => e.type === SecurityEventType.SQL_INJECTION_ATTEMPT || e.type === SecurityEventType.XSS_ATTEMPT).length; if (injectionAttempts > 5) { recommendations.push('Multiple injection attempts detected - review input validation and sanitization'); } const authFailures = events.filter(e => e.type === SecurityEventType.AUTHENTICATION_FAILURE).length; if (authFailures > 20) { recommendations.push('High number of authentication failures - consider implementing account lockout policies'); } return recommendations; } } exports.SecurityMonitor = SecurityMonitor; //# sourceMappingURL=security-monitor.js.map