feat(security): add intrusion detection system

Add pattern-based intrusion detector with attack recognition for:
- Brute force attacks (10 failures in 10min)
- SSRF bypass attempts (3 attempts in 5min)
- Path traversal attempts (5 attempts in 5min)
- Port scanning (20 connections in 10sec)

Features:
- Event aggregation with sliding windows
- Auto-blocking on detection
- Configurable thresholds per pattern
- Security event logging for all detections

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Ulrich Diedrichsen 2026-01-30 10:36:48 +01:00
parent 73ce95d9cc
commit 6c6d11c354

View File

@ -0,0 +1,266 @@
/**
* Intrusion detection system
* Pattern-based attack detection with configurable thresholds
*/
import { securityEventAggregator } from "./events/aggregator.js";
import { securityLogger } from "./events/logger.js";
import { SecurityActions, AttackPatterns, type SecurityEvent } from "./events/schema.js";
import { ipManager } from "./ip-manager.js";
import type { SecurityShieldConfig } from "../config/types.security.js";
export interface AttackPatternConfig {
threshold: number;
windowMs: number;
}
export interface IntrusionDetectionResult {
detected: boolean;
pattern?: string;
count?: number;
threshold?: number;
}
/**
* Intrusion detector
*/
export class IntrusionDetector {
private config: Required<NonNullable<SecurityShieldConfig["intrusionDetection"]>>;
constructor(config?: SecurityShieldConfig["intrusionDetection"]) {
this.config = {
enabled: config?.enabled ?? true,
patterns: {
bruteForce: config?.patterns?.bruteForce ?? { threshold: 10, windowMs: 600_000 },
ssrfBypass: config?.patterns?.ssrfBypass ?? { threshold: 3, windowMs: 300_000 },
pathTraversal: config?.patterns?.pathTraversal ?? { threshold: 5, windowMs: 300_000 },
portScanning: config?.patterns?.portScanning ?? { threshold: 20, windowMs: 10_000 },
},
anomalyDetection: config?.anomalyDetection ?? {
enabled: false,
learningPeriodMs: 86_400_000,
sensitivityScore: 0.95,
},
};
}
/**
* Check for brute force attack pattern
*/
checkBruteForce(params: {
ip: string;
event: SecurityEvent;
}): IntrusionDetectionResult {
if (!this.config.enabled) {
return { detected: false };
}
const { ip, event } = params;
const pattern = this.config.patterns.bruteForce;
const key = `brute_force:${ip}`;
const crossed = securityEventAggregator.trackEvent({
key,
event,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
});
if (crossed) {
const count = securityEventAggregator.getCount({ key, windowMs: pattern.windowMs });
// Log intrusion
securityLogger.logIntrusion({
action: SecurityActions.BRUTE_FORCE_DETECTED,
ip,
resource: event.resource,
attackPattern: AttackPatterns.BRUTE_FORCE,
details: {
failedAttempts: count,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
},
});
// Auto-block if configured
this.autoBlock(ip, AttackPatterns.BRUTE_FORCE);
return {
detected: true,
pattern: AttackPatterns.BRUTE_FORCE,
count,
threshold: pattern.threshold,
};
}
return { detected: false };
}
/**
* Check for SSRF bypass attempts
*/
checkSsrfBypass(params: {
ip: string;
event: SecurityEvent;
}): IntrusionDetectionResult {
if (!this.config.enabled) {
return { detected: false };
}
const { ip, event } = params;
const pattern = this.config.patterns.ssrfBypass;
const key = `ssrf_bypass:${ip}`;
const crossed = securityEventAggregator.trackEvent({
key,
event,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
});
if (crossed) {
const count = securityEventAggregator.getCount({ key, windowMs: pattern.windowMs });
securityLogger.logIntrusion({
action: SecurityActions.SSRF_BYPASS_ATTEMPT,
ip,
resource: event.resource,
attackPattern: AttackPatterns.SSRF_BYPASS,
details: {
attempts: count,
threshold: pattern.threshold,
},
});
this.autoBlock(ip, AttackPatterns.SSRF_BYPASS);
return {
detected: true,
pattern: AttackPatterns.SSRF_BYPASS,
count,
threshold: pattern.threshold,
};
}
return { detected: false };
}
/**
* Check for path traversal attempts
*/
checkPathTraversal(params: {
ip: string;
event: SecurityEvent;
}): IntrusionDetectionResult {
if (!this.config.enabled) {
return { detected: false };
}
const { ip, event } = params;
const pattern = this.config.patterns.pathTraversal;
const key = `path_traversal:${ip}`;
const crossed = securityEventAggregator.trackEvent({
key,
event,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
});
if (crossed) {
const count = securityEventAggregator.getCount({ key, windowMs: pattern.windowMs });
securityLogger.logIntrusion({
action: SecurityActions.PATH_TRAVERSAL_ATTEMPT,
ip,
resource: event.resource,
attackPattern: AttackPatterns.PATH_TRAVERSAL,
details: {
attempts: count,
threshold: pattern.threshold,
},
});
this.autoBlock(ip, AttackPatterns.PATH_TRAVERSAL);
return {
detected: true,
pattern: AttackPatterns.PATH_TRAVERSAL,
count,
threshold: pattern.threshold,
};
}
return { detected: false };
}
/**
* Check for port scanning
*/
checkPortScanning(params: {
ip: string;
event: SecurityEvent;
}): IntrusionDetectionResult {
if (!this.config.enabled) {
return { detected: false };
}
const { ip, event } = params;
const pattern = this.config.patterns.portScanning;
const key = `port_scan:${ip}`;
const crossed = securityEventAggregator.trackEvent({
key,
event,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
});
if (crossed) {
const count = securityEventAggregator.getCount({ key, windowMs: pattern.windowMs });
securityLogger.logIntrusion({
action: SecurityActions.PORT_SCANNING_DETECTED,
ip,
resource: event.resource,
attackPattern: AttackPatterns.PORT_SCANNING,
details: {
connections: count,
threshold: pattern.threshold,
windowMs: pattern.windowMs,
},
});
this.autoBlock(ip, AttackPatterns.PORT_SCANNING);
return {
detected: true,
pattern: AttackPatterns.PORT_SCANNING,
count,
threshold: pattern.threshold,
};
}
return { detected: false };
}
/**
* Auto-block IP if configured
*/
private autoBlock(ip: string, pattern: string): void {
// Use default 24h block duration
const durationMs = 86_400_000; // Will be configurable later
ipManager.blockIp({
ip,
reason: pattern,
durationMs,
source: "auto",
});
}
}
/**
* Singleton intrusion detector
*/
export const intrusionDetector = new IntrusionDetector();