recoder-security
Version:
Enterprise-grade security and compliance layer for CodeCraft CLI
561 lines • 19.4 kB
JavaScript
"use strict";
/**
* Enhanced Router Security Service with Threat Detection
* Provides comprehensive security for AI model routing and API access
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_ROUTER_SECURITY_CONFIG = exports.RouterSecurityService = void 0;
exports.createRouterSecurity = createRouterSecurity;
const crypto_1 = __importDefault(require("crypto"));
const events_1 = require("events");
class RouterSecurityService extends events_1.EventEmitter {
constructor(config) {
super();
this.requestHistory = new Map();
this.bannedIPs = new Set();
this.failedAttempts = new Map();
this.rateLimitCounters = new Map();
this.securityEvents = [];
this.isInitialized = false;
this.config = config;
this.initialize();
}
initialize() {
// Initialize default threat patterns if none provided
if (!this.config.suspiciousPatterns.length) {
this.config.suspiciousPatterns = this.getDefaultThreatPatterns();
}
// Start background cleanup tasks
this.startCleanupTasks();
this.isInitialized = true;
this.emit('initialized');
}
/**
* Analyze incoming request for security threats
*/
async analyzeRequest(context) {
const reasons = [];
let riskScore = 0;
let action = 'allow';
// Check if IP is banned
if (this.bannedIPs.has(context.ip)) {
reasons.push('IP address is banned');
riskScore = 100;
action = 'ban';
await this.logSecurityEvent({
type: 'intrusion',
severity: 'critical',
source: context,
details: { reason: 'Banned IP access attempt' },
riskScore: 100
});
return { allowed: false, riskScore, reasons, action };
}
// Rate limiting check
if (this.config.enableRateLimiting) {
const rateLimitResult = this.checkRateLimit(context.ip);
if (!rateLimitResult.allowed) {
reasons.push(`Rate limit exceeded: ${rateLimitResult.reason}`);
riskScore += 30;
action = 'block';
}
}
// Geolocation check
if (this.config.enableGeoBlocking && context.geolocation) {
const geoResult = this.checkGeolocation(context.geolocation);
if (!geoResult.allowed) {
reasons.push(`Blocked country: ${context.geolocation.country}`);
riskScore += 50;
action = 'block';
}
}
// Threat pattern analysis
if (this.config.enableThreatDetection) {
const threatResult = await this.analyzeThreatPatterns(context);
riskScore += threatResult.riskScore;
reasons.push(...threatResult.reasons);
if (threatResult.action === 'ban' || threatResult.action === 'block') {
action = threatResult.action;
}
}
// Anomaly detection
if (this.config.enableAnomalyDetection) {
const anomalyResult = this.detectAnomalies(context);
riskScore += anomalyResult.riskScore;
reasons.push(...anomalyResult.reasons);
}
// Store request for analysis
this.storeRequest(context);
// Log security event if risk is significant
if (riskScore > 30) {
await this.logSecurityEvent({
type: riskScore > 70 ? 'intrusion' : 'anomaly',
severity: riskScore > 70 ? 'high' : 'medium',
source: context,
details: { reasons, action },
riskScore
});
}
// Handle failed attempts
if (action === 'block' || action === 'ban') {
this.recordFailedAttempt(context.ip);
}
const isAllowed = action === 'allow' || action === 'warn';
return {
allowed: isAllowed,
riskScore,
reasons,
action
};
}
/**
* Check rate limiting for IP address
*/
checkRateLimit(ip) {
const now = new Date();
const counters = this.rateLimitCounters.get(ip) || {
minute: 0,
hour: 0,
lastReset: now
};
// Reset counters if time window passed
const timeSinceReset = (now.getTime() - counters.lastReset.getTime()) / 1000;
if (timeSinceReset >= 60) {
counters.minute = 0;
}
if (timeSinceReset >= 3600) {
counters.hour = 0;
counters.lastReset = now;
}
// Check limits
if (counters.minute >= this.config.maxRequestsPerMinute) {
return { allowed: false, reason: 'Per-minute limit exceeded' };
}
if (counters.hour >= this.config.maxRequestsPerHour) {
return { allowed: false, reason: 'Per-hour limit exceeded' };
}
// Increment counters
counters.minute++;
counters.hour++;
this.rateLimitCounters.set(ip, counters);
return { allowed: true };
}
/**
* Check geolocation restrictions
*/
checkGeolocation(geo) {
if (this.config.allowedCountries && this.config.allowedCountries.length > 0) {
return { allowed: this.config.allowedCountries.includes(geo.country) };
}
return { allowed: true };
}
/**
* Analyze request against threat patterns
*/
async analyzeThreatPatterns(context) {
let riskScore = 0;
const reasons = [];
let highestAction = 'allow';
for (const pattern of this.config.suspiciousPatterns) {
const matches = await this.evaluatePattern(pattern, context);
if (matches) {
const patternRisk = this.getSeverityScore(pattern.severity);
riskScore += patternRisk;
reasons.push(`Threat pattern: ${pattern.name}`);
if (this.getActionPriority(pattern.action) > this.getActionPriority(highestAction)) {
highestAction = pattern.action;
}
}
}
return { riskScore, reasons, action: highestAction };
}
/**
* Detect anomalies in request patterns
*/
detectAnomalies(context) {
let riskScore = 0;
const reasons = [];
const ipHistory = this.requestHistory.get(context.ip) || [];
// Check for unusual request frequency
const recentRequests = ipHistory.filter(req => (context.timestamp.getTime() - req.timestamp.getTime()) < 60000 // Last minute
);
if (recentRequests.length > 20) {
riskScore += 20;
reasons.push('Unusually high request frequency');
}
// Check for endpoint scanning behavior
const uniqueEndpoints = new Set(recentRequests.map(req => req.endpoint));
if (uniqueEndpoints.size > 10) {
riskScore += 25;
reasons.push('Potential endpoint scanning detected');
}
// Check for suspicious user agent
if (this.isSuspiciousUserAgent(context.userAgent)) {
riskScore += 15;
reasons.push('Suspicious user agent detected');
}
return { riskScore, reasons };
}
/**
* Evaluate threat pattern against request context
*/
async evaluatePattern(pattern, context) {
for (const rule of pattern.rules) {
const result = this.evaluateRule(rule, context);
if (!result) {
return false; // All rules must match
}
}
return true;
}
/**
* Evaluate individual threat rule
*/
evaluateRule(rule, context) {
let targetValue;
// Extract target value based on condition
switch (rule.condition) {
case 'userAgent':
targetValue = context.userAgent;
break;
case 'endpoint':
targetValue = context.endpoint;
break;
case 'method':
targetValue = context.method;
break;
case 'payloadSize':
targetValue = JSON.stringify(context.payload || {}).length;
break;
case 'requestsPerMinute':
const ipHistory = this.requestHistory.get(context.ip) || [];
const recentRequests = ipHistory.filter(req => (context.timestamp.getTime() - req.timestamp.getTime()) < 60000);
targetValue = recentRequests.length;
break;
default:
return false;
}
// Apply operator
switch (rule.operator) {
case 'equals':
return targetValue === rule.value;
case 'greater':
return targetValue > rule.value;
case 'less':
return targetValue < rule.value;
case 'contains':
return String(targetValue).includes(rule.value);
case 'matches':
return new RegExp(rule.value).test(String(targetValue));
default:
return false;
}
}
/**
* Check if user agent is suspicious
*/
isSuspiciousUserAgent(userAgent) {
const suspiciousPatterns = [
/bot/i,
/crawler/i,
/scanner/i,
/curl/i,
/wget/i,
/python-requests/i,
/go-http-client/i
];
return suspiciousPatterns.some(pattern => pattern.test(userAgent));
}
/**
* Record failed authentication attempt
*/
recordFailedAttempt(ip) {
const attempts = this.failedAttempts.get(ip) || 0;
const newAttempts = attempts + 1;
this.failedAttempts.set(ip, newAttempts);
if (newAttempts >= this.config.maxFailedAttemptsBeforeBan) {
this.banIP(ip);
}
}
/**
* Ban IP address
*/
banIP(ip) {
this.bannedIPs.add(ip);
// Auto-unban after configured duration
setTimeout(() => {
this.bannedIPs.delete(ip);
this.failedAttempts.delete(ip);
}, this.config.banDurationMinutes * 60 * 1000);
this.emit('ipBanned', ip);
}
/**
* Store request for analysis
*/
storeRequest(context) {
const ipHistory = this.requestHistory.get(context.ip) || [];
ipHistory.push(context);
// Keep only last 100 requests per IP
if (ipHistory.length > 100) {
ipHistory.shift();
}
this.requestHistory.set(context.ip, ipHistory);
}
/**
* Log security event
*/
async logSecurityEvent(event) {
const securityEvent = {
id: crypto_1.default.randomUUID(),
timestamp: new Date(),
...event
};
this.securityEvents.push(securityEvent);
// Keep only last 1000 events
if (this.securityEvents.length > 1000) {
this.securityEvents.shift();
}
this.emit('securityEvent', securityEvent);
}
/**
* Get default threat patterns
*/
getDefaultThreatPatterns() {
return [
{
name: 'SQL Injection Attempt',
description: 'Detects potential SQL injection patterns in requests',
severity: 'high',
action: 'block',
rules: [
{
condition: 'endpoint',
operator: 'contains',
value: 'union select'
}
]
},
{
name: 'Excessive API Calls',
description: 'Detects unusually high API call frequency',
severity: 'medium',
action: 'warn',
rules: [
{
condition: 'requestsPerMinute',
operator: 'greater',
value: 30
}
]
},
{
name: 'Bot User Agent',
description: 'Detects automated bot requests',
severity: 'low',
action: 'warn',
rules: [
{
condition: 'userAgent',
operator: 'matches',
value: '(bot|crawler|spider|scraper)'
}
]
},
{
name: 'Large Payload Attack',
description: 'Detects unusually large request payloads',
severity: 'medium',
action: 'block',
rules: [
{
condition: 'payloadSize',
operator: 'greater',
value: 1000000 // 1MB
}
]
}
];
}
/**
* Get severity score
*/
getSeverityScore(severity) {
switch (severity) {
case 'low': return 10;
case 'medium': return 25;
case 'high': return 50;
case 'critical': return 75;
default: return 0;
}
}
/**
* Get action priority for comparison
*/
getActionPriority(action) {
switch (action) {
case 'log': return 1;
case 'warn': return 2;
case 'block': return 3;
case 'ban': return 4;
default: return 0;
}
}
/**
* Start background cleanup tasks
*/
startCleanupTasks() {
// Clean up old request history every hour
setInterval(() => {
const cutoff = Date.now() - 24 * 60 * 60 * 1000; // 24 hours ago
for (const [ip, requests] of this.requestHistory.entries()) {
const filteredRequests = requests.filter(req => req.timestamp.getTime() > cutoff);
if (filteredRequests.length === 0) {
this.requestHistory.delete(ip);
}
else {
this.requestHistory.set(ip, filteredRequests);
}
}
}, 60 * 60 * 1000); // Every hour
// Reset rate limit counters every minute
setInterval(() => {
const now = Date.now();
for (const [ip, counters] of this.rateLimitCounters.entries()) {
if (now - counters.lastReset.getTime() > 60000) {
counters.minute = 0;
}
if (now - counters.lastReset.getTime() > 3600000) {
counters.hour = 0;
counters.lastReset = new Date(now);
}
}
}, 60 * 1000); // Every minute
}
/**
* Get security metrics
*/
getSecurityMetrics() {
const totalRequests = Array.from(this.requestHistory.values())
.reduce((sum, requests) => sum + requests.length, 0);
const blockedRequests = this.securityEvents.filter(event => event.details.action === 'block' || event.details.action === 'ban').length;
const suspiciousRequests = this.securityEvents.filter(event => event.riskScore > 30).length;
const uniqueIPs = this.requestHistory.size;
const attackTypes = this.securityEvents.reduce((acc, event) => {
acc[event.type] = (acc[event.type] || 0) + 1;
return acc;
}, {});
const topAttackTypes = Object.entries(attackTypes)
.map(([type, count]) => ({ type, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
const averageRiskScore = this.securityEvents.length > 0
? this.securityEvents.reduce((sum, event) => sum + event.riskScore, 0) / this.securityEvents.length
: 0;
const lastThreatDetected = this.securityEvents.length > 0
? this.securityEvents[this.securityEvents.length - 1].timestamp
: undefined;
return {
totalRequests,
blockedRequests,
suspiciousRequests,
uniqueIPs,
topAttackTypes,
averageRiskScore,
lastThreatDetected
};
}
/**
* Get recent security events
*/
getRecentSecurityEvents(limit = 50) {
return this.securityEvents.slice(-limit);
}
/**
* Manually ban IP address
*/
manuallyBanIP(ip, reason) {
this.bannedIPs.add(ip);
this.logSecurityEvent({
type: 'intrusion',
severity: 'high',
source: { ip, userAgent: 'manual-ban' },
details: { reason: `Manual ban: ${reason}` },
riskScore: 100
});
this.emit('ipBanned', ip);
}
/**
* Unban IP address
*/
unbanIP(ip) {
this.bannedIPs.delete(ip);
this.failedAttempts.delete(ip);
this.emit('ipUnbanned', ip);
}
/**
* Update security configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
this.emit('configUpdated', this.config);
}
/**
* Check if service is healthy
*/
healthCheck() {
try {
const metrics = this.getSecurityMetrics();
const recentEvents = this.getRecentSecurityEvents(10);
const criticalEvents = recentEvents.filter(event => event.severity === 'critical' &&
(Date.now() - event.timestamp.getTime()) < 5 * 60 * 1000 // Last 5 minutes
);
if (criticalEvents.length > 0) {
return {
status: 'degraded',
details: {
message: 'Critical security events detected',
criticalEvents: criticalEvents.length,
metrics
}
};
}
return {
status: 'healthy',
details: {
initialized: this.isInitialized,
bannedIPs: this.bannedIPs.size,
metrics
}
};
}
catch (error) {
return {
status: 'error',
details: { error: error instanceof Error ? error.message : 'Unknown error' }
};
}
}
}
exports.RouterSecurityService = RouterSecurityService;
// Export factory function
function createRouterSecurity(config) {
return new RouterSecurityService(config);
}
// Export default configuration
exports.DEFAULT_ROUTER_SECURITY_CONFIG = {
enableThreatDetection: true,
enableRateLimiting: true,
enableAnomalyDetection: true,
maxRequestsPerMinute: 60,
maxRequestsPerHour: 1000,
maxFailedAttemptsBeforeBan: 5,
banDurationMinutes: 60,
suspiciousPatterns: [],
enableGeoBlocking: false,
allowedCountries: [],
blockedIPs: []
};
//# sourceMappingURL=router-security.js.map