← back to Wine Finder Next

lib/securityMonitoring.ts

272 lines

// Security Monitoring and Intrusion Detection
// NOTE: Removed logAudit import to maintain Edge Runtime compatibility
// This file is used by middleware which runs in Edge Runtime

interface SecurityEvent {
  type: SecurityEventType;
  severity: 'low' | 'medium' | 'high' | 'critical';
  userId?: string;
  ipAddress: string;
  userAgent: string;
  details: any;
  timestamp: Date;
}

type SecurityEventType =
  | 'BRUTE_FORCE_ATTEMPT'
  | 'SQL_INJECTION_ATTEMPT'
  | 'XSS_ATTEMPT'
  | 'RATE_LIMIT_ABUSE'
  | 'INVALID_TOKEN'
  | 'UNAUTHORIZED_ACCESS'
  | 'SUSPICIOUS_ACTIVITY'
  | 'DATA_BREACH_ATTEMPT'
  | 'ANOMALOUS_BEHAVIOR';

// Event storage
const securityEvents: SecurityEvent[] = [];
const suspiciousIPs: Map<string, { score: number; lastSeen: Date }> = new Map();
const blockedIPs: Set<string> = new Set();

// Detect SQL injection attempts
export function detectSQLInjection(input: string): boolean {
  const sqlPatterns = [
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)\b)/i,
    /(UNION\s+SELECT)/i,
    /(OR\s+1\s*=\s*1)/i,
    /(AND\s+1\s*=\s*1)/i,
    /(['";]|--|\*|\/\*)/,
    /(xp_cmdshell|sp_executesql)/i
  ];

  return sqlPatterns.some(pattern => pattern.test(input));
}

