UNPKG

aimless-security

Version:

Enhanced Runtime Application Self-Protection (RASP) and API Fuzzing Engine with advanced threat detection, behavioral analysis, and intelligent response scoring for Node.js applications

356 lines 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AnomalyDetector = void 0; const types_1 = require("../types"); class AnomalyDetector { constructor() { this.maxHistorySize = 1000; this.timeWindow = 60000; // 1 minute this.maxRequestsPerMinute = 100; this.reputationDecayRate = 0.1; // Reputation improves over time this.suspiciousPatterns = [ /bot/i, /crawler/i, /spider/i, /scraper/i, /curl/i, /wget/i, /python-requests/i, /go-http-client/i, /java/i, /perl/i, /ruby/i, /scan/i, /nikto/i, /sqlmap/i, /nmap/i, /masscan/i, /metasploit/i, /burp/i, /zap/i, /acunetix/i, /nessus/i ]; this.requestHistory = new Map(); this.rateLimitMap = new Map(); this.ipReputation = new Map(); this.fingerprints = new Map(); } /** * Enhanced anomaly detection with behavioral analysis */ detect(ip, method, path, userAgent, bodySize, statusCode, responseTime) { const threats = []; const now = Date.now(); // Update IP reputation this.updateReputation(ip); // Check if IP is blocked const reputation = this.ipReputation.get(ip); if (reputation && reputation.blocked) { threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'critical', description: 'Request from blocked IP address', timestamp: new Date(), blocked: true, metadata: { ip, reputationScore: reputation.score, violations: reputation.violations } }); return threats; } // Create request profile const profile = { ip, method, path, timestamp: now, userAgent, bodySize, statusCode, responseTime }; // Generate fingerprint const fingerprint = this.generateFingerprint(ip, userAgent); this.trackFingerprint(fingerprint); // Check rate limiting const rateThreat = this.checkRateLimit(ip, now); if (rateThreat) { threats.push(rateThreat); this.penalizeReputation(ip, 10); } // Check for suspicious user agents if (userAgent && this.suspiciousPatterns.some(p => p.test(userAgent))) { threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'medium', description: 'Suspicious user agent detected', payload: userAgent, timestamp: new Date(), blocked: false, metadata: { ip, userAgent, fingerprint } }); this.penalizeReputation(ip, 5); } // Check for unusual request patterns const patternThreats = this.checkRequestPatterns(ip, profile); threats.push(...patternThreats); if (patternThreats.length > 0) { this.penalizeReputation(ip, patternThreats.length * 5); } // Check for velocity-based anomalies const velocityThreats = this.checkVelocity(ip, now); threats.push(...velocityThreats); if (velocityThreats.length > 0) { this.penalizeReputation(ip, velocityThreats.length * 3); } // Store request history this.storeRequest(ip, profile); return threats; } checkRateLimit(ip, now) { let timestamps = this.rateLimitMap.get(ip) || []; // Remove old timestamps timestamps = timestamps.filter(t => now - t < this.timeWindow); // Add current timestamp timestamps.push(now); this.rateLimitMap.set(ip, timestamps); // Check if rate limit exceeded if (timestamps.length > this.maxRequestsPerMinute) { return { type: types_1.ThreatType.RATE_LIMIT_EXCEEDED, severity: 'medium', description: `Rate limit exceeded: ${timestamps.length} requests in ${this.timeWindow}ms`, timestamp: new Date(), blocked: true, metadata: { ip, requestCount: timestamps.length } }; } return null; } checkRequestPatterns(ip, current) { const threats = []; const history = this.requestHistory.get(ip) || []; if (history.length < 5) return threats; const recentRequests = history.slice(-10); // Check for rapid sequential requests to different endpoints const uniquePaths = new Set(recentRequests.map(r => r.path)); if (uniquePaths.size > 8 && recentRequests.length === 10) { threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'medium', description: 'Potential scanning activity detected', timestamp: new Date(), blocked: false, metadata: { ip, uniqueEndpoints: uniquePaths.size } }); } // Check for repeated failed attempts (based on error patterns in path) const errorPaths = recentRequests.filter(r => r.path.includes('login') || r.path.includes('auth') || r.path.includes('admin')); if (errorPaths.length > 5) { threats.push({ type: types_1.ThreatType.AUTH_BYPASS_ATTEMPT, severity: 'high', description: 'Potential authentication bypass attempt detected', timestamp: new Date(), blocked: true, metadata: { ip, attempts: errorPaths.length } }); } // Check for unusually large request bodies if (current.bodySize && current.bodySize > 10 * 1024 * 1024) { // 10MB threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'medium', description: 'Unusually large request body detected', timestamp: new Date(), blocked: false, metadata: { ip, bodySize: current.bodySize } }); } return threats; } storeRequest(ip, profile) { let history = this.requestHistory.get(ip) || []; history.push(profile); // Limit history size if (history.length > this.maxHistorySize) { history = history.slice(-this.maxHistorySize); } this.requestHistory.set(ip, history); } /** * Update IP reputation (decay over time) */ updateReputation(ip) { let reputation = this.ipReputation.get(ip); if (!reputation) { reputation = { score: 100, lastUpdate: Date.now(), violations: 0, blocked: false }; this.ipReputation.set(ip, reputation); return; } // Reputation improves over time const timeSinceUpdate = Date.now() - reputation.lastUpdate; const improvement = (timeSinceUpdate / 3600000) * this.reputationDecayRate; reputation.score = Math.min(100, reputation.score + improvement); reputation.lastUpdate = Date.now(); // Unblock if score improves enough if (reputation.blocked && reputation.score > 50) { reputation.blocked = false; } } /** * Penalize IP reputation */ penalizeReputation(ip, penalty) { let reputation = this.ipReputation.get(ip); if (!reputation) { reputation = { score: 100 - penalty, lastUpdate: Date.now(), violations: 1, blocked: false }; } else { reputation.score = Math.max(0, reputation.score - penalty); reputation.violations++; reputation.lastUpdate = Date.now(); // Block if score drops too low if (reputation.score < 20) { reputation.blocked = true; } } this.ipReputation.set(ip, reputation); } /** * Generate fingerprint from IP and user agent */ generateFingerprint(ip, userAgent) { const crypto = require('crypto'); const data = `${ip}:${userAgent || 'unknown'}`; return crypto.createHash('md5').update(data).digest('hex'); } /** * Track fingerprint frequency */ trackFingerprint(hash) { const existing = this.fingerprints.get(hash); const now = Date.now(); if (existing) { existing.count++; existing.lastSeen = now; } else { this.fingerprints.set(hash, { hash, count: 1, firstSeen: now, lastSeen: now }); } } /** * Check request velocity (rapid changes) */ checkVelocity(ip, now) { const threats = []; const history = this.requestHistory.get(ip) || []; if (history.length < 3) return threats; const recentRequests = history.filter(r => now - r.timestamp < 10000); // Last 10 seconds // Check for burst activity if (recentRequests.length > 20) { threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'high', description: 'Burst activity detected', timestamp: new Date(), blocked: true, metadata: { ip, requestCount: recentRequests.length, timeWindow: '10s' } }); } // Check for distributed attack pattern const uniquePaths = new Set(recentRequests.map(r => r.path)); if (uniquePaths.size > 15 && recentRequests.length > 15) { threats.push({ type: types_1.ThreatType.ANOMALOUS_BEHAVIOR, severity: 'critical', description: 'Potential distributed attack or scanning detected', timestamp: new Date(), blocked: true, metadata: { ip, uniquePaths: uniquePaths.size, requests: recentRequests.length } }); } return threats; } clearHistory(ip) { if (ip) { this.requestHistory.delete(ip); this.rateLimitMap.delete(ip); this.ipReputation.delete(ip); } else { this.requestHistory.clear(); this.rateLimitMap.clear(); this.ipReputation.clear(); this.fingerprints.clear(); } } /** * Get IP reputation score */ getReputationScore(ip) { const reputation = this.ipReputation.get(ip); return reputation ? reputation.score : 100; } /** * Block or unblock an IP */ setIPBlocked(ip, blocked) { let reputation = this.ipReputation.get(ip); if (!reputation) { reputation = { score: blocked ? 0 : 100, lastUpdate: Date.now(), violations: blocked ? 1 : 0, blocked }; } else { reputation.blocked = blocked; if (blocked) { reputation.score = 0; } } this.ipReputation.set(ip, reputation); } /** * Get statistics for monitoring */ getStats() { let totalRequests = 0; let blockedIPs = 0; for (const [, requests] of this.requestHistory) { totalRequests += requests.length; } for (const [, reputation] of this.ipReputation) { if (reputation.blocked) blockedIPs++; } return { totalIPs: this.requestHistory.size, blockedIPs, totalRequests, uniqueFingerprints: this.fingerprints.size }; } } exports.AnomalyDetector = AnomalyDetector; //# sourceMappingURL=anomaly-detector.js.map