feat: implement security logging system
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { SecurityLogger, SecurityEventType } from './logger';
|
||||
|
||||
describe('Security Logging System', () => {
|
||||
let logger: SecurityLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
logger = new SecurityLogger();
|
||||
});
|
||||
|
||||
test('should log security events', () => {
|
||||
logger.logEvent({
|
||||
type: SecurityEventType.CAPTCHA_FAILED,
|
||||
ip: '192.168.1.1',
|
||||
email: 'test@example.com',
|
||||
details: { reason: 'Invalid answer' },
|
||||
});
|
||||
|
||||
const logs = logger.getRecentLogs(10);
|
||||
expect(logs.length).toBe(1);
|
||||
expect(logs[0].type).toBe(SecurityEventType.CAPTCHA_FAILED);
|
||||
});
|
||||
|
||||
test('should detect suspicious activity', () => {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
logger.logEvent({
|
||||
type: SecurityEventType.CAPTCHA_FAILED,
|
||||
ip: '192.168.1.2',
|
||||
email: 'suspicious@example.com',
|
||||
details: {},
|
||||
});
|
||||
}
|
||||
|
||||
const suspiciousIPs = logger.getSuspiciousIPs();
|
||||
expect(suspiciousIPs).toContain('192.168.1.2');
|
||||
});
|
||||
|
||||
test('should clear old logs', () => {
|
||||
logger.logEvent({
|
||||
type: SecurityEventType.RATE_LIMIT_EXCEEDED,
|
||||
ip: '192.168.1.3',
|
||||
details: {},
|
||||
});
|
||||
|
||||
logger.clearOldLogs(0);
|
||||
|
||||
const logs = logger.getRecentLogs(10);
|
||||
expect(logs.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
export enum SecurityEventType {
|
||||
CAPTCHA_FAILED = 'captcha_failed',
|
||||
CAPTCHA_EXPIRED = 'captcha_expired',
|
||||
RATE_LIMIT_EXCEEDED = 'rate_limit_exceeded',
|
||||
MALICIOUS_CONTENT = 'malicious_content',
|
||||
XSS_ATTEMPT = 'xss_attempt',
|
||||
SQL_INJECTION_ATTEMPT = 'sql_injection_attempt',
|
||||
SUSPICIOUS_IP = 'suspicious_ip',
|
||||
BLOCKED_REQUEST = 'blocked_request',
|
||||
}
|
||||
|
||||
export interface SecurityEvent {
|
||||
type: SecurityEventType;
|
||||
timestamp: number;
|
||||
ip?: string;
|
||||
email?: string;
|
||||
userAgent?: string;
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface SuspiciousActivity {
|
||||
ip: string;
|
||||
eventCount: number;
|
||||
lastSeen: number;
|
||||
eventTypes: SecurityEventType[];
|
||||
}
|
||||
|
||||
class InMemorySecurityLogger {
|
||||
private events: SecurityEvent[] = [];
|
||||
private ipActivity = new Map<string, number>();
|
||||
|
||||
logEvent(event: Omit<SecurityEvent, 'timestamp'>): void {
|
||||
const fullEvent: SecurityEvent = {
|
||||
...event,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
this.events.push(fullEvent);
|
||||
|
||||
if (event.ip) {
|
||||
const currentCount = this.ipActivity.get(event.ip) || 0;
|
||||
this.ipActivity.set(event.ip, currentCount + 1);
|
||||
}
|
||||
|
||||
console.log(`[Security] ${event.type}:`, event);
|
||||
}
|
||||
|
||||
getRecentLogs(limit: number = 100): SecurityEvent[] {
|
||||
return this.events.slice(-limit);
|
||||
}
|
||||
|
||||
getSuspiciousIPs(threshold: number = 5): string[] {
|
||||
const suspicious: string[] = [];
|
||||
for (const [ip, count] of this.ipActivity.entries()) {
|
||||
if (count >= threshold) {
|
||||
suspicious.push(ip);
|
||||
}
|
||||
}
|
||||
return suspicious;
|
||||
}
|
||||
|
||||
clearOldLogs(daysToKeep: number = 30): void {
|
||||
const cutoffTime = Date.now() - (daysToKeep * 24 * 60 * 60 * 1000);
|
||||
this.events = this.events.filter(event => event.timestamp > cutoffTime);
|
||||
}
|
||||
|
||||
getActivityByIP(ip: string): number {
|
||||
return this.ipActivity.get(ip) || 0;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.events = [];
|
||||
this.ipActivity.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export class SecurityLogger {
|
||||
private logger: InMemorySecurityLogger;
|
||||
|
||||
constructor() {
|
||||
this.logger = new InMemorySecurityLogger();
|
||||
}
|
||||
|
||||
logEvent(event: Omit<SecurityEvent, 'timestamp'>): void {
|
||||
this.logger.logEvent(event);
|
||||
}
|
||||
|
||||
getRecentLogs(limit?: number): SecurityEvent[] {
|
||||
return this.logger.getRecentLogs(limit);
|
||||
}
|
||||
|
||||
getSuspiciousIPs(threshold?: number): string[] {
|
||||
return this.logger.getSuspiciousIPs(threshold);
|
||||
}
|
||||
|
||||
clearOldLogs(daysToKeep?: number): void {
|
||||
this.logger.clearOldLogs(daysToKeep);
|
||||
}
|
||||
|
||||
getActivityByIP(ip: string): number {
|
||||
return this.logger.getActivityByIP(ip);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.logger.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user