// Detect XSS attempts
export function detectXSS(input: string): boolean {
  const xssPatterns = [
    /<script[^>]*>.*?<\/script>/i,
    /javascript:/i,
    /on\w+\s*=/i,
    /<iframe/i,
    /<embed/i,
    /<object/i,
    /eval\(/i,
    /document\.cookie/i,
    /window\.location/i
  ];

  return xssPatterns.some(pattern => pattern.test(input));
}

// Detect path traversal attempts
export function detectPathTraversal(input: string): boolean {
  const traversalPatterns = [
    /\.\.\//,
    /\.\.\\/,
    /%2e%2e/i,
    /\.\.%2f/i,
    /\.\.%5c/i
  ];

  return traversalPatterns.some(pattern => pattern.test(input));
}

// Log security event
export function logSecurityEvent(event: Omit<SecurityEvent, 'timestamp'>): void {
  const fullEvent: SecurityEvent = {
    ...event,
    timestamp: new Date()
  };

  securityEvents.push(fullEvent);

  // Audit logging disabled for Edge Runtime compatibility
  // (Edge Runtime doesn't support Node.js crypto module)
  // In production API routes, events are already logged via logAudit calls

  // Update suspicious IP score
  updateSuspiciousIPScore(event.ipAddress, event.severity);

  // Auto-block on critical events
  if (event.severity === 'critical') {
    blockIP(event.ipAddress, 'Automatic block due to critical security event');
  }

  // Alert on high/critical severity
  if (['high', 'critical'].includes(event.severity)) {
    alertSecurityTeam(fullEvent);
  }
}

// Update suspicious IP scoring
function updateSuspiciousIPScore(ipAddress: string, severity: string): void {
  const scores = { low: 1, medium: 5, high: 10, critical: 50 };
  const score = scores[severity as keyof typeof scores] || 1;

  const current = suspiciousIPs.get(ipAddress) || { score: 0, lastSeen: new Date() };
  current.score += score;
  current.lastSeen = new Date();

  suspiciousIPs.set(ipAddress, current);

  // Auto-block if score exceeds threshold
  if (current.score >= 100) {
    blockIP(ipAddress, 'Suspicious activity score exceeded threshold');
  }
}

// Block IP address
export function blockIP(ipAddress: string, reason: string): void {
  blockedIPs.add(ipAddress);

  logSecurityEvent({
    type: 'UNAUTHORIZED_ACCESS',
    severity: 'high',
    ipAddress,
    userAgent: 'system',
    details: { reason, action: 'IP_BLOCKED' }
  });

  console.warn(`[SECURITY] IP BLOCKED: ${ipAddress} - ${reason}`);
}

// Check if IP is blocked
export function isIPBlocked(ipAddress: string): boolean {
  return blockedIPs.has(ipAddress);
}

// Unblock IP address
export function unblockIP(ipAddress: string): void {
  blockedIPs.delete(ipAddress);
  suspiciousIPs.delete(ipAddress);
}

// Analyze request for threats
export function analyzeRequest(req: {
  body?: any;
  query?: any;
  params?: any;
  headers: any;
  ip: string;
}): { safe: boolean; threats: string[] } {
  const threats: string[] = [];

  // Check if IP is blocked
  if (isIPBlocked(req.ip)) {
    threats.push('BLOCKED_IP');
    return { safe: false, threats };
  }

  // Analyze all inputs
  const inputs = [
    ...Object.values(req.body || {}),
    ...Object.values(req.query || {}),
    ...Object.values(req.params || {})
  ].filter(v => typeof v === 'string');

  for (const input of inputs) {
    if (detectSQLInjection(input)) {
      threats.push('SQL_INJECTION');
      logSecurityEvent({
        type: 'SQL_INJECTION_ATTEMPT',
        severity: 'high',
        ipAddress: req.ip,
        userAgent: req.headers['user-agent'] || 'unknown',
        details: { input: input.substring(0, 100) }
      });
    }

    if (detectXSS(input)) {
      threats.push('XSS');
      logSecurityEvent({
        type: 'XSS_ATTEMPT',
        severity: 'high',
        ipAddress: req.ip,
        userAgent: req.headers['user-agent'] || 'unknown',
        details: { input: input.substring(0, 100) }
      });
    }

    if (detectPathTraversal(input)) {
      threats.push('PATH_TRAVERSAL');
      logSecurityEvent({
        type: 'DATA_BREACH_ATTEMPT',
        severity: 'critical',
        ipAddress: req.ip,
        userAgent: req.headers['user-agent'] || 'unknown',
        details: { input: input.substring(0, 100) }
      });
    }
  }

  return { safe: threats.length === 0, threats };
}

// Alert security team (in production, send to Slack/email/PagerDuty)
function alertSecurityTeam(event: SecurityEvent): void {
  console.error('[SECURITY ALERT]', JSON.stringify(event, null, 2));

  // In production:
  // - Send to Slack webhook
  // - Email security team
  // - Trigger PagerDuty alert
  // - Log to SIEM system
}

// Get security dashboard data
export function getSecurityDashboard(): {
  recentEvents: SecurityEvent[];
  suspiciousIPs: Array<{ ip: string; score: number; lastSeen: Date }>;
  blockedIPs: string[];
  stats: {
    total: number;
    last24h: number;
    bySeverity: Record<string, number>;
  };
} {
  const last24h = Date.now() - 24 * 60 * 60 * 1000;
  const recent24h = securityEvents.filter(e => e.timestamp.getTime() > last24h);

  const bySeverity: Record<string, number> = {};
  for (const event of securityEvents) {
    bySeverity[event.severity] = (bySeverity[event.severity] || 0) + 1;
  }

  return {
    recentEvents: securityEvents.slice(-100),
    suspiciousIPs: Array.from(suspiciousIPs.entries()).map(([ip, data]) => ({
      ip,
      ...data
    })),
    blockedIPs: Array.from(blockedIPs),
    stats: {
      total: securityEvents.length,
      last24h: recent24h.length,
      bySeverity
    }
  };
}

// Cleanup old events
export function cleanupSecurityEvents(): void {
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000; // 30 days

  const filteredEvents = securityEvents.filter(e => e.timestamp.getTime() > cutoff);
  securityEvents.length = 0;
  securityEvents.push(...filteredEvents);

  // Decay suspicious IP scores
  for (const [ip, data] of suspiciousIPs.entries()) {
    if (Date.now() - data.lastSeen.getTime() > 7 * 24 * 60 * 60 * 1000) {
      data.score = Math.max(0, data.score - 10);
      if (data.score === 0) {
        suspiciousIPs.delete(ip);
      }
    }
  }
}

// Run cleanup periodically
setInterval(cleanupSecurityEvents, 60 * 60 * 1000); // Every hour