@guardcore/core 1.0.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.
@@ -0,0 +1,1099 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-DGUM43GV.js";
4
+
5
+ // src/detection-engine/preprocessor.ts
6
+ var ATTACK_INDICATORS = [
7
+ /<script/i,
8
+ /javascript:/i,
9
+ /on\w+=/i,
10
+ /SELECT\s+.{0,50}?\s+FROM/i,
11
+ /UNION\s+SELECT/i,
12
+ /\.\.\//,
13
+ /eval\s*\(/i,
14
+ /exec\s*\(/i,
15
+ /system\s*\(/i,
16
+ /<\?php/i,
17
+ /<%%/,
18
+ /\{\{/,
19
+ /\{%/,
20
+ /<iframe/i,
21
+ /<object/i,
22
+ /<embed/i,
23
+ /onerror\s*=/i,
24
+ /onload\s*=/i,
25
+ /\$\{/,
26
+ /\\x[0-9a-fA-F]{2}/,
27
+ /%[0-9a-fA-F]{2}/
28
+ ];
29
+ var LOOKALIKES = {
30
+ "\u2044": "/",
31
+ "\uFF0F": "/",
32
+ "\u29F8": "/",
33
+ "\u0130": "I",
34
+ "\u0131": "i",
35
+ "\u200B": "",
36
+ "\u200C": "",
37
+ "\u200D": "",
38
+ "\uFEFF": "",
39
+ "\xAD": "",
40
+ "\u034F": "",
41
+ "\u180E": "",
42
+ "\u2028": "\n",
43
+ "\u2029": "\n",
44
+ "\uE000": "",
45
+ "\uFFF0": "",
46
+ "\u01C0": "|",
47
+ "\u037E": ";",
48
+ "\u2215": "/",
49
+ "\u2216": "\\",
50
+ "\uFF1C": "<",
51
+ "\uFF1E": ">"
52
+ };
53
+ var CONTROL_CHARS_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
54
+ var ContentPreprocessor = class {
55
+ maxContentLength;
56
+ preserveAttackPatterns;
57
+ constructor(maxContentLength = 1e4, preserveAttackPatterns = true) {
58
+ this.maxContentLength = maxContentLength;
59
+ this.preserveAttackPatterns = preserveAttackPatterns;
60
+ }
61
+ normalizeUnicode(content) {
62
+ let normalized = content.normalize("NFKC");
63
+ for (const [char, replacement] of Object.entries(LOOKALIKES)) {
64
+ normalized = normalized.replaceAll(char, replacement);
65
+ }
66
+ return normalized;
67
+ }
68
+ removeNullBytes(content) {
69
+ return content.replace(/\x00/g, "").replace(CONTROL_CHARS_RE, "");
70
+ }
71
+ removeExcessiveWhitespace(content) {
72
+ return content.replace(/\s+/g, " ").trim();
73
+ }
74
+ decodeCommonEncodings(content) {
75
+ const maxIterations = 3;
76
+ let current = content;
77
+ for (let i = 0; i < maxIterations; i++) {
78
+ const original = current;
79
+ try {
80
+ const decoded = decodeURIComponent(current);
81
+ if (decoded !== current) current = decoded;
82
+ } catch {
83
+ }
84
+ try {
85
+ current = this.decodeHtmlEntities(current);
86
+ } catch {
87
+ }
88
+ if (current === original) break;
89
+ }
90
+ return current;
91
+ }
92
+ decodeHtmlEntities(content) {
93
+ const entityMap = {
94
+ "&amp;": "&",
95
+ "&lt;": "<",
96
+ "&gt;": ">",
97
+ "&quot;": '"',
98
+ "&#39;": "'",
99
+ "&apos;": "'",
100
+ "&#x27;": "'",
101
+ "&#x2F;": "/",
102
+ "&#47;": "/",
103
+ "&nbsp;": " "
104
+ };
105
+ let result = content;
106
+ for (const [entity, char] of Object.entries(entityMap)) {
107
+ result = result.replaceAll(entity, char);
108
+ }
109
+ result = result.replace(
110
+ /&#x([0-9a-fA-F]+);/g,
111
+ (_, hex) => String.fromCharCode(parseInt(hex, 16))
112
+ );
113
+ result = result.replace(
114
+ /&#(\d+);/g,
115
+ (_, dec) => String.fromCharCode(parseInt(dec, 10))
116
+ );
117
+ return result;
118
+ }
119
+ extractAttackRegions(content) {
120
+ const maxRegions = Math.min(100, Math.floor(this.maxContentLength / 100));
121
+ const regions = [];
122
+ for (const indicator of ATTACK_INDICATORS) {
123
+ const regex = new RegExp(indicator.source, indicator.flags + "g");
124
+ let match;
125
+ while ((match = regex.exec(content)) !== null) {
126
+ if (regions.length >= maxRegions) break;
127
+ const start = Math.max(0, match.index - 100);
128
+ const end = Math.min(content.length, match.index + match[0].length + 100);
129
+ regions.push([start, end]);
130
+ }
131
+ if (regions.length >= maxRegions) break;
132
+ }
133
+ if (regions.length === 0) return [];
134
+ regions.sort((a, b) => a[0] - b[0]);
135
+ const merged = [regions[0]];
136
+ for (let i = 1; i < regions.length; i++) {
137
+ const [start, end] = regions[i];
138
+ const last = merged[merged.length - 1];
139
+ if (start <= last[1]) {
140
+ last[1] = Math.max(last[1], end);
141
+ } else {
142
+ merged.push([start, end]);
143
+ }
144
+ }
145
+ return merged.slice(0, maxRegions);
146
+ }
147
+ truncateSafely(content) {
148
+ if (content.length <= this.maxContentLength) return content;
149
+ if (!this.preserveAttackPatterns) return content.slice(0, this.maxContentLength);
150
+ const attackRegions = this.extractAttackRegions(content);
151
+ if (attackRegions.length === 0) return content.slice(0, this.maxContentLength);
152
+ const attackLength = attackRegions.reduce((sum, [s, e]) => sum + (e - s), 0);
153
+ if (attackLength >= this.maxContentLength) {
154
+ let result = "";
155
+ let remaining2 = this.maxContentLength;
156
+ for (const [start, end] of attackRegions) {
157
+ const chunkLen = Math.min(end - start, remaining2);
158
+ result += content.slice(start, start + chunkLen);
159
+ remaining2 -= chunkLen;
160
+ if (remaining2 <= 0) break;
161
+ }
162
+ return result;
163
+ }
164
+ const parts = [];
165
+ for (const [start, end] of attackRegions) {
166
+ parts.push(content.slice(start, end));
167
+ }
168
+ let remaining = this.maxContentLength - attackLength;
169
+ let lastEnd = 0;
170
+ const contextParts = [];
171
+ for (const [start, end] of attackRegions) {
172
+ if (lastEnd < start && remaining > 0) {
173
+ const chunkLen = Math.min(start - lastEnd, remaining);
174
+ contextParts.push(content.slice(lastEnd, lastEnd + chunkLen));
175
+ remaining -= chunkLen;
176
+ }
177
+ lastEnd = end;
178
+ }
179
+ return [...contextParts, ...parts].join("");
180
+ }
181
+ async preprocess(content) {
182
+ if (!content) return "";
183
+ let result = this.normalizeUnicode(content);
184
+ result = this.decodeCommonEncodings(result);
185
+ result = this.removeNullBytes(result);
186
+ result = this.removeExcessiveWhitespace(result);
187
+ result = this.truncateSafely(result);
188
+ return result;
189
+ }
190
+ async preprocessBatch(contents) {
191
+ return Promise.all(contents.map((c) => this.preprocess(c)));
192
+ }
193
+ };
194
+
195
+ // src/detection-engine/compiler.ts
196
+ var DANGEROUS_PATTERNS = [
197
+ /\(\.\*\)\+/,
198
+ /\(\.\+\)\+/,
199
+ /\([^)]*\*\)\+/,
200
+ /\([^)]*\+\)\+/,
201
+ /(?:\.\*){2,}/,
202
+ /(?:\.\+){2,}/
203
+ ];
204
+ var DEFAULT_TEST_STRINGS = [
205
+ "a".repeat(10),
206
+ "a".repeat(100),
207
+ "a".repeat(1e3),
208
+ "x".repeat(50) + "y".repeat(50),
209
+ "<".repeat(100) + ">".repeat(100)
210
+ ];
211
+ var RE2Ctor = null;
212
+ async function loadRE2() {
213
+ if (RE2Ctor) return RE2Ctor;
214
+ try {
215
+ const mod = await import("re2-wasm");
216
+ RE2Ctor = mod.RE2 ?? mod.default?.RE2 ?? mod.default;
217
+ return RE2Ctor;
218
+ } catch {
219
+ return null;
220
+ }
221
+ }
222
+ var PatternCompiler = class {
223
+ constructor(defaultTimeoutMs = 2e3, maxCacheSize = 1e3) {
224
+ this.defaultTimeoutMs = defaultTimeoutMs;
225
+ this.maxCacheSize = maxCacheSize;
226
+ this.maxCacheSize = Math.min(maxCacheSize, 5e3);
227
+ }
228
+ cache = /* @__PURE__ */ new Map();
229
+ cacheOrder = [];
230
+ re2Available = null;
231
+ async ensureRE2() {
232
+ if (this.re2Available !== null) return this.re2Available;
233
+ const ctor = await loadRE2();
234
+ this.re2Available = ctor !== null;
235
+ return this.re2Available;
236
+ }
237
+ async compile(pattern, flags = "gi") {
238
+ const key = `${pattern}:${flags}`;
239
+ if (this.cache.has(key)) {
240
+ const idx = this.cacheOrder.indexOf(key);
241
+ if (idx !== -1) {
242
+ this.cacheOrder.splice(idx, 1);
243
+ this.cacheOrder.push(key);
244
+ }
245
+ return this.cache.get(key);
246
+ }
247
+ if (this.cache.size >= this.maxCacheSize) {
248
+ const oldest = this.cacheOrder.shift();
249
+ if (oldest) this.cache.delete(oldest);
250
+ }
251
+ let compiled;
252
+ if (await this.ensureRE2()) {
253
+ try {
254
+ compiled = new RE2Ctor(pattern, flags);
255
+ } catch {
256
+ compiled = new RegExp(pattern, flags);
257
+ }
258
+ } else {
259
+ compiled = new RegExp(pattern, flags);
260
+ }
261
+ this.cache.set(key, compiled);
262
+ this.cacheOrder.push(key);
263
+ return compiled;
264
+ }
265
+ compileSync(pattern, flags = "gi") {
266
+ return new RegExp(pattern, flags);
267
+ }
268
+ async safeMatch(pattern, content, timeoutMs) {
269
+ try {
270
+ const compiled = await this.compile(pattern);
271
+ return compiled.exec(content);
272
+ } catch {
273
+ return this.fallbackMatch(pattern, content, timeoutMs ?? this.defaultTimeoutMs);
274
+ }
275
+ }
276
+ async fallbackMatch(pattern, content, timeoutMs) {
277
+ try {
278
+ const { Worker } = await import("worker_threads");
279
+ return new Promise((resolve) => {
280
+ const workerCode = `
281
+ const { parentPort, workerData } = require('node:worker_threads');
282
+ try {
283
+ const re = new RegExp(workerData.pattern, workerData.flags);
284
+ const result = re.exec(workerData.content);
285
+ parentPort.postMessage({ result });
286
+ } catch (e) {
287
+ parentPort.postMessage({ result: null });
288
+ }
289
+ `;
290
+ const worker = new Worker(workerCode, {
291
+ eval: true,
292
+ workerData: { pattern, content, flags: "gi" }
293
+ });
294
+ const timer = setTimeout(() => {
295
+ worker.terminate();
296
+ resolve(null);
297
+ }, timeoutMs);
298
+ worker.on("message", (msg) => {
299
+ clearTimeout(timer);
300
+ worker.terminate();
301
+ resolve(msg.result);
302
+ });
303
+ worker.on("error", () => {
304
+ clearTimeout(timer);
305
+ worker.terminate();
306
+ resolve(null);
307
+ });
308
+ });
309
+ } catch {
310
+ try {
311
+ const re = new RegExp(pattern, "gi");
312
+ return re.exec(content);
313
+ } catch {
314
+ return null;
315
+ }
316
+ }
317
+ }
318
+ validatePatternSafety(pattern, testStrings) {
319
+ for (const dangerous of DANGEROUS_PATTERNS) {
320
+ if (dangerous.test(pattern)) {
321
+ return [false, `Pattern contains dangerous construct: ${dangerous.source}`];
322
+ }
323
+ }
324
+ const strings = testStrings ?? DEFAULT_TEST_STRINGS;
325
+ try {
326
+ const compiled = this.compileSync(pattern);
327
+ for (const testStr of strings) {
328
+ const start = performance.now();
329
+ compiled.exec(testStr);
330
+ const elapsed = performance.now() - start;
331
+ if (elapsed > 50) {
332
+ return [false, `Pattern timed out on test string of length ${testStr.length}`];
333
+ }
334
+ }
335
+ } catch (e) {
336
+ return [false, `Pattern validation failed: ${String(e)}`];
337
+ }
338
+ return [true, "Pattern appears safe"];
339
+ }
340
+ async batchCompile(patterns, validate = true) {
341
+ const compiled = /* @__PURE__ */ new Map();
342
+ for (const pattern of patterns) {
343
+ if (validate) {
344
+ const [isSafe] = this.validatePatternSafety(pattern);
345
+ if (!isSafe) continue;
346
+ }
347
+ try {
348
+ compiled.set(pattern, await this.compile(pattern));
349
+ } catch {
350
+ continue;
351
+ }
352
+ }
353
+ return compiled;
354
+ }
355
+ async clearCache() {
356
+ this.cache.clear();
357
+ this.cacheOrder = [];
358
+ }
359
+ };
360
+
361
+ // src/detection-engine/monitor.ts
362
+ var MAX_RECENT_TIMES = 100;
363
+ var MAX_PATTERN_LENGTH = 100;
364
+ var MIN_SAMPLES_FOR_STATS = 10;
365
+ function mean(values) {
366
+ if (values.length === 0) return 0;
367
+ return values.reduce((a, b) => a + b, 0) / values.length;
368
+ }
369
+ function stdev(values) {
370
+ if (values.length <= 1) return 0;
371
+ const avg = mean(values);
372
+ const squareDiffs = values.map((v) => (v - avg) ** 2);
373
+ return Math.sqrt(squareDiffs.reduce((a, b) => a + b, 0) / (values.length - 1));
374
+ }
375
+ function truncatePattern(pattern) {
376
+ return pattern.length > 50 ? pattern.slice(0, 50) + "..." : pattern;
377
+ }
378
+ function patternHash(pattern) {
379
+ let hash = 0;
380
+ for (let i = 0; i < pattern.length; i++) {
381
+ hash = (hash << 5) - hash + pattern.charCodeAt(i);
382
+ hash |= 0;
383
+ }
384
+ return String(hash).slice(0, 8);
385
+ }
386
+ var PerformanceMonitor = class {
387
+ anomalyThreshold;
388
+ slowPatternThreshold;
389
+ historySize;
390
+ maxTrackedPatterns;
391
+ patternStats = /* @__PURE__ */ new Map();
392
+ recentMetrics = [];
393
+ anomalyCallbacks = [];
394
+ constructor(anomalyThreshold = 3, slowPatternThreshold = 0.1, historySize = 1e3, maxTrackedPatterns = 1e3) {
395
+ this.anomalyThreshold = Math.max(1, Math.min(10, anomalyThreshold));
396
+ this.slowPatternThreshold = Math.max(0.01, Math.min(10, slowPatternThreshold));
397
+ this.historySize = Math.max(100, Math.min(1e4, historySize));
398
+ this.maxTrackedPatterns = Math.max(100, Math.min(5e3, maxTrackedPatterns));
399
+ }
400
+ async recordMetric(pattern, executionTime, contentLength, matched, timeout = false, agentHandler = null, correlationId = null) {
401
+ let truncatedPattern = pattern;
402
+ if (pattern.length > MAX_PATTERN_LENGTH) {
403
+ truncatedPattern = pattern.slice(0, MAX_PATTERN_LENGTH) + "...[truncated]";
404
+ }
405
+ executionTime = Math.max(0, executionTime);
406
+ contentLength = Math.max(0, contentLength);
407
+ const metric = {
408
+ pattern: truncatedPattern,
409
+ executionTime,
410
+ contentLength,
411
+ timestamp: /* @__PURE__ */ new Date(),
412
+ matched,
413
+ timeout
414
+ };
415
+ this.recentMetrics.push(metric);
416
+ if (this.recentMetrics.length > this.historySize) {
417
+ this.recentMetrics.shift();
418
+ }
419
+ if (!this.patternStats.has(truncatedPattern)) {
420
+ if (this.patternStats.size >= this.maxTrackedPatterns) {
421
+ const oldestKey = this.patternStats.keys().next().value;
422
+ this.patternStats.delete(oldestKey);
423
+ }
424
+ this.patternStats.set(truncatedPattern, {
425
+ pattern: truncatedPattern,
426
+ totalExecutions: 0,
427
+ totalMatches: 0,
428
+ totalTimeouts: 0,
429
+ avgExecutionTime: 0,
430
+ maxExecutionTime: 0,
431
+ minExecutionTime: Infinity,
432
+ recentTimes: []
433
+ });
434
+ }
435
+ const stats = this.patternStats.get(truncatedPattern);
436
+ stats.totalExecutions++;
437
+ if (matched) stats.totalMatches++;
438
+ if (timeout) stats.totalTimeouts++;
439
+ if (!timeout) {
440
+ stats.recentTimes.push(executionTime);
441
+ if (stats.recentTimes.length > MAX_RECENT_TIMES) {
442
+ stats.recentTimes.shift();
443
+ }
444
+ stats.maxExecutionTime = Math.max(stats.maxExecutionTime, executionTime);
445
+ stats.minExecutionTime = Math.min(stats.minExecutionTime, executionTime);
446
+ if (stats.recentTimes.length > 0) {
447
+ stats.avgExecutionTime = mean(stats.recentTimes);
448
+ }
449
+ }
450
+ await this.checkAnomalies(metric, agentHandler, correlationId);
451
+ }
452
+ async checkAnomalies(metric, agentHandler, correlationId) {
453
+ const anomalies = [];
454
+ if (metric.timeout) {
455
+ anomalies.push({
456
+ type: "timeout",
457
+ pattern: metric.pattern,
458
+ contentLength: metric.contentLength
459
+ });
460
+ } else if (metric.executionTime > this.slowPatternThreshold) {
461
+ anomalies.push({
462
+ type: "slow_execution",
463
+ pattern: metric.pattern,
464
+ executionTime: metric.executionTime,
465
+ contentLength: metric.contentLength
466
+ });
467
+ }
468
+ const stats = this.patternStats.get(metric.pattern);
469
+ if (stats && stats.recentTimes.length >= MIN_SAMPLES_FOR_STATS) {
470
+ const avgTime = mean(stats.recentTimes);
471
+ const stdTime = stdev(stats.recentTimes);
472
+ if (stdTime > 0) {
473
+ const zScore = (metric.executionTime - avgTime) / stdTime;
474
+ if (Math.abs(zScore) > this.anomalyThreshold) {
475
+ anomalies.push({
476
+ type: "statistical_anomaly",
477
+ pattern: metric.pattern,
478
+ executionTime: metric.executionTime,
479
+ zScore,
480
+ avgTime,
481
+ stdTime
482
+ });
483
+ }
484
+ }
485
+ }
486
+ if (agentHandler) {
487
+ for (const anomaly of anomalies) {
488
+ try {
489
+ await agentHandler.sendEvent({
490
+ timestamp: /* @__PURE__ */ new Date(),
491
+ eventType: `pattern_anomaly_${anomaly["type"]}`,
492
+ ipAddress: "system",
493
+ actionTaken: "anomaly_detected",
494
+ reason: `Pattern performance anomaly: ${anomaly["type"]}`,
495
+ metadata: { component: "PerformanceMonitor", correlationId, ...anomaly }
496
+ });
497
+ } catch {
498
+ }
499
+ }
500
+ }
501
+ for (const anomaly of anomalies) {
502
+ const safe = { ...anomaly };
503
+ if (typeof safe["pattern"] === "string") {
504
+ safe["pattern"] = truncatePattern(safe["pattern"]);
505
+ safe["patternHash"] = patternHash(anomaly["pattern"]);
506
+ }
507
+ for (const callback of this.anomalyCallbacks) {
508
+ try {
509
+ callback(safe);
510
+ } catch {
511
+ }
512
+ }
513
+ }
514
+ }
515
+ getPatternReport(pattern) {
516
+ let key = pattern;
517
+ if (key.length > MAX_PATTERN_LENGTH) {
518
+ key = key.slice(0, MAX_PATTERN_LENGTH) + "...[truncated]";
519
+ }
520
+ const stats = this.patternStats.get(key);
521
+ if (!stats) return null;
522
+ return {
523
+ pattern: truncatePattern(key),
524
+ patternHash: patternHash(key),
525
+ totalExecutions: stats.totalExecutions,
526
+ totalMatches: stats.totalMatches,
527
+ totalTimeouts: stats.totalTimeouts,
528
+ matchRate: stats.totalMatches / Math.max(stats.totalExecutions, 1),
529
+ timeoutRate: stats.totalTimeouts / Math.max(stats.totalExecutions, 1),
530
+ avgExecutionTime: Math.round(stats.avgExecutionTime * 1e4) / 1e4,
531
+ maxExecutionTime: Math.round(stats.maxExecutionTime * 1e4) / 1e4,
532
+ minExecutionTime: Math.round(
533
+ (stats.minExecutionTime === Infinity ? 0 : stats.minExecutionTime) * 1e4
534
+ ) / 1e4
535
+ };
536
+ }
537
+ getSlowPatterns(limit = 10) {
538
+ const entries = [...this.patternStats.entries()].filter(([, s]) => s.recentTimes.length > 0).sort(([, a], [, b]) => b.avgExecutionTime - a.avgExecutionTime).slice(0, limit);
539
+ const reports = [];
540
+ for (const [pattern] of entries) {
541
+ const report = this.getPatternReport(pattern);
542
+ if (report) reports.push(report);
543
+ }
544
+ return reports;
545
+ }
546
+ getProblematicPatterns() {
547
+ const problematic = [];
548
+ for (const [pattern, stats] of this.patternStats) {
549
+ if (stats.totalExecutions === 0) continue;
550
+ const timeoutRate = stats.totalTimeouts / stats.totalExecutions;
551
+ if (timeoutRate > 0.1) {
552
+ const report = this.getPatternReport(pattern);
553
+ if (report) {
554
+ report.issue = "high_timeout_rate";
555
+ problematic.push(report);
556
+ }
557
+ } else if (stats.avgExecutionTime > this.slowPatternThreshold) {
558
+ const report = this.getPatternReport(pattern);
559
+ if (report) {
560
+ report.issue = "consistently_slow";
561
+ problematic.push(report);
562
+ }
563
+ }
564
+ }
565
+ return problematic;
566
+ }
567
+ getSummaryStats() {
568
+ if (this.recentMetrics.length === 0) {
569
+ return { totalExecutions: 0, avgExecutionTime: 0, timeoutRate: 0, matchRate: 0 };
570
+ }
571
+ const recentTimes = this.recentMetrics.filter((m) => !m.timeout).map((m) => m.executionTime);
572
+ const timeouts = this.recentMetrics.filter((m) => m.timeout).length;
573
+ const matches = this.recentMetrics.filter((m) => m.matched).length;
574
+ const total = this.recentMetrics.length;
575
+ return {
576
+ totalExecutions: total,
577
+ avgExecutionTime: recentTimes.length > 0 ? mean(recentTimes) : 0,
578
+ maxExecutionTime: recentTimes.length > 0 ? Math.max(...recentTimes) : 0,
579
+ minExecutionTime: recentTimes.length > 0 ? Math.min(...recentTimes) : 0,
580
+ timeoutRate: timeouts / total,
581
+ matchRate: matches / total,
582
+ totalPatterns: this.patternStats.size
583
+ };
584
+ }
585
+ registerAnomalyCallback(callback) {
586
+ this.anomalyCallbacks.push(callback);
587
+ }
588
+ async clearStats() {
589
+ this.patternStats.clear();
590
+ this.recentMetrics = [];
591
+ }
592
+ async removePatternStats(pattern) {
593
+ this.patternStats.delete(pattern);
594
+ }
595
+ };
596
+
597
+ // src/detection-engine/semantic.ts
598
+ var ATTACK_KEYWORDS = {
599
+ xss: /* @__PURE__ */ new Set([
600
+ "script",
601
+ "javascript",
602
+ "onerror",
603
+ "onload",
604
+ "onclick",
605
+ "onmouseover",
606
+ "alert",
607
+ "eval",
608
+ "document",
609
+ "cookie",
610
+ "window",
611
+ "location"
612
+ ]),
613
+ sql: /* @__PURE__ */ new Set([
614
+ "select",
615
+ "union",
616
+ "insert",
617
+ "update",
618
+ "delete",
619
+ "drop",
620
+ "from",
621
+ "where",
622
+ "order",
623
+ "group",
624
+ "having",
625
+ "concat",
626
+ "substring",
627
+ "database",
628
+ "table",
629
+ "column"
630
+ ]),
631
+ command: /* @__PURE__ */ new Set([
632
+ "exec",
633
+ "system",
634
+ "shell",
635
+ "cmd",
636
+ "bash",
637
+ "powershell",
638
+ "wget",
639
+ "curl",
640
+ "nc",
641
+ "netcat",
642
+ "chmod",
643
+ "chown",
644
+ "sudo",
645
+ "passwd"
646
+ ]),
647
+ path: /* @__PURE__ */ new Set(["etc", "passwd", "shadow", "hosts", "proc", "boot", "win", "ini"]),
648
+ template: /* @__PURE__ */ new Set([
649
+ "render",
650
+ "template",
651
+ "jinja",
652
+ "mustache",
653
+ "handlebars",
654
+ "ejs",
655
+ "pug",
656
+ "twig"
657
+ ])
658
+ };
659
+ var ATTACK_STRUCTURES = {
660
+ tag_like: /<[^>]+>/gi,
661
+ function_call: /\w+\s*\([^)]*\)/gi,
662
+ command_chain: /[;&|]{1,2}/g,
663
+ path_traversal: /\.{2,}[/\\]/g,
664
+ url_pattern: /[a-z]+:\/\//gi
665
+ };
666
+ var PATTERN_CHECKS = {
667
+ xss: [/<[^>]+>/g, ""],
668
+ sql: [/\b(?:union|select|from|where)\b/gi, ""],
669
+ command: [/[;&|]/g, ""],
670
+ path: [/\.{2,}[/\\]/g, ""]
671
+ };
672
+ var INJECTION_KEYWORDS = ["eval", "exec", "compile", "__import__", "globals", "locals"];
673
+ var MAX_CONTENT_LENGTH = 5e4;
674
+ var MAX_TOKENS = 1e3;
675
+ var MAX_ENTROPY_LENGTH = 1e4;
676
+ var MAX_SCAN_LENGTH = 1e4;
677
+ var MAX_AST_LENGTH = 1e3;
678
+ var SemanticAnalyzer = class {
679
+ extractTokens(content) {
680
+ let truncated = content;
681
+ if (truncated.length > MAX_CONTENT_LENGTH) {
682
+ truncated = truncated.slice(0, MAX_CONTENT_LENGTH);
683
+ }
684
+ truncated = truncated.replace(/\s+/g, " ");
685
+ const wordTokens = (truncated.toLowerCase().match(/\b\w+\b/g) ?? []).slice(0, MAX_TOKENS);
686
+ const specialPatterns = [];
687
+ for (const [, regex] of Object.entries(ATTACK_STRUCTURES)) {
688
+ const re = new RegExp(regex.source, regex.flags);
689
+ let match;
690
+ while ((match = re.exec(truncated)) !== null && specialPatterns.length < 50) {
691
+ specialPatterns.push(match[0]);
692
+ }
693
+ if (specialPatterns.length >= 50) break;
694
+ }
695
+ return [...wordTokens, ...specialPatterns].slice(0, MAX_TOKENS);
696
+ }
697
+ calculateEntropy(content) {
698
+ if (!content) return 0;
699
+ const truncated = content.length > MAX_ENTROPY_LENGTH ? content.slice(0, MAX_ENTROPY_LENGTH) : content;
700
+ const counts = /* @__PURE__ */ new Map();
701
+ for (const char of truncated) {
702
+ counts.set(char, (counts.get(char) ?? 0) + 1);
703
+ }
704
+ const length = truncated.length;
705
+ let entropy = 0;
706
+ for (const count of counts.values()) {
707
+ const probability = count / length;
708
+ if (probability > 0) {
709
+ entropy -= probability * Math.log2(probability);
710
+ }
711
+ }
712
+ return entropy;
713
+ }
714
+ detectEncodingLayers(content) {
715
+ const truncated = content.length > MAX_SCAN_LENGTH ? content.slice(0, MAX_SCAN_LENGTH) : content;
716
+ let layers = 0;
717
+ if (/%[0-9a-fA-F]{2}/.test(truncated)) layers++;
718
+ if (/[A-Za-z0-9+/]{4,}={0,2}/.test(truncated)) layers++;
719
+ if (/(?:0x)?[0-9a-fA-F]{4,}/.test(truncated)) layers++;
720
+ if (/\\u[0-9a-fA-F]{4}/.test(truncated)) layers++;
721
+ if (/&[#\w]+;/.test(truncated)) layers++;
722
+ return layers;
723
+ }
724
+ analyzeAttackProbability(content) {
725
+ const tokens = this.extractTokens(content);
726
+ const tokenSet = new Set(tokens);
727
+ const probabilities = {};
728
+ for (const [attackType, keywords] of Object.entries(ATTACK_KEYWORDS)) {
729
+ let matches = 0;
730
+ for (const token of tokenSet) {
731
+ if (keywords.has(token)) matches++;
732
+ }
733
+ const baseScore = keywords.size > 0 ? matches / keywords.size : 0;
734
+ let patternBoost = 0;
735
+ const check = PATTERN_CHECKS[attackType];
736
+ if (check) {
737
+ const [re] = check;
738
+ if (new RegExp(re.source, re.flags).test(content)) {
739
+ patternBoost = 0.3;
740
+ }
741
+ }
742
+ probabilities[attackType] = Math.min(baseScore + patternBoost, 1);
743
+ }
744
+ return probabilities;
745
+ }
746
+ detectObfuscation(content) {
747
+ if (this.calculateEntropy(content) > 4.5) return true;
748
+ if (this.detectEncodingLayers(content) > 2) return true;
749
+ const specialChars = (content.match(/[^a-zA-Z0-9\s]/g) ?? []).length;
750
+ if (specialChars / Math.max(content.length, 1) > 0.4) return true;
751
+ if (/\S{100,}/.test(content)) return true;
752
+ return false;
753
+ }
754
+ extractSuspiciousPatterns(content) {
755
+ const patterns = [];
756
+ for (const [name, regex] of Object.entries(ATTACK_STRUCTURES)) {
757
+ const re = new RegExp(regex.source, regex.flags);
758
+ let match;
759
+ while ((match = re.exec(content)) !== null) {
760
+ const contextStart = Math.max(0, match.index - 20);
761
+ const contextEnd = Math.min(content.length, match.index + match[0].length + 20);
762
+ patterns.push({
763
+ type: name,
764
+ pattern: match[0],
765
+ position: match.index,
766
+ context: content.slice(contextStart, contextEnd)
767
+ });
768
+ }
769
+ }
770
+ return patterns;
771
+ }
772
+ analyzeCodeInjectionRisk(content) {
773
+ let riskScore = 0;
774
+ if (/[{}].*[{}]/.test(content)) riskScore += 0.2;
775
+ if (/\w+\s*\([^)]*\)/.test(content)) riskScore += 0.2;
776
+ if (/[$@]\w+/.test(content)) riskScore += 0.1;
777
+ if (/[=+\-*/]{2,}/.test(content)) riskScore += 0.1;
778
+ if (content.length <= MAX_AST_LENGTH) {
779
+ try {
780
+ const acorn = __require("acorn");
781
+ acorn.parse(content, { ecmaVersion: "latest", sourceType: "module" });
782
+ riskScore += 0.3;
783
+ } catch {
784
+ }
785
+ }
786
+ for (const keyword of INJECTION_KEYWORDS) {
787
+ if (new RegExp(`\\b${keyword}\\b`, "i").test(content)) {
788
+ riskScore += 0.2;
789
+ break;
790
+ }
791
+ }
792
+ return Math.min(riskScore, 1);
793
+ }
794
+ analyze(content) {
795
+ return {
796
+ attackProbabilities: this.analyzeAttackProbability(content),
797
+ entropy: this.calculateEntropy(content),
798
+ encodingLayers: this.detectEncodingLayers(content),
799
+ isObfuscated: this.detectObfuscation(content),
800
+ suspiciousPatterns: this.extractSuspiciousPatterns(content),
801
+ codeInjectionRisk: this.analyzeCodeInjectionRisk(content),
802
+ tokenCount: this.extractTokens(content).length
803
+ };
804
+ }
805
+ getThreatScore(analysis) {
806
+ let score = 0;
807
+ const probs = analysis.attackProbabilities;
808
+ const maxProb = Object.values(probs).length > 0 ? Math.max(...Object.values(probs)) : 0;
809
+ score += maxProb * 0.3;
810
+ if (analysis.isObfuscated) score += 0.2;
811
+ if (analysis.encodingLayers > 0) {
812
+ score += Math.min(analysis.encodingLayers * 0.1, 0.2);
813
+ }
814
+ score += analysis.codeInjectionRisk * 0.2;
815
+ if (analysis.suspiciousPatterns.length > 0) {
816
+ score += Math.min(analysis.suspiciousPatterns.length * 0.05, 0.1);
817
+ }
818
+ return Math.min(score, 1);
819
+ }
820
+ };
821
+
822
+ // src/handlers/sus-patterns.ts
823
+ var CTX_XSS = /* @__PURE__ */ new Set(["query_param", "header", "request_body", "unknown"]);
824
+ var CTX_SQLI = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
825
+ var CTX_DIR_TRAVERSAL = /* @__PURE__ */ new Set(["url_path", "query_param", "request_body", "unknown"]);
826
+ var CTX_CMD_INJECTION = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
827
+ var CTX_FILE_INCLUSION = /* @__PURE__ */ new Set(["url_path", "query_param", "request_body", "unknown"]);
828
+ var CTX_LDAP = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
829
+ var CTX_XML = /* @__PURE__ */ new Set(["header", "request_body", "unknown"]);
830
+ var CTX_SSRF = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
831
+ var CTX_NOSQL = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
832
+ var CTX_FILE_UPLOAD = /* @__PURE__ */ new Set(["header", "request_body", "unknown"]);
833
+ var CTX_PATH_TRAVERSAL = /* @__PURE__ */ new Set(["url_path", "query_param", "request_body", "unknown"]);
834
+ var CTX_TEMPLATE = /* @__PURE__ */ new Set(["query_param", "request_body", "unknown"]);
835
+ var CTX_HTTP_SPLIT = /* @__PURE__ */ new Set(["header", "query_param", "request_body", "unknown"]);
836
+ var CTX_SENSITIVE_FILE = /* @__PURE__ */ new Set(["url_path", "request_body", "unknown"]);
837
+ var CTX_CMS_PROBING = /* @__PURE__ */ new Set(["url_path", "request_body", "unknown"]);
838
+ var CTX_RECON = /* @__PURE__ */ new Set(["url_path", "unknown"]);
839
+ var CTX_ALL = /* @__PURE__ */ new Set(["query_param", "header", "url_path", "request_body", "unknown"]);
840
+ var KNOWN_CONTEXTS = /* @__PURE__ */ new Set(["query_param", "header", "url_path", "request_body", "unknown"]);
841
+ var PATTERN_DEFINITIONS = [
842
+ [String.raw`<script[^>]*>[^<]*<\/script\s*>`, CTX_XSS],
843
+ [String.raw`javascript:\s*[^\s]+`, CTX_XSS],
844
+ [String.raw`(?:on(?:error|load|click|mouseover|submit|mouse|unload|change|focus|blur|drag))=(?:["'][^"']*["']|[^\s>]+)`, CTX_XSS],
845
+ [String.raw`(?:<[^>]+\s+(?:href|src|data|action)\s*=[\s"']*(?:javascript|vbscript|data):)`, CTX_XSS],
846
+ [String.raw`(?:<[^>]+style\s*=[\s"']*[^>"']*(?:expression|behavior|url)\s*\([^)]*\))`, CTX_XSS],
847
+ [String.raw`(?:<object[^>]*>[\s\S]*<\/object\s*>)`, CTX_XSS],
848
+ [String.raw`(?:<embed[^>]*>[\s\S]*<\/embed\s*>)`, CTX_XSS],
849
+ [String.raw`(?:<applet[^>]*>[\s\S]*<\/applet\s*>)`, CTX_XSS],
850
+ [String.raw`SELECT\s+[\w\s,*]+\s+FROM\s+[\w\s._]+`, CTX_SQLI],
851
+ [String.raw`UNION\s+(?:ALL\s+)?SELECT`, CTX_SQLI],
852
+ [String.raw`('\s*(?:OR|AND)\s*[(\s]*'?[\d\w]+\s*(?:=|LIKE|<|>|<=|>=)\s*[(\s]*'?[\d\w]+)`, CTX_SQLI],
853
+ [String.raw`(UNION\s+(?:ALL\s+)?SELECT\s+(?:NULL[,\s]*)+|\(\s*SELECT\s+(?:@@|VERSION))`, CTX_SQLI],
854
+ [String.raw`(?:INTO\s+(?:OUTFILE|DUMPFILE)\s+'[^']+')`, CTX_SQLI],
855
+ [String.raw`(?:LOAD_FILE\s*\([^)]+\))`, CTX_SQLI],
856
+ [String.raw`(?:BENCHMARK\s*\(\s*\d+\s*,)`, CTX_SQLI],
857
+ [String.raw`(?:SLEEP\s*\(\s*\d+\s*\))`, CTX_SQLI],
858
+ [String.raw`(?:\/\*![0-9]*\s*(?:OR|AND|UNION|SELECT|INSERT|DELETE|DROP|CONCAT|CHAR|UPDATE)\b)`, CTX_SQLI],
859
+ [String.raw`(?:\.\.\/|\.\.\\)(?:\.\.\/|\.\.\\)+`, CTX_DIR_TRAVERSAL],
860
+ [String.raw`(?:/etc/(?:passwd|shadow|group|hosts|motd|issue|mysql/my.cnf|ssh/ssh_config)$)`, CTX_DIR_TRAVERSAL],
861
+ [String.raw`(?:boot\.ini|win\.ini|system\.ini|config\.sys)\s*$`, CTX_DIR_TRAVERSAL],
862
+ [String.raw`(?:\/proc\/self\/environ$)`, CTX_DIR_TRAVERSAL],
863
+ [String.raw`(?:\/var\/log\/[^/]+$)`, CTX_DIR_TRAVERSAL],
864
+ [String.raw`;\s*(?:ls|cat|rm|chmod|chown|wget|curl|nc|netcat|ping|telnet)\s+-[a-zA-Z]+\s+`, CTX_CMD_INJECTION],
865
+ [String.raw`\|\s*(?:wget|curl|fetch|lwp-download|lynx|links|GET)\s+`, CTX_CMD_INJECTION],
866
+ [String.raw`(?:[;&|` + "`" + String.raw`]\s*(?:\$\([^)]+\)|\$\{[^}]+\}))`, CTX_CMD_INJECTION],
867
+ [String.raw`(?:^|;)\s*(?:bash|sh|ksh|csh|tsch|zsh|ash)\s+-[a-zA-Z]+`, CTX_CMD_INJECTION],
868
+ [String.raw`\b(?:eval|system|exec|shell_exec|passthru|popen|proc_open)\s*\(`, CTX_CMD_INJECTION],
869
+ [String.raw`(?:php|data|zip|rar|file|glob|expect|input|phpinfo|zlib|phar|ssh2|rar|ogg|expect)://[^\s]+`, CTX_FILE_INCLUSION],
870
+ [String.raw`(?:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:[0-9]+)?(?:\/?)?(?:[a-zA-Z0-9\-.,?'/\\+&amp;%$#_]*)?)`, CTX_FILE_INCLUSION],
871
+ [String.raw`\(\s*[|&]\s*\(\s*[^)]+=[*]`, CTX_LDAP],
872
+ [String.raw`(?:\*(?:[\s\d\w]+\s*=|=\s*[\d\w\s]+))`, CTX_LDAP],
873
+ [String.raw`(?:\(\s*[&|]\s*)`, CTX_LDAP],
874
+ [String.raw`<!(?:ENTITY|DOCTYPE)[^>]+SYSTEM[^>]+>`, CTX_XML],
875
+ [String.raw`(?:<!\[CDATA\[.*?\]\]>)`, CTX_XML],
876
+ [String.raw`(?:<\?xml.*?\?>)`, CTX_XML],
877
+ [String.raw`(?:^|\s|/)(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::(?:\d*)\]|(?:169\.254|192\.168|10\.|172\.(?:1[6-9]|2[0-9]|3[01]))\.\d+)(?:\s|$|/)`, CTX_SSRF],
878
+ [String.raw`(?:file|dict|gopher|jar|tftp)://[^\s]+`, CTX_SSRF],
879
+ [String.raw`\{\s*\$(?:where|gt|lt|ne|eq|regex|in|nin|all|size|exists|type|mod|options):`, CTX_NOSQL],
880
+ [String.raw`(?:\{\s*\$[a-zA-Z]+\s*:\s*(?:\{|\[))`, CTX_NOSQL],
881
+ [String.raw`filename=["'].*?\.(?:php\d*|phar|phtml|exe|jsp|asp|aspx|sh|bash|rb|py|pl|cgi|com|bat|cmd|vbs|vbe|js|ws|wsf|msi|hta)["']`, CTX_FILE_UPLOAD],
882
+ [String.raw`(?:%2e%2e|%252e%252e|%uff0e%uff0e|%c0%ae%c0%ae|%e0%40%ae|%c0%ae%e0%80%ae|%25c0%25ae)/`, CTX_PATH_TRAVERSAL],
883
+ [String.raw`\{\{\s*[^}]+(?:system|exec|popen|eval|require|include)\s*\}\}`, CTX_TEMPLATE],
884
+ [String.raw`\{%\s*[^%]+(?:system|exec|popen|eval|require|include)\s*%\}`, CTX_TEMPLATE],
885
+ [String.raw`[\r\n]\s*(?:HTTP\/[0-9.]+|Location:|Set-Cookie:)`, CTX_HTTP_SPLIT],
886
+ [String.raw`(?:^|/)\.env(?:\.\w+)?(?:\?|$|/)`, CTX_SENSITIVE_FILE],
887
+ [String.raw`(?:^|/)[\w-]*config[\w-]*\.(?:env|yml|yaml|json|toml|ini|xml|conf)(?:\?|$)`, CTX_SENSITIVE_FILE],
888
+ [String.raw`(?:^|/)[\w./-]*\.map(?:\?|$)`, CTX_SENSITIVE_FILE],
889
+ [String.raw`(?:^|/)[\w./-]*\.(?:ts|tsx|jsx|py|rb|java|go|rs|php|pl|sh|sql)(?:\?|$)`, CTX_SENSITIVE_FILE],
890
+ [String.raw`(?:^|/)\.(?:git|svn|hg|bzr)(?:/|$)`, CTX_SENSITIVE_FILE],
891
+ [String.raw`(?:^|/)(?:wp-(?:admin|login|content|includes|config)|administrator|xmlrpc)\.?(?:php)?(?:/|$|\?)`, CTX_CMS_PROBING],
892
+ [String.raw`(?:^|/)(?:phpinfo|info|test|php_info)\.php(?:\?|$)`, CTX_CMS_PROBING],
893
+ [String.raw`(?:^|/)[\w./-]*\.(?:bak|backup|old|orig|save|swp|swo|tmp|temp)(?:\?|$)`, CTX_CMS_PROBING],
894
+ [String.raw`(?:^|/)(?:\.htaccess|\.htpasswd|\.DS_Store|Thumbs\.db|\.npmrc|\.dockerenv|web\.config)(?:\?|$)`, CTX_CMS_PROBING],
895
+ [String.raw`(?:^|/)[\w./-]*\.(?:asp|aspx|jsp|jsa|jhtml|shtml|cfm|cgi|do|action|lua|inc|woa|nsf|esp|html?|js|css|properties|png|gif|jpg|jpeg|svg|webp|bmp|pl)(?:\?|$)`, CTX_RECON],
896
+ [String.raw`^/(?:api|rest|v\d+|management|system|version|status|config|config_dump|credentials)(?:/|$|\?)`, CTX_RECON],
897
+ [String.raw`^/admin(?:istrator)?(?:[./?\-]|$)`, CTX_RECON],
898
+ [String.raw`^/(?:login|logon|signin)(?:[./?\-]|$|/)`, CTX_RECON],
899
+ [String.raw`(?:^|/)account/login(?:\?|$|/)`, CTX_RECON],
900
+ [String.raw`(?:^|/)(?:actuator|server-status|telescope)(?:/|$|\?)`, CTX_RECON],
901
+ [String.raw`(?:CSCOE|dana-(?:na|cached)|sslvpn|RDWeb|/owa/|/ecp/|global-protect|ssl-vpn/|svpn/|sonicui|/remote/login|myvpn|vpntunnel|versa/login)`, CTX_RECON],
902
+ [String.raw`(?:^|/)(?:geoserver|confluence|nifi|ScadaBR|pandora_console|centreon|kylin|decisioncenter|evox|MagicInfo|metasys|officescan|helpdesk|ignite)(?:/|$|\?|\.|\-)`, CTX_RECON],
903
+ [String.raw`(?:^|/)cgi-(?:bin|mod)/`, CTX_RECON],
904
+ [String.raw`(?:^|/)(?:HNAP1|IPCamDesc\.xml|SDK/webLanguage)(?:\?|$|/)`, CTX_RECON],
905
+ [String.raw`^/(?:scripts|language|languages|images|css|img)/`, CTX_RECON],
906
+ [String.raw`(?:^|/)(?:robots\.txt|sitemap\.xml|security\.txt|readme\.txt|README\.md|CHANGELOG|pom\.xml|build\.gradle|appsettings\.json|crossdomain\.xml)(?:\?|$|\.)`, CTX_RECON],
907
+ [String.raw`(?:^|/)(?:sap|ise|nidp|cslu|rustfs|developmentserver|fog/management|lms/db|json/login_session|sms_mp|plugin/webs_model|wsman|am_bin)(?:/|$|\?)`, CTX_RECON],
908
+ [String.raw`(?:nmaplowercheck|nice\s+ports|Trinity\.txt)`, CTX_RECON],
909
+ [String.raw`(?:^|/)\.(?:openclaw|clawdbot)(?:/|$)`, CTX_RECON],
910
+ [String.raw`^/(?:default|inicio|indice|localstart)(?:\.|/|$|\?)`, CTX_RECON],
911
+ [String.raw`(?:^|/)(?:\.streamlit|\.gpt-pilot|\.aider|\.cursor|\.windsurf|\.copilot|\.devcontainer)(?:/|$)`, CTX_RECON],
912
+ [String.raw`(?:^|/)(?:docker-compose|Dockerfile|Makefile|Vagrantfile|Jenkinsfile|Procfile)(?:\.ya?ml)?(?:\?|$)`, CTX_RECON],
913
+ [String.raw`(?:^|/)[\w./-]*(?:secrets?|credentials?)\.(?:py|json|yml|yaml|toml|txt|env|xml|conf|cfg)(?:\?|$)`, CTX_RECON],
914
+ [String.raw`(?:^|/)autodiscover/`, CTX_RECON],
915
+ [String.raw`^/dns-query(?:\?|$)`, CTX_RECON],
916
+ [String.raw`(?:^|/)\.git/(?:refs|index|HEAD|objects|logs)(?:/|$)`, CTX_RECON]
917
+ ];
918
+ var SusPatternsManager = class {
919
+ constructor(config, logger) {
920
+ this.logger = logger;
921
+ this.compiler = new PatternCompiler(config.detectionCompilerTimeout * 1e3, config.detectionMaxTrackedPatterns);
922
+ this.preprocessor = new ContentPreprocessor(config.detectionMaxContentLength, config.detectionPreserveAttackPatterns);
923
+ this.semantic = new SemanticAnalyzer();
924
+ this.monitor = new PerformanceMonitor(
925
+ config.detectionAnomalyThreshold,
926
+ config.detectionSlowPatternThreshold,
927
+ config.detectionMonitorHistorySize,
928
+ config.detectionMaxTrackedPatterns
929
+ );
930
+ this.semanticThreshold = config.detectionSemanticThreshold;
931
+ }
932
+ compiler;
933
+ preprocessor;
934
+ semantic;
935
+ monitor;
936
+ customPatterns = /* @__PURE__ */ new Set();
937
+ compiledCustomContexts = /* @__PURE__ */ new Map();
938
+ redisHandler = null;
939
+ agentHandler = null;
940
+ semanticThreshold;
941
+ async initializeRedis(redisHandler) {
942
+ this.redisHandler = redisHandler;
943
+ const cached = await redisHandler.getKey("patterns", "custom");
944
+ if (typeof cached === "string" && cached.length > 0) {
945
+ for (const p of cached.split(",")) {
946
+ if (p.trim()) {
947
+ this.customPatterns.add(p.trim());
948
+ this.compiledCustomContexts.set(p.trim(), CTX_ALL);
949
+ }
950
+ }
951
+ }
952
+ }
953
+ async initializeAgent(agentHandler) {
954
+ this.agentHandler = agentHandler;
955
+ }
956
+ normalizeContext(context) {
957
+ const parts = context.split(":");
958
+ const normalized = parts[0].toLowerCase();
959
+ return KNOWN_CONTEXTS.has(normalized) ? normalized : "unknown";
960
+ }
961
+ async detect(content, ipAddress, context = "unknown", correlationId = null) {
962
+ const startTime = performance.now();
963
+ const originalLength = content.length;
964
+ const processed = await this.preprocessor.preprocess(content);
965
+ const normalizedCtx = this.normalizeContext(context);
966
+ const threats = [];
967
+ const timeouts = [];
968
+ for (const [pattern, contexts] of PATTERN_DEFINITIONS) {
969
+ if (!contexts.has(normalizedCtx)) continue;
970
+ const patternStart = performance.now();
971
+ try {
972
+ const match = await this.compiler.safeMatch(pattern, processed);
973
+ const elapsed = (performance.now() - patternStart) / 1e3;
974
+ await this.monitor.recordMetric(pattern, elapsed, processed.length, match !== null, false, this.agentHandler, correlationId);
975
+ if (match) {
976
+ threats.push({
977
+ pattern,
978
+ context: normalizedCtx,
979
+ matchedContent: String(match[0] ?? "").slice(0, 200),
980
+ detectionMethod: "regex"
981
+ });
982
+ }
983
+ } catch {
984
+ timeouts.push(pattern);
985
+ const elapsed = (performance.now() - patternStart) / 1e3;
986
+ await this.monitor.recordMetric(pattern, elapsed, processed.length, false, true, this.agentHandler, correlationId);
987
+ }
988
+ }
989
+ for (const customPattern of this.customPatterns) {
990
+ const ctxSet = this.compiledCustomContexts.get(customPattern) ?? CTX_ALL;
991
+ if (!ctxSet.has(normalizedCtx)) continue;
992
+ try {
993
+ const match = await this.compiler.safeMatch(customPattern, processed);
994
+ if (match) {
995
+ threats.push({
996
+ pattern: customPattern,
997
+ context: normalizedCtx,
998
+ matchedContent: String(match[0] ?? "").slice(0, 200),
999
+ detectionMethod: "regex_custom"
1000
+ });
1001
+ }
1002
+ } catch {
1003
+ timeouts.push(customPattern);
1004
+ }
1005
+ }
1006
+ const analysis = this.semantic.analyze(processed);
1007
+ const semanticScore = this.semantic.getThreatScore(analysis);
1008
+ if (semanticScore >= this.semanticThreshold) {
1009
+ const topAttack = Object.entries(analysis.attackProbabilities).sort(([, a], [, b]) => b - a)[0];
1010
+ if (topAttack) {
1011
+ threats.push({
1012
+ pattern: `semantic:${topAttack[0]}`,
1013
+ context: normalizedCtx,
1014
+ matchedContent: `score=${semanticScore.toFixed(3)}`,
1015
+ detectionMethod: "semantic"
1016
+ });
1017
+ }
1018
+ }
1019
+ const regexScore = threats.some((t) => t.detectionMethod.startsWith("regex")) ? 1 : 0;
1020
+ const threatScore = Math.max(regexScore, semanticScore);
1021
+ const executionTime = (performance.now() - startTime) / 1e3;
1022
+ return {
1023
+ isThreat: threats.length > 0,
1024
+ threatScore,
1025
+ threats,
1026
+ executionTime,
1027
+ timeouts,
1028
+ correlationId,
1029
+ originalLength,
1030
+ processedLength: processed.length
1031
+ };
1032
+ }
1033
+ async detectPatternMatch(content, ipAddress, context = "unknown", correlationId = null) {
1034
+ const result = await this.detect(content, ipAddress, context, correlationId);
1035
+ if (result.isThreat && result.threats.length > 0) {
1036
+ return [true, result.threats[0].pattern];
1037
+ }
1038
+ return [false, null];
1039
+ }
1040
+ async addPattern(pattern, custom = true) {
1041
+ this.customPatterns.add(pattern);
1042
+ this.compiledCustomContexts.set(pattern, CTX_ALL);
1043
+ if (custom && this.redisHandler) {
1044
+ await this.redisHandler.setKey("patterns", "custom", [...this.customPatterns].join(","));
1045
+ }
1046
+ }
1047
+ async removePattern(pattern) {
1048
+ this.customPatterns.delete(pattern);
1049
+ this.compiledCustomContexts.delete(pattern);
1050
+ if (this.redisHandler) {
1051
+ await this.redisHandler.setKey("patterns", "custom", [...this.customPatterns].join(","));
1052
+ }
1053
+ await this.compiler.clearCache();
1054
+ await this.monitor.removePatternStats(pattern);
1055
+ }
1056
+ getDefaultPatterns() {
1057
+ return PATTERN_DEFINITIONS.map(([p]) => p);
1058
+ }
1059
+ getCustomPatterns() {
1060
+ return [...this.customPatterns];
1061
+ }
1062
+ getAllPatterns() {
1063
+ return [...this.getDefaultPatterns(), ...this.getCustomPatterns()];
1064
+ }
1065
+ async getPerformanceStats() {
1066
+ return {
1067
+ summary: this.monitor.getSummaryStats(),
1068
+ slowPatterns: this.monitor.getSlowPatterns(),
1069
+ problematicPatterns: this.monitor.getProblematicPatterns()
1070
+ };
1071
+ }
1072
+ getComponentStatus() {
1073
+ return {
1074
+ compiler: true,
1075
+ preprocessor: true,
1076
+ semanticAnalyzer: true,
1077
+ performanceMonitor: true
1078
+ };
1079
+ }
1080
+ async configureSemanticThreshold(threshold) {
1081
+ this.semanticThreshold = Math.max(0, Math.min(1, threshold));
1082
+ }
1083
+ async reset() {
1084
+ this.customPatterns.clear();
1085
+ this.compiledCustomContexts.clear();
1086
+ this.agentHandler = null;
1087
+ await this.compiler.clearCache();
1088
+ await this.monitor.clearStats();
1089
+ }
1090
+ };
1091
+
1092
+ export {
1093
+ PatternCompiler,
1094
+ ContentPreprocessor,
1095
+ PerformanceMonitor,
1096
+ SemanticAnalyzer,
1097
+ SusPatternsManager
1098
+ };
1099
+ //# sourceMappingURL=chunk-I634N6VV.js.map