@devilsdev/rag-pipeline-utils 2.1.11 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ragrc.schema.json +218 -0
- package/README.md +651 -393
- package/bin/cli.js +8 -14
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +17 -13
- package/src/ai/adaptive-retrieval.js +191 -0
- package/src/ai/federated-learning.js +214 -0
- package/src/ai/federation/federated-learning-coordinator.js +345 -0
- package/src/ai/federation.js +76 -0
- package/src/ai/index-fixed.js +509 -0
- package/src/ai/index.js +33 -0
- package/src/ai/model-trainer.js +217 -0
- package/src/ai/model-training.js +205 -0
- package/src/ai/module-template.js +37 -0
- package/src/ai/multimodal/multi-modal-processor.js +345 -0
- package/src/ai/multimodal-processing.js +268 -0
- package/src/ai/multimodal.js +71 -0
- package/src/ai/orchestrator.js +70 -0
- package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
- package/src/ai/retrieval-engine.js +69 -0
- package/src/ai/training/model-training-orchestrator.js +228 -0
- package/src/ci/workflow-analyzer.js +71 -0
- package/src/cli/commands/ai-ml.js +967 -0
- package/src/cli/commands/docs.js +228 -0
- package/src/cli/commands/dx.js +538 -0
- package/src/cli/commands/plugin-hub.js +875 -0
- package/src/cli/doctor-command.js +639 -0
- package/src/cli/enhanced-cli-commands.js +945 -0
- package/src/cli/interactive-wizard.js +997 -0
- package/src/cli/plugin-marketplace-commands.js +869 -0
- package/src/cli.js +17 -0
- package/src/config/enhanced-ragrc-schema.js +632 -0
- package/src/config/load-config.js +54 -0
- package/src/config/load-plugin-config.js +69 -0
- package/src/config/normalize-config.js +105 -0
- package/src/config/schema-validator.js +315 -0
- package/src/config/validate-plugin-schema.js +35 -0
- package/src/config/validate-schema.js +113 -0
- package/src/contracts/plugin-contracts.js +33 -0
- package/src/core/create-pipeline.js +99 -0
- package/src/core/dependency-injection.js +242 -0
- package/src/core/observability/event-logger.js +241 -0
- package/src/core/observability/instrumented-pipeline.js +609 -0
- package/src/core/observability/metrics.js +497 -0
- package/src/core/observability/tracing.js +781 -0
- package/src/core/performance/benchmark.js +516 -0
- package/src/core/performance/parallel-processor.js +240 -0
- package/src/core/performance/streaming-safeguards.js +611 -0
- package/src/core/pipeline-factory.js +60 -0
- package/src/core/plugin-contracts.js +89 -0
- package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
- package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
- package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
- package/src/core/plugin-marketplace/version-resolver.js +61 -0
- package/src/core/plugin-registry.js +265 -0
- package/src/dag/dag-engine.js +954 -0
- package/src/dag/dag-node.js +76 -0
- package/src/dag/enhanced-dag-engine.js +822 -0
- package/src/dx/index.js +40 -0
- package/src/dx/integration-templates.js +665 -0
- package/src/dx/performance-profiler.js +968 -0
- package/src/dx/realtime-debugger.js +755 -0
- package/src/dx/visual-pipeline-builder.js +578 -0
- package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
- package/src/ecosystem/plugin-certification.js +580 -0
- package/src/ecosystem/plugin-hub.js +821 -0
- package/src/enterprise/audit-logging.js +967 -0
- package/src/enterprise/data-governance.js +408 -0
- package/src/enterprise/multi-tenancy.js +496 -0
- package/src/enterprise/sso-integration.js +903 -0
- package/src/evaluate/evaluator.js +52 -0
- package/src/evaluate/scoring.js +91 -0
- package/src/index.js +67 -0
- package/src/ingest.js +59 -0
- package/src/loader/csv-loader.js +38 -0
- package/src/loader/directory-loader.js +48 -0
- package/src/loader/html-loader.js +62 -0
- package/src/loader/markdown-loader.js +67 -0
- package/src/mocks/openai-embedder.js +119 -0
- package/src/mocks/openai-llm.js +77 -0
- package/src/mocks/pdf-loader.js +21 -0
- package/src/mocks/pinecone-retriever.js +106 -0
- package/src/observability/audit-logger.js +758 -0
- package/src/observability/dashboard-config.json +675 -0
- package/src/observability/event-logger.js +310 -0
- package/src/observability/metrics.js +691 -0
- package/src/observability/security-monitor.js +830 -0
- package/src/observability/slo-monitor.js +319 -0
- package/src/observability/telemetry.js +686 -0
- package/src/pipeline/pipeline.js +18 -0
- package/src/plugins/sample-embedder.js +53 -0
- package/src/plugins/sample-llm.js +30 -0
- package/src/plugins/sample-loader.js +24 -0
- package/src/plugins/sample-retriever.js +84 -0
- package/src/query.js +117 -0
- package/src/reranker/llm-reranker.js +85 -0
- package/src/scripts/script-utilities.js +36 -0
- package/src/security/plugin-sandbox.js +586 -0
- package/src/security/plugin-signature-verifier.js +414 -0
- package/src/security/validator.js +36 -0
- package/src/utils/ci/diagnostic-reporter.js +57 -0
- package/src/utils/logger.js +211 -0
- package/src/utils/performance-monitor.js +561 -0
- package/src/utils/plugin-scaffolder.js +447 -0
- package/src/utils/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/structured-logger.js +361 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Security monitoring and alerting system for the RAG pipeline.
|
|
3
|
+
* Provides real-time threat detection, anomaly analysis, and automated incident response.
|
|
4
|
+
*
|
|
5
|
+
* @author DevilsDev Team
|
|
6
|
+
* @since 2.1.0
|
|
7
|
+
* @version 2.1.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { EventEmitter } = require('events');
|
|
11
|
+
const fs = require('fs').promises;
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const crypto = require('crypto');
|
|
14
|
+
const logger = require('../utils/structured-logger');
|
|
15
|
+
const { getTelemetryService } = require('./telemetry');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Security monitoring service for threat detection and incident response.
|
|
19
|
+
*
|
|
20
|
+
* Features:
|
|
21
|
+
* - Real-time security event monitoring
|
|
22
|
+
* - Anomaly detection and pattern analysis
|
|
23
|
+
* - Automated threat response
|
|
24
|
+
* - Security metrics and dashboards
|
|
25
|
+
* - Integration with SIEM systems
|
|
26
|
+
* - Compliance audit trails
|
|
27
|
+
*
|
|
28
|
+
* @class SecurityMonitor
|
|
29
|
+
* @extends EventEmitter
|
|
30
|
+
*/
|
|
31
|
+
class SecurityMonitor extends EventEmitter {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
super();
|
|
34
|
+
|
|
35
|
+
this.config = {
|
|
36
|
+
// Monitoring Configuration
|
|
37
|
+
enabled: options.enabled !== false,
|
|
38
|
+
checkInterval: options.checkInterval || 30000, // 30 seconds
|
|
39
|
+
alertThreshold: options.alertThreshold || 5, // 5 events trigger alert
|
|
40
|
+
retentionDays: options.retentionDays || 30,
|
|
41
|
+
|
|
42
|
+
// Threat Detection
|
|
43
|
+
threatDetection: {
|
|
44
|
+
enabled: options.threatDetection?.enabled !== false,
|
|
45
|
+
rateLimitThreshold: options.threatDetection?.rateLimitThreshold || 100,
|
|
46
|
+
timeWindow: options.threatDetection?.timeWindow || 60000, // 1 minute
|
|
47
|
+
bruteForceThreshold: options.threatDetection?.bruteForceThreshold || 10,
|
|
48
|
+
suspiciousPatterns: options.threatDetection?.suspiciousPatterns || [],
|
|
49
|
+
...options.threatDetection,
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Anomaly Detection
|
|
53
|
+
anomalyDetection: {
|
|
54
|
+
enabled: options.anomalyDetection?.enabled !== false,
|
|
55
|
+
baselineWindow: options.anomalyDetection?.baselineWindow || 3600000, // 1 hour
|
|
56
|
+
deviationThreshold: options.anomalyDetection?.deviationThreshold || 2.5, // 2.5 standard deviations
|
|
57
|
+
minSamples: options.anomalyDetection?.minSamples || 30,
|
|
58
|
+
...options.anomalyDetection,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Alerting Configuration
|
|
62
|
+
alerting: {
|
|
63
|
+
enabled: options.alerting?.enabled !== false,
|
|
64
|
+
channels: options.alerting?.channels || ['log'], // log, webhook, email
|
|
65
|
+
webhookUrl: options.alerting?.webhookUrl,
|
|
66
|
+
emailConfig: options.alerting?.emailConfig,
|
|
67
|
+
severity: {
|
|
68
|
+
low: options.alerting?.severity?.low || 'info',
|
|
69
|
+
medium: options.alerting?.severity?.medium || 'warn',
|
|
70
|
+
high: options.alerting?.severity?.high || 'error',
|
|
71
|
+
critical: options.alerting?.severity?.critical || 'error',
|
|
72
|
+
},
|
|
73
|
+
...options.alerting,
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// Response Configuration
|
|
77
|
+
autoResponse: {
|
|
78
|
+
enabled: options.autoResponse?.enabled !== false,
|
|
79
|
+
blockSuspiciousIPs: options.autoResponse?.blockSuspiciousIPs !== false,
|
|
80
|
+
rateLimitViolators: options.autoResponse?.rateLimitViolators !== false,
|
|
81
|
+
quarantineThreats: options.autoResponse?.quarantineThreats !== false,
|
|
82
|
+
...options.autoResponse,
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// Storage Configuration
|
|
86
|
+
storage: {
|
|
87
|
+
type: options.storage?.type || 'file', // file, database, s3
|
|
88
|
+
location: options.storage?.location || './security-logs',
|
|
89
|
+
maxFileSize: options.storage?.maxFileSize || 100 * 1024 * 1024, // 100MB
|
|
90
|
+
compression: options.storage?.compression !== false,
|
|
91
|
+
...options.storage,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
...options,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Internal state
|
|
98
|
+
this.isRunning = false;
|
|
99
|
+
this.securityEvents = new Map();
|
|
100
|
+
this.threatMetrics = new Map();
|
|
101
|
+
this.anomalyBaselines = new Map();
|
|
102
|
+
this.blockedEntities = new Set();
|
|
103
|
+
this.activeIncidents = new Map();
|
|
104
|
+
this.monitoringInterval = null;
|
|
105
|
+
|
|
106
|
+
// Event counters and rate tracking
|
|
107
|
+
this.eventCounters = new Map();
|
|
108
|
+
this.rateTracking = new Map();
|
|
109
|
+
|
|
110
|
+
// Telemetry service
|
|
111
|
+
this.telemetry = getTelemetryService();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Start the security monitoring service.
|
|
116
|
+
*
|
|
117
|
+
* @returns {Promise<void>} Start promise
|
|
118
|
+
*/
|
|
119
|
+
async start() {
|
|
120
|
+
if (this.isRunning) {
|
|
121
|
+
logger.warn('Security monitor already running');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!this.config.enabled) {
|
|
126
|
+
logger.info('Security monitoring is disabled');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
logger.info('Starting security monitoring service', {
|
|
132
|
+
checkInterval: this.config.checkInterval,
|
|
133
|
+
threatDetection: this.config.threatDetection.enabled,
|
|
134
|
+
anomalyDetection: this.config.anomalyDetection.enabled,
|
|
135
|
+
autoResponse: this.config.autoResponse.enabled,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Initialize storage
|
|
139
|
+
await this.initializeStorage();
|
|
140
|
+
|
|
141
|
+
// Load historical data for baselines
|
|
142
|
+
await this.loadBaselines();
|
|
143
|
+
|
|
144
|
+
// Start monitoring loop
|
|
145
|
+
this.startMonitoringLoop();
|
|
146
|
+
|
|
147
|
+
// Setup event listeners
|
|
148
|
+
this.setupEventListeners();
|
|
149
|
+
|
|
150
|
+
this.isRunning = true;
|
|
151
|
+
|
|
152
|
+
// Record telemetry
|
|
153
|
+
this.telemetry.recordMetric('security_monitor_started', 1, {
|
|
154
|
+
service: 'security-monitor',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
logger.info('Security monitoring service started successfully');
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logger.error('Failed to start security monitoring service', {
|
|
160
|
+
error: error.message,
|
|
161
|
+
stack: error.stack,
|
|
162
|
+
});
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Stop the security monitoring service.
|
|
169
|
+
*
|
|
170
|
+
* @returns {Promise<void>} Stop promise
|
|
171
|
+
*/
|
|
172
|
+
async stop() {
|
|
173
|
+
if (!this.isRunning) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
logger.info('Stopping security monitoring service');
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// Stop monitoring loop
|
|
181
|
+
if (this.monitoringInterval) {
|
|
182
|
+
clearInterval(this.monitoringInterval);
|
|
183
|
+
this.monitoringInterval = null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Save current state
|
|
187
|
+
await this.saveState();
|
|
188
|
+
|
|
189
|
+
// Clear event listeners
|
|
190
|
+
this.removeAllListeners();
|
|
191
|
+
|
|
192
|
+
this.isRunning = false;
|
|
193
|
+
|
|
194
|
+
logger.info('Security monitoring service stopped successfully');
|
|
195
|
+
} catch (error) {
|
|
196
|
+
logger.error('Error stopping security monitoring service', {
|
|
197
|
+
error: error.message,
|
|
198
|
+
});
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Record a security event for monitoring and analysis.
|
|
205
|
+
*
|
|
206
|
+
* @param {Object} event - Security event data
|
|
207
|
+
* @param {string} event.type - Event type (auth_failure, rate_limit, etc.)
|
|
208
|
+
* @param {string} event.severity - Event severity (low, medium, high, critical)
|
|
209
|
+
* @param {string} event.source - Event source identifier
|
|
210
|
+
* @param {Object} event.details - Additional event details
|
|
211
|
+
*/
|
|
212
|
+
async recordSecurityEvent(event) {
|
|
213
|
+
if (!this.isRunning || !this.config.enabled) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const timestamp = Date.now();
|
|
219
|
+
const eventId = crypto.randomUUID();
|
|
220
|
+
|
|
221
|
+
const securityEvent = {
|
|
222
|
+
id: eventId,
|
|
223
|
+
timestamp,
|
|
224
|
+
type: event.type,
|
|
225
|
+
severity: event.severity || 'medium',
|
|
226
|
+
source: event.source,
|
|
227
|
+
details: event.details || {},
|
|
228
|
+
...event,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Store event
|
|
232
|
+
this.securityEvents.set(eventId, securityEvent);
|
|
233
|
+
|
|
234
|
+
// Update counters
|
|
235
|
+
this.updateEventCounters(securityEvent);
|
|
236
|
+
|
|
237
|
+
// Track rate metrics
|
|
238
|
+
this.trackRateMetrics(securityEvent);
|
|
239
|
+
|
|
240
|
+
// Perform threat detection
|
|
241
|
+
await this.detectThreats(securityEvent);
|
|
242
|
+
|
|
243
|
+
// Perform anomaly detection
|
|
244
|
+
await this.detectAnomalies(securityEvent);
|
|
245
|
+
|
|
246
|
+
// Log event with correlation
|
|
247
|
+
await logger.withCorrelation(eventId, async () => {
|
|
248
|
+
logger.security('Security event recorded', {
|
|
249
|
+
eventId,
|
|
250
|
+
type: securityEvent.type,
|
|
251
|
+
severity: securityEvent.severity,
|
|
252
|
+
source: securityEvent.source,
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Record telemetry
|
|
257
|
+
this.telemetry.recordMetric('security_events_total', 1, {
|
|
258
|
+
type: securityEvent.type,
|
|
259
|
+
severity: securityEvent.severity,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Emit event for external handlers
|
|
263
|
+
this.emit('securityEvent', securityEvent);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
logger.error('Failed to record security event', {
|
|
266
|
+
event,
|
|
267
|
+
error: error.message,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Detect threats based on security event patterns.
|
|
274
|
+
*
|
|
275
|
+
* @private
|
|
276
|
+
* @param {Object} event - Security event
|
|
277
|
+
*/
|
|
278
|
+
async detectThreats(event) {
|
|
279
|
+
if (!this.config.threatDetection.enabled) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const threats = [];
|
|
284
|
+
|
|
285
|
+
// Rate limiting detection
|
|
286
|
+
const rateViolation = this.detectRateLimitViolation(event);
|
|
287
|
+
if (rateViolation) {
|
|
288
|
+
threats.push(rateViolation);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Brute force detection
|
|
292
|
+
const bruteForce = this.detectBruteForce(event);
|
|
293
|
+
if (bruteForce) {
|
|
294
|
+
threats.push(bruteForce);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Suspicious pattern detection
|
|
298
|
+
const suspiciousPattern = this.detectSuspiciousPatterns(event);
|
|
299
|
+
if (suspiciousPattern) {
|
|
300
|
+
threats.push(suspiciousPattern);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Process detected threats
|
|
304
|
+
for (const threat of threats) {
|
|
305
|
+
await this.handleThreat(threat);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Detect rate limit violations.
|
|
311
|
+
*
|
|
312
|
+
* @private
|
|
313
|
+
* @param {Object} event - Security event
|
|
314
|
+
* @returns {Object|null} Threat detection result
|
|
315
|
+
*/
|
|
316
|
+
detectRateLimitViolation(event) {
|
|
317
|
+
const { rateLimitThreshold, timeWindow } = this.config.threatDetection;
|
|
318
|
+
const now = Date.now();
|
|
319
|
+
const windowStart = now - timeWindow;
|
|
320
|
+
|
|
321
|
+
// Count events from the same source in the time window
|
|
322
|
+
const sourceEvents = Array.from(this.securityEvents.values()).filter(
|
|
323
|
+
(e) => e.source === event.source && e.timestamp >= windowStart,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (sourceEvents.length > rateLimitThreshold) {
|
|
327
|
+
return {
|
|
328
|
+
type: 'rate_limit_violation',
|
|
329
|
+
severity: 'high',
|
|
330
|
+
source: event.source,
|
|
331
|
+
details: {
|
|
332
|
+
eventCount: sourceEvents.length,
|
|
333
|
+
threshold: rateLimitThreshold,
|
|
334
|
+
timeWindow: timeWindow,
|
|
335
|
+
events: sourceEvents.slice(-5), // Last 5 events
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Detect brute force attacks.
|
|
345
|
+
*
|
|
346
|
+
* @private
|
|
347
|
+
* @param {Object} event - Security event
|
|
348
|
+
* @returns {Object|null} Threat detection result
|
|
349
|
+
*/
|
|
350
|
+
detectBruteForce(event) {
|
|
351
|
+
if (event.type !== 'auth_failure') {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const { bruteForceThreshold, timeWindow } = this.config.threatDetection;
|
|
356
|
+
const now = Date.now();
|
|
357
|
+
const windowStart = now - timeWindow;
|
|
358
|
+
|
|
359
|
+
// Count authentication failures from the same source
|
|
360
|
+
const authFailures = Array.from(this.securityEvents.values()).filter(
|
|
361
|
+
(e) =>
|
|
362
|
+
e.type === 'auth_failure' &&
|
|
363
|
+
e.source === event.source &&
|
|
364
|
+
e.timestamp >= windowStart,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
if (authFailures.length >= bruteForceThreshold) {
|
|
368
|
+
return {
|
|
369
|
+
type: 'brute_force_attack',
|
|
370
|
+
severity: 'critical',
|
|
371
|
+
source: event.source,
|
|
372
|
+
details: {
|
|
373
|
+
failureCount: authFailures.length,
|
|
374
|
+
threshold: bruteForceThreshold,
|
|
375
|
+
timeWindow: timeWindow,
|
|
376
|
+
attempts: authFailures.map((e) => ({
|
|
377
|
+
timestamp: e.timestamp,
|
|
378
|
+
details: e.details,
|
|
379
|
+
})),
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Detect suspicious patterns in event data.
|
|
389
|
+
*
|
|
390
|
+
* @private
|
|
391
|
+
* @param {Object} event - Security event
|
|
392
|
+
* @returns {Object|null} Threat detection result
|
|
393
|
+
*/
|
|
394
|
+
detectSuspiciousPatterns(event) {
|
|
395
|
+
const { suspiciousPatterns } = this.config.threatDetection;
|
|
396
|
+
|
|
397
|
+
for (const pattern of suspiciousPatterns) {
|
|
398
|
+
const match = this.matchPattern(event, pattern);
|
|
399
|
+
if (match) {
|
|
400
|
+
return {
|
|
401
|
+
type: 'suspicious_pattern',
|
|
402
|
+
severity: pattern.severity || 'medium',
|
|
403
|
+
source: event.source,
|
|
404
|
+
details: {
|
|
405
|
+
pattern: pattern.name || 'unnamed',
|
|
406
|
+
match: match,
|
|
407
|
+
event: event,
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Detect anomalies in security event patterns.
|
|
418
|
+
*
|
|
419
|
+
* @private
|
|
420
|
+
* @param {Object} event - Security event
|
|
421
|
+
*/
|
|
422
|
+
async detectAnomalies(event) {
|
|
423
|
+
if (!this.config.anomalyDetection.enabled) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const baseline = this.getBaseline(event.type);
|
|
428
|
+
if (
|
|
429
|
+
!baseline ||
|
|
430
|
+
baseline.samples.length < this.config.anomalyDetection.minSamples
|
|
431
|
+
) {
|
|
432
|
+
// Not enough data for anomaly detection
|
|
433
|
+
this.updateBaseline(event);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const currentRate = this.getCurrentRate(event.type);
|
|
438
|
+
const anomaly = this.isAnomaly(currentRate, baseline);
|
|
439
|
+
|
|
440
|
+
if (anomaly) {
|
|
441
|
+
await this.handleAnomaly({
|
|
442
|
+
type: 'anomaly_detected',
|
|
443
|
+
severity: anomaly.severity,
|
|
444
|
+
eventType: event.type,
|
|
445
|
+
details: {
|
|
446
|
+
currentRate,
|
|
447
|
+
expectedRate: baseline.mean,
|
|
448
|
+
deviation: anomaly.deviation,
|
|
449
|
+
threshold: this.config.anomalyDetection.deviationThreshold,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Update baseline with new data
|
|
455
|
+
this.updateBaseline(event);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Handle detected threats with appropriate response.
|
|
460
|
+
*
|
|
461
|
+
* @private
|
|
462
|
+
* @param {Object} threat - Threat detection result
|
|
463
|
+
*/
|
|
464
|
+
async handleThreat(threat) {
|
|
465
|
+
const incidentId = crypto.randomUUID();
|
|
466
|
+
const incident = {
|
|
467
|
+
id: incidentId,
|
|
468
|
+
timestamp: Date.now(),
|
|
469
|
+
threat,
|
|
470
|
+
status: 'active',
|
|
471
|
+
responseActions: [],
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// Store active incident
|
|
475
|
+
this.activeIncidents.set(incidentId, incident);
|
|
476
|
+
|
|
477
|
+
// Log threat detection
|
|
478
|
+
logger.security('Threat detected', {
|
|
479
|
+
incidentId,
|
|
480
|
+
threatType: threat.type,
|
|
481
|
+
severity: threat.severity,
|
|
482
|
+
source: threat.source,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Send alert
|
|
486
|
+
await this.sendAlert(threat, incidentId);
|
|
487
|
+
|
|
488
|
+
// Execute automated response
|
|
489
|
+
if (this.config.autoResponse.enabled) {
|
|
490
|
+
await this.executeAutoResponse(threat, incident);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Record telemetry
|
|
494
|
+
this.telemetry.recordMetric('threats_detected', 1, {
|
|
495
|
+
type: threat.type,
|
|
496
|
+
severity: threat.severity,
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Emit threat event
|
|
500
|
+
this.emit('threatDetected', { threat, incident });
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Execute automated threat response actions.
|
|
505
|
+
*
|
|
506
|
+
* @private
|
|
507
|
+
* @param {Object} threat - Threat data
|
|
508
|
+
* @param {Object} incident - Incident data
|
|
509
|
+
*/
|
|
510
|
+
async executeAutoResponse(threat, incident) {
|
|
511
|
+
const actions = [];
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
// Block suspicious IP/source
|
|
515
|
+
if (this.config.autoResponse.blockSuspiciousIPs && threat.source) {
|
|
516
|
+
await this.blockEntity(threat.source, 'Automated threat response');
|
|
517
|
+
actions.push(`Blocked entity: ${threat.source}`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Rate limit violators
|
|
521
|
+
if (
|
|
522
|
+
this.config.autoResponse.rateLimitViolators &&
|
|
523
|
+
threat.type === 'rate_limit_violation'
|
|
524
|
+
) {
|
|
525
|
+
await this.applyRateLimit(threat.source);
|
|
526
|
+
actions.push(`Applied rate limit to: ${threat.source}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Quarantine threats
|
|
530
|
+
if (
|
|
531
|
+
this.config.autoResponse.quarantineThreats &&
|
|
532
|
+
threat.severity === 'critical'
|
|
533
|
+
) {
|
|
534
|
+
await this.quarantineThreat(threat);
|
|
535
|
+
actions.push('Threat quarantined');
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Update incident with response actions
|
|
539
|
+
incident.responseActions = actions;
|
|
540
|
+
this.activeIncidents.set(incident.id, incident);
|
|
541
|
+
|
|
542
|
+
logger.info('Automated threat response executed', {
|
|
543
|
+
incidentId: incident.id,
|
|
544
|
+
actions,
|
|
545
|
+
});
|
|
546
|
+
} catch (error) {
|
|
547
|
+
logger.error('Failed to execute automated response', {
|
|
548
|
+
incidentId: incident.id,
|
|
549
|
+
error: error.message,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Send security alert through configured channels.
|
|
556
|
+
*
|
|
557
|
+
* @private
|
|
558
|
+
* @param {Object} threat - Threat data
|
|
559
|
+
* @param {string} incidentId - Incident identifier
|
|
560
|
+
*/
|
|
561
|
+
async sendAlert(threat, incidentId) {
|
|
562
|
+
if (!this.config.alerting.enabled) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const alert = {
|
|
567
|
+
id: crypto.randomUUID(),
|
|
568
|
+
timestamp: Date.now(),
|
|
569
|
+
incidentId,
|
|
570
|
+
threat,
|
|
571
|
+
severity: threat.severity,
|
|
572
|
+
title: `Security Threat Detected: ${threat.type}`,
|
|
573
|
+
description: this.formatThreatDescription(threat),
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
const channels = this.config.alerting.channels;
|
|
577
|
+
|
|
578
|
+
for (const channel of channels) {
|
|
579
|
+
try {
|
|
580
|
+
switch (channel) {
|
|
581
|
+
case 'log':
|
|
582
|
+
await this.sendLogAlert(alert);
|
|
583
|
+
break;
|
|
584
|
+
case 'webhook':
|
|
585
|
+
await this.sendWebhookAlert(alert);
|
|
586
|
+
break;
|
|
587
|
+
case 'email':
|
|
588
|
+
await this.sendEmailAlert(alert);
|
|
589
|
+
break;
|
|
590
|
+
default:
|
|
591
|
+
logger.warn('Unknown alert channel', { channel });
|
|
592
|
+
}
|
|
593
|
+
} catch (error) {
|
|
594
|
+
logger.error('Failed to send alert', {
|
|
595
|
+
channel,
|
|
596
|
+
alertId: alert.id,
|
|
597
|
+
error: error.message,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Block an entity (IP, user, etc.) for security reasons.
|
|
605
|
+
*
|
|
606
|
+
* @private
|
|
607
|
+
* @param {string} entity - Entity identifier to block
|
|
608
|
+
* @param {string} reason - Reason for blocking
|
|
609
|
+
*/
|
|
610
|
+
async blockEntity(entity, reason) {
|
|
611
|
+
this.blockedEntities.add(entity);
|
|
612
|
+
|
|
613
|
+
logger.security('Entity blocked', {
|
|
614
|
+
entity,
|
|
615
|
+
reason,
|
|
616
|
+
timestamp: Date.now(),
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Emit block event for external handlers
|
|
620
|
+
this.emit('entityBlocked', { entity, reason });
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Check if an entity is blocked.
|
|
625
|
+
*
|
|
626
|
+
* @param {string} entity - Entity identifier
|
|
627
|
+
* @returns {boolean} True if blocked
|
|
628
|
+
*/
|
|
629
|
+
isBlocked(entity) {
|
|
630
|
+
return this.blockedEntities.has(entity);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Get security monitoring health status.
|
|
635
|
+
*
|
|
636
|
+
* @returns {Object} Health status
|
|
637
|
+
*/
|
|
638
|
+
getHealth() {
|
|
639
|
+
return {
|
|
640
|
+
running: this.isRunning,
|
|
641
|
+
enabled: this.config.enabled,
|
|
642
|
+
activeIncidents: this.activeIncidents.size,
|
|
643
|
+
blockedEntities: this.blockedEntities.size,
|
|
644
|
+
eventsProcessed: this.securityEvents.size,
|
|
645
|
+
threatDetection: this.config.threatDetection.enabled,
|
|
646
|
+
anomalyDetection: this.config.anomalyDetection.enabled,
|
|
647
|
+
autoResponse: this.config.autoResponse.enabled,
|
|
648
|
+
lastCheck: this.lastCheckTime || null,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Get security metrics for monitoring dashboards.
|
|
654
|
+
*
|
|
655
|
+
* @returns {Object} Security metrics
|
|
656
|
+
*/
|
|
657
|
+
getMetrics() {
|
|
658
|
+
const now = Date.now();
|
|
659
|
+
const lastHour = now - 3600000; // 1 hour ago
|
|
660
|
+
|
|
661
|
+
const recentEvents = Array.from(this.securityEvents.values()).filter(
|
|
662
|
+
(e) => e.timestamp >= lastHour,
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
const eventsByType = recentEvents.reduce((acc, event) => {
|
|
666
|
+
acc[event.type] = (acc[event.type] || 0) + 1;
|
|
667
|
+
return acc;
|
|
668
|
+
}, {});
|
|
669
|
+
|
|
670
|
+
const eventsBySeverity = recentEvents.reduce((acc, event) => {
|
|
671
|
+
acc[event.severity] = (acc[event.severity] || 0) + 1;
|
|
672
|
+
return acc;
|
|
673
|
+
}, {});
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
totalEvents: this.securityEvents.size,
|
|
677
|
+
recentEvents: recentEvents.length,
|
|
678
|
+
eventsByType,
|
|
679
|
+
eventsBySeverity,
|
|
680
|
+
activeIncidents: this.activeIncidents.size,
|
|
681
|
+
blockedEntities: this.blockedEntities.size,
|
|
682
|
+
uptime: this.isRunning ? now - (this.startTime || now) : 0,
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Initialize storage for security events and state.
|
|
688
|
+
*
|
|
689
|
+
* @private
|
|
690
|
+
*/
|
|
691
|
+
async initializeStorage() {
|
|
692
|
+
if (this.config.storage.type === 'file') {
|
|
693
|
+
const storageDir = this.config.storage.location;
|
|
694
|
+
try {
|
|
695
|
+
await fs.mkdir(storageDir, { recursive: true });
|
|
696
|
+
logger.debug('Security storage initialized', { storageDir });
|
|
697
|
+
} catch (error) {
|
|
698
|
+
logger.error('Failed to initialize security storage', {
|
|
699
|
+
storageDir,
|
|
700
|
+
error: error.message,
|
|
701
|
+
});
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Start the monitoring loop for periodic checks.
|
|
709
|
+
*
|
|
710
|
+
* @private
|
|
711
|
+
*/
|
|
712
|
+
startMonitoringLoop() {
|
|
713
|
+
this.startTime = Date.now();
|
|
714
|
+
this.monitoringInterval = setInterval(async () => {
|
|
715
|
+
try {
|
|
716
|
+
this.lastCheckTime = Date.now();
|
|
717
|
+
await this.performPeriodicChecks();
|
|
718
|
+
} catch (error) {
|
|
719
|
+
logger.error('Error in monitoring loop', {
|
|
720
|
+
error: error.message,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}, this.config.checkInterval);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Perform periodic security checks and maintenance.
|
|
728
|
+
*
|
|
729
|
+
* @private
|
|
730
|
+
*/
|
|
731
|
+
async performPeriodicChecks() {
|
|
732
|
+
// Clean up old events
|
|
733
|
+
await this.cleanupOldEvents();
|
|
734
|
+
|
|
735
|
+
// Update metrics
|
|
736
|
+
this.updateSecurityMetrics();
|
|
737
|
+
|
|
738
|
+
// Check for resolved incidents
|
|
739
|
+
await this.checkIncidentStatus();
|
|
740
|
+
|
|
741
|
+
// Emit health check event
|
|
742
|
+
this.emit('healthCheck', this.getHealth());
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Clean up old security events based on retention policy.
|
|
747
|
+
*
|
|
748
|
+
* @private
|
|
749
|
+
*/
|
|
750
|
+
async cleanupOldEvents() {
|
|
751
|
+
const retentionMs = this.config.retentionDays * 24 * 60 * 60 * 1000;
|
|
752
|
+
const cutoffTime = Date.now() - retentionMs;
|
|
753
|
+
|
|
754
|
+
let cleanedCount = 0;
|
|
755
|
+
for (const [eventId, event] of this.securityEvents) {
|
|
756
|
+
if (event.timestamp < cutoffTime) {
|
|
757
|
+
this.securityEvents.delete(eventId);
|
|
758
|
+
cleanedCount++;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (cleanedCount > 0) {
|
|
763
|
+
logger.debug('Cleaned up old security events', { cleanedCount });
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Update security metrics for monitoring.
|
|
769
|
+
*
|
|
770
|
+
* @private
|
|
771
|
+
*/
|
|
772
|
+
updateSecurityMetrics() {
|
|
773
|
+
const metrics = this.getMetrics();
|
|
774
|
+
|
|
775
|
+
// Record telemetry metrics
|
|
776
|
+
this.telemetry.recordMetric('security_events_active', metrics.totalEvents);
|
|
777
|
+
this.telemetry.recordMetric(
|
|
778
|
+
'security_incidents_active',
|
|
779
|
+
metrics.activeIncidents,
|
|
780
|
+
);
|
|
781
|
+
this.telemetry.recordMetric(
|
|
782
|
+
'security_entities_blocked',
|
|
783
|
+
metrics.blockedEntities,
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
// Update threat metrics
|
|
787
|
+
this.threatMetrics.set('current', metrics);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Additional helper methods would be implemented here...
|
|
791
|
+
// (matchPattern, getBaseline, updateBaseline, etc.)
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Singleton security monitor instance.
|
|
796
|
+
*/
|
|
797
|
+
let securityMonitorInstance = null;
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Get or create the global security monitor instance.
|
|
801
|
+
*
|
|
802
|
+
* @param {Object} options - Configuration options
|
|
803
|
+
* @returns {SecurityMonitor} Security monitor instance
|
|
804
|
+
*/
|
|
805
|
+
function getSecurityMonitor(options = {}) {
|
|
806
|
+
if (!securityMonitorInstance) {
|
|
807
|
+
securityMonitorInstance = new SecurityMonitor(options);
|
|
808
|
+
}
|
|
809
|
+
return securityMonitorInstance;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Initialize the global security monitoring service.
|
|
814
|
+
*
|
|
815
|
+
* @param {Object} options - Configuration options
|
|
816
|
+
* @returns {Promise<SecurityMonitor>} Initialized security monitor
|
|
817
|
+
*/
|
|
818
|
+
async function initializeSecurityMonitoring(options = {}) {
|
|
819
|
+
const monitor = getSecurityMonitor(options);
|
|
820
|
+
await monitor.start();
|
|
821
|
+
return monitor;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
module.exports = {
|
|
825
|
+
SecurityMonitor,
|
|
826
|
+
getSecurityMonitor,
|
|
827
|
+
initializeSecurityMonitoring,
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
module.exports.default = module.exports;
|