devfortress-sdk 4.2.0 → 4.2.1

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.
@@ -0,0 +1,683 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InternalClosedLoopEngine = void 0;
4
+ class BoundedMap {
5
+ constructor(maxSize) {
6
+ this.map = new Map();
7
+ this.maxSize = maxSize;
8
+ }
9
+ get(key) {
10
+ const val = this.map.get(key);
11
+ if (val !== undefined) {
12
+ this.map.delete(key);
13
+ this.map.set(key, val);
14
+ }
15
+ return val;
16
+ }
17
+ set(key, value) {
18
+ if (this.map.has(key)) {
19
+ this.map.delete(key);
20
+ }
21
+ else if (this.map.size >= this.maxSize) {
22
+ const first = this.map.keys().next().value;
23
+ if (first !== undefined)
24
+ this.map.delete(first);
25
+ }
26
+ this.map.set(key, value);
27
+ }
28
+ has(key) {
29
+ return this.map.has(key);
30
+ }
31
+ delete(key) {
32
+ return this.map.delete(key);
33
+ }
34
+ get size() {
35
+ return this.map.size;
36
+ }
37
+ clear() {
38
+ this.map.clear();
39
+ }
40
+ values() {
41
+ return this.map.values();
42
+ }
43
+ keys() {
44
+ return this.map.keys();
45
+ }
46
+ }
47
+ const SQL_INJECTION_PATTERNS = [
48
+ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE)\b.*\b(FROM|INTO|TABLE|SET|WHERE)\b)/i,
49
+ /(\bOR\b\s+\d+\s*=\s*\d+)/i,
50
+ /(';\s*--)/,
51
+ /(\/\*.*\*\/)/,
52
+ /(\bEXEC\b|\bEXECUTE\b)\s*\(/i,
53
+ /(\bxp_\w+)/i,
54
+ ];
55
+ const XSS_PATTERNS = [
56
+ /<script[\s>]/i,
57
+ /javascript\s*:/i,
58
+ /on(load|error|click|mouseover|submit|focus|blur)\s*=/i,
59
+ /(<iframe|<object|<embed|<applet)/i,
60
+ /(document\.(cookie|write|location))/i,
61
+ /(window\.(location|open))/i,
62
+ ];
63
+ const PATH_TRAVERSAL_PATTERNS = [
64
+ /\.\.\//,
65
+ /\.\.%2[fF]/,
66
+ /\.\.\\$/,
67
+ /%2e%2e[%\/\\]/i,
68
+ /\/etc\/(passwd|shadow|hosts)/,
69
+ /\/proc\/self/,
70
+ /(\/\.env|\/\.git|\/\.ssh)/,
71
+ ];
72
+ const SCANNER_SIGNATURES = [
73
+ /sqlmap/i,
74
+ /nikto/i,
75
+ /nmap/i,
76
+ /dirbuster/i,
77
+ /gobuster/i,
78
+ /wpscan/i,
79
+ /burp\s*suite/i,
80
+ /acunetix/i,
81
+ /nessus/i,
82
+ /masscan/i,
83
+ /zgrab/i,
84
+ /nuclei/i,
85
+ ];
86
+ class InternalClosedLoopEngine {
87
+ constructor(config = {}) {
88
+ this.auditLog = [];
89
+ this.rules = [];
90
+ this.eventCounter = 0;
91
+ this.config = {
92
+ failMode: config.failMode ?? 'closed',
93
+ enableExternalRelay: config.enableExternalRelay ?? false,
94
+ maxBlockedEntries: config.maxBlockedEntries ?? 10000,
95
+ maxRateLimitEntries: config.maxRateLimitEntries ?? 50000,
96
+ maxAuditEntries: config.maxAuditEntries ?? 100000,
97
+ defaultBlockTtlSeconds: config.defaultBlockTtlSeconds ?? 3600,
98
+ tier2Scorer: config.tier2Scorer,
99
+ blockThreshold: config.blockThreshold ?? 85,
100
+ rateLimitThreshold: config.rateLimitThreshold ?? 60,
101
+ rateLimitMax: config.rateLimitMax ?? 100,
102
+ rateLimitWindowMs: config.rateLimitWindowMs ?? 60000,
103
+ debug: config.debug ?? false,
104
+ onAction: config.onAction,
105
+ };
106
+ this.blockedIPs = new BoundedMap(this.config.maxBlockedEntries);
107
+ this.blockedUsers = new BoundedMap(this.config.maxBlockedEntries);
108
+ this.blockedSessions = new BoundedMap(this.config.maxBlockedEntries);
109
+ this.quarantinedAgents = new BoundedMap(this.config.maxBlockedEntries);
110
+ this.rateLimiter = new BoundedMap(this.config.maxRateLimitEntries);
111
+ this.registerDefaultRules();
112
+ this.log('info', `Internal Closed-Loop Engine initialized (failMode=${this.config.failMode})`);
113
+ }
114
+ async evaluate(req) {
115
+ const startTime = performance.now();
116
+ const now = req.timestamp ?? Date.now();
117
+ const eventId = this.generateEventId();
118
+ try {
119
+ const preCheck = this.checkBlocked(req, now);
120
+ if (preCheck) {
121
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
122
+ const result = {
123
+ decision: preCheck.decision,
124
+ score: 100,
125
+ tier: 1,
126
+ matchedRules: [preCheck.reason],
127
+ evaluationTimeUs: elapsed,
128
+ actions: [],
129
+ eventId,
130
+ };
131
+ this.recordAudit(result, req);
132
+ return result;
133
+ }
134
+ const tier1Result = this.evaluateTier1(req);
135
+ if (tier1Result.decision !== 'allow') {
136
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
137
+ const actions = this.executeActions(tier1Result, req, eventId, now);
138
+ const result = {
139
+ ...tier1Result,
140
+ evaluationTimeUs: elapsed,
141
+ actions,
142
+ eventId,
143
+ };
144
+ this.recordAudit(result, req);
145
+ if (this.config.enableExternalRelay) {
146
+ this.relayToExternal(result, req).catch(() => { });
147
+ }
148
+ return result;
149
+ }
150
+ const rateLimited = this.checkRateLimit(req.ip, now);
151
+ if (rateLimited) {
152
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
153
+ const actions = this.executeActions({
154
+ decision: 'rate_limit',
155
+ score: 65,
156
+ tier: 1,
157
+ matchedRules: ['rate_limit_exceeded'],
158
+ }, req, eventId, now);
159
+ const result = {
160
+ decision: 'rate_limit',
161
+ score: 65,
162
+ tier: 1,
163
+ matchedRules: ['rate_limit_exceeded'],
164
+ evaluationTimeUs: elapsed,
165
+ actions,
166
+ eventId,
167
+ };
168
+ this.recordAudit(result, req);
169
+ return result;
170
+ }
171
+ this.recordRequest(req.ip, now);
172
+ if (this.config.tier2Scorer) {
173
+ const tier2Score = await this.config.tier2Scorer(req);
174
+ if (tier2Score >= this.config.blockThreshold) {
175
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
176
+ const tier2Decision = {
177
+ decision: 'block',
178
+ score: tier2Score,
179
+ tier: 2,
180
+ matchedRules: ['tier2_ml_score'],
181
+ };
182
+ const actions = this.executeActions(tier2Decision, req, eventId, now);
183
+ const result = {
184
+ ...tier2Decision,
185
+ evaluationTimeUs: elapsed,
186
+ actions,
187
+ eventId,
188
+ };
189
+ this.recordAudit(result, req);
190
+ if (this.config.enableExternalRelay) {
191
+ this.relayToExternal(result, req).catch(() => { });
192
+ }
193
+ return result;
194
+ }
195
+ else if (tier2Score >= this.config.rateLimitThreshold) {
196
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
197
+ const result = {
198
+ decision: 'rate_limit',
199
+ score: tier2Score,
200
+ tier: 2,
201
+ matchedRules: ['tier2_ml_elevated'],
202
+ evaluationTimeUs: elapsed,
203
+ actions: [],
204
+ eventId,
205
+ };
206
+ this.recordAudit(result, req);
207
+ return result;
208
+ }
209
+ }
210
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
211
+ const result = {
212
+ decision: 'allow',
213
+ score: 0,
214
+ tier: 1,
215
+ matchedRules: [],
216
+ evaluationTimeUs: elapsed,
217
+ actions: [],
218
+ eventId,
219
+ };
220
+ return result;
221
+ }
222
+ catch (error) {
223
+ const elapsed = Math.round((performance.now() - startTime) * 1000);
224
+ this.log('error', `Evaluation error: ${error instanceof Error ? error.message : 'Unknown'}`);
225
+ if (this.config.failMode === 'closed') {
226
+ return {
227
+ decision: 'block',
228
+ score: 100,
229
+ tier: 1,
230
+ matchedRules: ['fail_closed_error'],
231
+ evaluationTimeUs: elapsed,
232
+ actions: [],
233
+ eventId,
234
+ };
235
+ }
236
+ return {
237
+ decision: 'allow',
238
+ score: 0,
239
+ tier: 1,
240
+ matchedRules: ['fail_open_error'],
241
+ evaluationTimeUs: elapsed,
242
+ actions: [],
243
+ eventId,
244
+ };
245
+ }
246
+ }
247
+ blockIP(ip, reason, ttlSeconds) {
248
+ const ttl = ttlSeconds ?? this.config.defaultBlockTtlSeconds;
249
+ this.blockedIPs.set(ip, {
250
+ expiresAt: Date.now() + ttl * 1000,
251
+ reason,
252
+ });
253
+ this.log('info', `IP blocked: ${ip} — ${reason} (TTL: ${ttl}s)`);
254
+ }
255
+ unblockIP(ip) {
256
+ const existed = this.blockedIPs.has(ip);
257
+ this.blockedIPs.delete(ip);
258
+ if (existed)
259
+ this.log('info', `IP unblocked: ${ip}`);
260
+ return existed;
261
+ }
262
+ blockUser(userId, reason, ttlSeconds) {
263
+ const ttl = ttlSeconds ?? this.config.defaultBlockTtlSeconds;
264
+ this.blockedUsers.set(userId, {
265
+ expiresAt: Date.now() + ttl * 1000,
266
+ reason,
267
+ });
268
+ this.log('info', `User blocked: ${userId} — ${reason}`);
269
+ }
270
+ blockSession(sessionId, reason, ttlSeconds) {
271
+ const ttl = ttlSeconds ?? this.config.defaultBlockTtlSeconds;
272
+ this.blockedSessions.set(sessionId, {
273
+ expiresAt: Date.now() + ttl * 1000,
274
+ reason,
275
+ });
276
+ this.log('info', `Session blocked: ${sessionId} — ${reason}`);
277
+ }
278
+ quarantineAgent(agentId, reason) {
279
+ this.quarantinedAgents.set(agentId, {
280
+ reason,
281
+ timestamp: Date.now(),
282
+ });
283
+ this.log('info', `Agent quarantined: ${agentId} — ${reason}`);
284
+ }
285
+ unquarantineAgent(agentId) {
286
+ const existed = this.quarantinedAgents.has(agentId);
287
+ this.quarantinedAgents.delete(agentId);
288
+ if (existed)
289
+ this.log('info', `Agent unquarantined: ${agentId}`);
290
+ return existed;
291
+ }
292
+ isIPBlocked(ip) {
293
+ const entry = this.blockedIPs.get(ip);
294
+ if (!entry)
295
+ return false;
296
+ if (Date.now() > entry.expiresAt) {
297
+ this.blockedIPs.delete(ip);
298
+ return false;
299
+ }
300
+ return true;
301
+ }
302
+ isAgentQuarantined(agentId) {
303
+ return this.quarantinedAgents.has(agentId);
304
+ }
305
+ addRule(rule) {
306
+ this.rules.push(rule);
307
+ this.log('debug', `Rule added: ${rule.name}`);
308
+ }
309
+ removeRule(name) {
310
+ const idx = this.rules.findIndex(r => r.name === name);
311
+ if (idx >= 0) {
312
+ this.rules.splice(idx, 1);
313
+ this.log('debug', `Rule removed: ${name}`);
314
+ return true;
315
+ }
316
+ return false;
317
+ }
318
+ getRules() {
319
+ return this.rules.map(({ name, description, severity, score, decision, enabled }) => ({
320
+ name,
321
+ description,
322
+ severity,
323
+ score,
324
+ decision,
325
+ enabled,
326
+ }));
327
+ }
328
+ getAuditLog(limit = 100) {
329
+ return this.auditLog.slice(-limit);
330
+ }
331
+ getStats() {
332
+ return {
333
+ blockedIPs: this.blockedIPs.size,
334
+ blockedUsers: this.blockedUsers.size,
335
+ blockedSessions: this.blockedSessions.size,
336
+ quarantinedAgents: this.quarantinedAgents.size,
337
+ rateLimitEntries: this.rateLimiter.size,
338
+ auditLogSize: this.auditLog.length,
339
+ rulesCount: this.rules.length,
340
+ failMode: this.config.failMode,
341
+ };
342
+ }
343
+ importBlockedIPs(entries) {
344
+ let count = 0;
345
+ for (const entry of entries) {
346
+ this.blockIP(entry.ip, entry.reason, entry.ttlSeconds);
347
+ count++;
348
+ }
349
+ return count;
350
+ }
351
+ _reset() {
352
+ this.blockedIPs.clear();
353
+ this.blockedUsers.clear();
354
+ this.blockedSessions.clear();
355
+ this.quarantinedAgents.clear();
356
+ this.rateLimiter.clear();
357
+ this.auditLog = [];
358
+ this.eventCounter = 0;
359
+ }
360
+ checkBlocked(req, now) {
361
+ const ipBlock = this.blockedIPs.get(req.ip);
362
+ if (ipBlock) {
363
+ if (now > ipBlock.expiresAt) {
364
+ this.blockedIPs.delete(req.ip);
365
+ }
366
+ else {
367
+ return { decision: 'block', reason: `ip_blocked:${ipBlock.reason}` };
368
+ }
369
+ }
370
+ if (req.userId) {
371
+ const userBlock = this.blockedUsers.get(req.userId);
372
+ if (userBlock) {
373
+ if (now > userBlock.expiresAt) {
374
+ this.blockedUsers.delete(req.userId);
375
+ }
376
+ else {
377
+ return {
378
+ decision: 'block',
379
+ reason: `user_blocked:${userBlock.reason}`,
380
+ };
381
+ }
382
+ }
383
+ }
384
+ if (req.sessionId) {
385
+ const sessionBlock = this.blockedSessions.get(req.sessionId);
386
+ if (sessionBlock) {
387
+ if (now > sessionBlock.expiresAt) {
388
+ this.blockedSessions.delete(req.sessionId);
389
+ }
390
+ else {
391
+ return {
392
+ decision: 'block',
393
+ reason: `session_blocked:${sessionBlock.reason}`,
394
+ };
395
+ }
396
+ }
397
+ }
398
+ if (req.agentId) {
399
+ if (this.quarantinedAgents.has(req.agentId)) {
400
+ return {
401
+ decision: 'quarantine',
402
+ reason: `agent_quarantined:${req.agentId}`,
403
+ };
404
+ }
405
+ }
406
+ return null;
407
+ }
408
+ evaluateTier1(req) {
409
+ const matchedRules = [];
410
+ let maxScore = 0;
411
+ let decision = 'allow';
412
+ for (const rule of this.rules) {
413
+ if (!rule.enabled)
414
+ continue;
415
+ try {
416
+ if (rule.match(req)) {
417
+ matchedRules.push(rule.name);
418
+ if (rule.score > maxScore) {
419
+ maxScore = rule.score;
420
+ decision = rule.decision;
421
+ }
422
+ }
423
+ }
424
+ catch {
425
+ this.log('error', `Rule ${rule.name} threw an error`);
426
+ }
427
+ }
428
+ return {
429
+ decision,
430
+ score: maxScore,
431
+ tier: 1,
432
+ matchedRules,
433
+ };
434
+ }
435
+ checkRateLimit(ip, now) {
436
+ const entry = this.rateLimiter.get(ip);
437
+ if (!entry)
438
+ return false;
439
+ const windowStart = now - this.config.rateLimitWindowMs;
440
+ entry.timestamps = entry.timestamps.filter(t => t > windowStart);
441
+ if (entry.timestamps.length >= this.config.rateLimitMax) {
442
+ entry.blocked = true;
443
+ return true;
444
+ }
445
+ return false;
446
+ }
447
+ recordRequest(ip, now) {
448
+ let entry = this.rateLimiter.get(ip);
449
+ if (!entry) {
450
+ entry = { timestamps: [], blocked: false };
451
+ }
452
+ entry.timestamps.push(now);
453
+ this.rateLimiter.set(ip, entry);
454
+ }
455
+ executeActions(result, req, eventId, now) {
456
+ const actions = [];
457
+ const ttl = this.config.defaultBlockTtlSeconds;
458
+ if (result.decision === 'block' || result.decision === 'quarantine') {
459
+ this.blockedIPs.set(req.ip, {
460
+ expiresAt: now + ttl * 1000,
461
+ reason: result.matchedRules.join(', '),
462
+ });
463
+ actions.push({
464
+ type: 'block_ip',
465
+ target: req.ip,
466
+ reason: result.matchedRules.join(', '),
467
+ ttlSeconds: ttl,
468
+ timestamp: now,
469
+ eventId,
470
+ });
471
+ if (req.sessionId) {
472
+ this.blockedSessions.set(req.sessionId, {
473
+ expiresAt: now + ttl * 1000,
474
+ reason: result.matchedRules.join(', '),
475
+ });
476
+ actions.push({
477
+ type: 'block_session',
478
+ target: req.sessionId,
479
+ reason: result.matchedRules.join(', '),
480
+ ttlSeconds: ttl,
481
+ timestamp: now,
482
+ eventId,
483
+ });
484
+ }
485
+ if (req.agentId) {
486
+ this.quarantinedAgents.set(req.agentId, {
487
+ reason: result.matchedRules.join(', '),
488
+ timestamp: now,
489
+ });
490
+ actions.push({
491
+ type: 'quarantine_agent',
492
+ target: req.agentId,
493
+ reason: result.matchedRules.join(', '),
494
+ ttlSeconds: 0,
495
+ timestamp: now,
496
+ eventId,
497
+ });
498
+ }
499
+ }
500
+ if (this.config.onAction) {
501
+ for (const action of actions) {
502
+ try {
503
+ const maybePromise = this.config.onAction(action);
504
+ if (maybePromise &&
505
+ typeof maybePromise.catch === 'function') {
506
+ maybePromise.catch(() => { });
507
+ }
508
+ }
509
+ catch {
510
+ this.log('error', `onAction callback error for ${action.type}`);
511
+ }
512
+ }
513
+ }
514
+ return actions;
515
+ }
516
+ recordAudit(result, req) {
517
+ if (this.auditLog.length >= this.config.maxAuditEntries) {
518
+ this.auditLog.splice(0, Math.floor(this.config.maxAuditEntries * 0.1));
519
+ }
520
+ this.auditLog.push({
521
+ eventId: result.eventId,
522
+ timestamp: req.timestamp ?? Date.now(),
523
+ ip: req.ip,
524
+ decision: result.decision,
525
+ score: result.score,
526
+ tier: result.tier,
527
+ matchedRules: result.matchedRules,
528
+ userId: req.userId,
529
+ agentId: req.agentId,
530
+ path: req.path,
531
+ method: req.method,
532
+ });
533
+ }
534
+ async relayToExternal(result, req) {
535
+ this.log('debug', `Tier 3 relay: ${result.eventId} → external platform`);
536
+ }
537
+ generateEventId() {
538
+ this.eventCounter++;
539
+ const ts = Date.now().toString(36);
540
+ const counter = this.eventCounter.toString(36).padStart(4, '0');
541
+ const rand = Math.random().toString(36).substring(2, 6);
542
+ return `icl_${ts}_${counter}_${rand}`;
543
+ }
544
+ registerDefaultRules() {
545
+ this.rules.push({
546
+ name: 'sql_injection',
547
+ description: 'Detects SQL injection patterns in path, query, and body',
548
+ severity: 'critical',
549
+ score: 95,
550
+ decision: 'block',
551
+ enabled: true,
552
+ match: req => {
553
+ const targets = [req.path, req.query, req.body].filter(Boolean);
554
+ return targets.some(t => SQL_INJECTION_PATTERNS.some(p => p.test(t)));
555
+ },
556
+ });
557
+ this.rules.push({
558
+ name: 'xss_attempt',
559
+ description: 'Detects cross-site scripting patterns',
560
+ severity: 'high',
561
+ score: 90,
562
+ decision: 'block',
563
+ enabled: true,
564
+ match: req => {
565
+ const targets = [req.path, req.query, req.body].filter(Boolean);
566
+ return targets.some(t => XSS_PATTERNS.some(p => p.test(t)));
567
+ },
568
+ });
569
+ this.rules.push({
570
+ name: 'path_traversal',
571
+ description: 'Detects directory traversal attempts',
572
+ severity: 'high',
573
+ score: 90,
574
+ decision: 'block',
575
+ enabled: true,
576
+ match: req => {
577
+ const targets = [req.path, req.query].filter(Boolean);
578
+ return targets.some(t => PATH_TRAVERSAL_PATTERNS.some(p => p.test(t)));
579
+ },
580
+ });
581
+ this.rules.push({
582
+ name: 'scanner_detected',
583
+ description: 'Detects known vulnerability scanner user agents',
584
+ severity: 'medium',
585
+ score: 75,
586
+ decision: 'block',
587
+ enabled: true,
588
+ match: req => {
589
+ if (!req.userAgent)
590
+ return false;
591
+ return SCANNER_SIGNATURES.some(p => p.test(req.userAgent));
592
+ },
593
+ });
594
+ this.rules.push({
595
+ name: 'sensitive_file_access',
596
+ description: 'Detects access to sensitive files (.env, .git, wp-admin, etc.)',
597
+ severity: 'high',
598
+ score: 85,
599
+ decision: 'block',
600
+ enabled: true,
601
+ match: req => {
602
+ const sensitivePatterns = [
603
+ /\/\.env/,
604
+ /\/\.git/,
605
+ /\/\.ssh/,
606
+ /\/wp-admin/,
607
+ /\/wp-login/,
608
+ /\/phpmyadmin/i,
609
+ /\/admin\.php/,
610
+ /\/\.htaccess/,
611
+ /\/\.htpasswd/,
612
+ /\/server-status/,
613
+ /\/actuator/,
614
+ /\/debug\//,
615
+ ];
616
+ return sensitivePatterns.some(p => p.test(req.path));
617
+ },
618
+ });
619
+ this.rules.push({
620
+ name: 'credential_stuffing_pattern',
621
+ description: 'Detects credential stuffing via POST to auth endpoints',
622
+ severity: 'high',
623
+ score: 80,
624
+ decision: 'rate_limit',
625
+ enabled: true,
626
+ match: req => {
627
+ if (req.method !== 'POST')
628
+ return false;
629
+ const authPaths = [
630
+ '/login',
631
+ '/signin',
632
+ '/auth',
633
+ '/api/auth',
634
+ '/api/login',
635
+ '/oauth/token',
636
+ ];
637
+ return authPaths.some(p => req.path.toLowerCase().includes(p));
638
+ },
639
+ });
640
+ this.rules.push({
641
+ name: 'agent_unsanctioned_tool',
642
+ description: 'Detects agent attempting to use dangerous tools',
643
+ severity: 'critical',
644
+ score: 95,
645
+ decision: 'quarantine',
646
+ enabled: true,
647
+ match: req => {
648
+ if (!req.agentId || !req.toolName)
649
+ return false;
650
+ const dangerousTools = [
651
+ 'shell_exec',
652
+ 'eval',
653
+ 'exec',
654
+ 'rm',
655
+ 'sudo',
656
+ 'chmod',
657
+ 'kill',
658
+ ];
659
+ return dangerousTools.includes(req.toolName.toLowerCase());
660
+ },
661
+ });
662
+ }
663
+ log(level, message) {
664
+ if (!this.config.debug && level === 'debug')
665
+ return;
666
+ const prefix = '[DevFortress ICL]';
667
+ switch (level) {
668
+ case 'error':
669
+ console.error(`${prefix} ${message}`);
670
+ break;
671
+ case 'warn':
672
+ console.warn(`${prefix} ${message}`);
673
+ break;
674
+ case 'info':
675
+ case 'debug':
676
+ if (this.config.debug) {
677
+ console.log(`${prefix} ${message}`);
678
+ }
679
+ break;
680
+ }
681
+ }
682
+ }
683
+ exports.InternalClosedLoopEngine = InternalClosedLoopEngine;
@@ -1,9 +1,3 @@
1
- /**
2
- * Express.js Middleware for DevFortress Surveillance
3
- */
4
1
  import { Request, Response, NextFunction } from 'express';
5
2
  import type { DevFortressMiddlewareOptions } from '../types';
6
- /**
7
- * Create Express middleware for automatic threat monitoring
8
- */
9
3
  export declare function devfortressMiddleware(options: DevFortressMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;