@nahisaho/musubix-security 1.8.0 → 1.8.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.
Files changed (186) hide show
  1. package/README.md +27 -0
  2. package/dist/analyzers/ai/index.d.ts +6 -0
  3. package/dist/analyzers/ai/index.d.ts.map +1 -0
  4. package/dist/analyzers/ai/index.js +6 -0
  5. package/dist/analyzers/ai/index.js.map +1 -0
  6. package/dist/analyzers/ai/prompt-injection-detector.d.ts +152 -0
  7. package/dist/analyzers/ai/prompt-injection-detector.d.ts.map +1 -0
  8. package/dist/analyzers/ai/prompt-injection-detector.js +468 -0
  9. package/dist/analyzers/ai/prompt-injection-detector.js.map +1 -0
  10. package/dist/analyzers/api/api-security-analyzer.d.ts +263 -0
  11. package/dist/analyzers/api/api-security-analyzer.d.ts.map +1 -0
  12. package/dist/analyzers/api/api-security-analyzer.js +581 -0
  13. package/dist/analyzers/api/api-security-analyzer.js.map +1 -0
  14. package/dist/analyzers/compliance/compliance-checker.d.ts +201 -0
  15. package/dist/analyzers/compliance/compliance-checker.d.ts.map +1 -0
  16. package/dist/analyzers/compliance/compliance-checker.js +772 -0
  17. package/dist/analyzers/compliance/compliance-checker.js.map +1 -0
  18. package/dist/analyzers/container/image-scanner.d.ts +163 -0
  19. package/dist/analyzers/container/image-scanner.d.ts.map +1 -0
  20. package/dist/analyzers/container/image-scanner.js +459 -0
  21. package/dist/analyzers/container/image-scanner.js.map +1 -0
  22. package/dist/analyzers/container/index.d.ts +6 -0
  23. package/dist/analyzers/container/index.d.ts.map +1 -0
  24. package/dist/analyzers/container/index.js +6 -0
  25. package/dist/analyzers/container/index.js.map +1 -0
  26. package/dist/analyzers/dashboard/security-dashboard.d.ts +286 -0
  27. package/dist/analyzers/dashboard/security-dashboard.d.ts.map +1 -0
  28. package/dist/analyzers/dashboard/security-dashboard.js +796 -0
  29. package/dist/analyzers/dashboard/security-dashboard.js.map +1 -0
  30. package/dist/analyzers/iac/iac-checker.d.ts +124 -0
  31. package/dist/analyzers/iac/iac-checker.d.ts.map +1 -0
  32. package/dist/analyzers/iac/iac-checker.js +755 -0
  33. package/dist/analyzers/iac/iac-checker.js.map +1 -0
  34. package/dist/analyzers/iac/index.d.ts +6 -0
  35. package/dist/analyzers/iac/index.d.ts.map +1 -0
  36. package/dist/analyzers/iac/index.js +6 -0
  37. package/dist/analyzers/iac/index.js.map +1 -0
  38. package/dist/analyzers/index.d.ts +9 -0
  39. package/dist/analyzers/index.d.ts.map +1 -0
  40. package/dist/analyzers/index.js +13 -0
  41. package/dist/analyzers/index.js.map +1 -0
  42. package/dist/analyzers/monitor/realtime-monitor.d.ts +216 -0
  43. package/dist/analyzers/monitor/realtime-monitor.d.ts.map +1 -0
  44. package/dist/analyzers/monitor/realtime-monitor.js +601 -0
  45. package/dist/analyzers/monitor/realtime-monitor.js.map +1 -0
  46. package/dist/analyzers/sast/index.d.ts +7 -0
  47. package/dist/analyzers/sast/index.d.ts.map +1 -0
  48. package/dist/analyzers/sast/index.js +7 -0
  49. package/dist/analyzers/sast/index.js.map +1 -0
  50. package/dist/analyzers/sast/interprocedural-analyzer.d.ts +276 -0
  51. package/dist/analyzers/sast/interprocedural-analyzer.d.ts.map +1 -0
  52. package/dist/analyzers/sast/interprocedural-analyzer.js +635 -0
  53. package/dist/analyzers/sast/interprocedural-analyzer.js.map +1 -0
  54. package/dist/analyzers/sast/zero-day-detector.d.ts +183 -0
  55. package/dist/analyzers/sast/zero-day-detector.d.ts.map +1 -0
  56. package/dist/analyzers/sast/zero-day-detector.js +593 -0
  57. package/dist/analyzers/sast/zero-day-detector.js.map +1 -0
  58. package/dist/analyzers/sca/dependency-scanner.d.ts +275 -0
  59. package/dist/analyzers/sca/dependency-scanner.d.ts.map +1 -0
  60. package/dist/analyzers/sca/dependency-scanner.js +642 -0
  61. package/dist/analyzers/sca/dependency-scanner.js.map +1 -0
  62. package/dist/core/index.d.ts +8 -0
  63. package/dist/core/index.d.ts.map +1 -0
  64. package/dist/core/index.js +10 -0
  65. package/dist/core/index.js.map +1 -0
  66. package/dist/core/pipeline-manager.d.ts +105 -0
  67. package/dist/core/pipeline-manager.d.ts.map +1 -0
  68. package/dist/core/pipeline-manager.js +449 -0
  69. package/dist/core/pipeline-manager.js.map +1 -0
  70. package/dist/core/result-aggregator.d.ts +96 -0
  71. package/dist/core/result-aggregator.d.ts.map +1 -0
  72. package/dist/core/result-aggregator.js +462 -0
  73. package/dist/core/result-aggregator.js.map +1 -0
  74. package/dist/index.d.ts +15 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +68 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/integrations/ci-integration.d.ts +227 -0
  79. package/dist/integrations/ci-integration.d.ts.map +1 -0
  80. package/dist/integrations/ci-integration.js +472 -0
  81. package/dist/integrations/ci-integration.js.map +1 -0
  82. package/dist/integrations/git-hooks.d.ts +155 -0
  83. package/dist/integrations/git-hooks.d.ts.map +1 -0
  84. package/dist/integrations/git-hooks.js +425 -0
  85. package/dist/integrations/git-hooks.js.map +1 -0
  86. package/dist/integrations/index.d.ts +9 -0
  87. package/dist/integrations/index.d.ts.map +1 -0
  88. package/dist/integrations/index.js +9 -0
  89. package/dist/integrations/index.js.map +1 -0
  90. package/dist/integrations/report-aggregator.d.ts +250 -0
  91. package/dist/integrations/report-aggregator.d.ts.map +1 -0
  92. package/dist/integrations/report-aggregator.js +488 -0
  93. package/dist/integrations/report-aggregator.js.map +1 -0
  94. package/dist/integrations/vscode-integration.d.ts +245 -0
  95. package/dist/integrations/vscode-integration.d.ts.map +1 -0
  96. package/dist/integrations/vscode-integration.js +449 -0
  97. package/dist/integrations/vscode-integration.js.map +1 -0
  98. package/dist/intelligence/attack-pattern-matcher.d.ts +217 -0
  99. package/dist/intelligence/attack-pattern-matcher.d.ts.map +1 -0
  100. package/dist/intelligence/attack-pattern-matcher.js +887 -0
  101. package/dist/intelligence/attack-pattern-matcher.js.map +1 -0
  102. package/dist/intelligence/index.d.ts +12 -0
  103. package/dist/intelligence/index.d.ts.map +1 -0
  104. package/dist/intelligence/index.js +18 -0
  105. package/dist/intelligence/index.js.map +1 -0
  106. package/dist/intelligence/neuro-symbolic-core.d.ts +88 -0
  107. package/dist/intelligence/neuro-symbolic-core.d.ts.map +1 -0
  108. package/dist/intelligence/neuro-symbolic-core.js +403 -0
  109. package/dist/intelligence/neuro-symbolic-core.js.map +1 -0
  110. package/dist/intelligence/predictive-analyzer.d.ts +317 -0
  111. package/dist/intelligence/predictive-analyzer.d.ts.map +1 -0
  112. package/dist/intelligence/predictive-analyzer.js +714 -0
  113. package/dist/intelligence/predictive-analyzer.js.map +1 -0
  114. package/dist/intelligence/risk-scorer.d.ts +333 -0
  115. package/dist/intelligence/risk-scorer.d.ts.map +1 -0
  116. package/dist/intelligence/risk-scorer.js +824 -0
  117. package/dist/intelligence/risk-scorer.js.map +1 -0
  118. package/dist/intelligence/security-analytics.d.ts +349 -0
  119. package/dist/intelligence/security-analytics.d.ts.map +1 -0
  120. package/dist/intelligence/security-analytics.js +813 -0
  121. package/dist/intelligence/security-analytics.js.map +1 -0
  122. package/dist/intelligence/threat-intelligence.d.ts +288 -0
  123. package/dist/intelligence/threat-intelligence.d.ts.map +1 -0
  124. package/dist/intelligence/threat-intelligence.js +639 -0
  125. package/dist/intelligence/threat-intelligence.js.map +1 -0
  126. package/dist/policy/index.d.ts +6 -0
  127. package/dist/policy/index.d.ts.map +1 -0
  128. package/dist/policy/index.js +6 -0
  129. package/dist/policy/index.js.map +1 -0
  130. package/dist/policy/policy-engine.d.ts +254 -0
  131. package/dist/policy/policy-engine.d.ts.map +1 -0
  132. package/dist/policy/policy-engine.js +651 -0
  133. package/dist/policy/policy-engine.js.map +1 -0
  134. package/dist/remediation/auto-fixer.d.ts +179 -0
  135. package/dist/remediation/auto-fixer.d.ts.map +1 -0
  136. package/dist/remediation/auto-fixer.js +540 -0
  137. package/dist/remediation/auto-fixer.js.map +1 -0
  138. package/dist/remediation/fix-validator.d.ts +195 -0
  139. package/dist/remediation/fix-validator.d.ts.map +1 -0
  140. package/dist/remediation/fix-validator.js +462 -0
  141. package/dist/remediation/fix-validator.js.map +1 -0
  142. package/dist/remediation/index.d.ts +10 -0
  143. package/dist/remediation/index.d.ts.map +1 -0
  144. package/dist/remediation/index.js +15 -0
  145. package/dist/remediation/index.js.map +1 -0
  146. package/dist/remediation/patch-generator.d.ts +203 -0
  147. package/dist/remediation/patch-generator.d.ts.map +1 -0
  148. package/dist/remediation/patch-generator.js +533 -0
  149. package/dist/remediation/patch-generator.js.map +1 -0
  150. package/dist/remediation/remediation-planner.d.ts +262 -0
  151. package/dist/remediation/remediation-planner.d.ts.map +1 -0
  152. package/dist/remediation/remediation-planner.js +531 -0
  153. package/dist/remediation/remediation-planner.js.map +1 -0
  154. package/dist/remediation/secure-code-transformer.d.ts +222 -0
  155. package/dist/remediation/secure-code-transformer.d.ts.map +1 -0
  156. package/dist/remediation/secure-code-transformer.js +625 -0
  157. package/dist/remediation/secure-code-transformer.js.map +1 -0
  158. package/dist/types/fix.d.ts +3 -1
  159. package/dist/types/fix.d.ts.map +1 -1
  160. package/dist/types/index.d.ts +6 -0
  161. package/dist/types/index.d.ts.map +1 -1
  162. package/dist/types/index.js +1 -0
  163. package/dist/types/index.js.map +1 -1
  164. package/dist/types/interprocedural.d.ts +203 -0
  165. package/dist/types/interprocedural.d.ts.map +1 -0
  166. package/dist/types/interprocedural.js +7 -0
  167. package/dist/types/interprocedural.js.map +1 -0
  168. package/dist/types/neuro-symbolic.d.ts +179 -0
  169. package/dist/types/neuro-symbolic.d.ts.map +1 -0
  170. package/dist/types/neuro-symbolic.js +7 -0
  171. package/dist/types/neuro-symbolic.js.map +1 -0
  172. package/dist/types/pipeline.d.ts +173 -0
  173. package/dist/types/pipeline.d.ts.map +1 -0
  174. package/dist/types/pipeline.js +7 -0
  175. package/dist/types/pipeline.js.map +1 -0
  176. package/dist/types/result.d.ts +134 -0
  177. package/dist/types/result.d.ts.map +1 -0
  178. package/dist/types/result.js +25 -0
  179. package/dist/types/result.js.map +1 -0
  180. package/dist/types/vulnerability.d.ts +2 -2
  181. package/dist/types/vulnerability.d.ts.map +1 -1
  182. package/dist/types/zero-day.d.ts +146 -0
  183. package/dist/types/zero-day.d.ts.map +1 -0
  184. package/dist/types/zero-day.js +7 -0
  185. package/dist/types/zero-day.js.map +1 -0
  186. package/package.json +2 -2
@@ -0,0 +1,813 @@
1
+ /**
2
+ * @fileoverview Security Analytics Engine
3
+ * @module @nahisaho/musubix-security/intelligence/security-analytics
4
+ *
5
+ * Provides trend analysis, metrics collection, statistical reporting,
6
+ * and security insights for comprehensive security management.
7
+ */
8
+ // ============================================================================
9
+ // SecurityAnalytics Class
10
+ // ============================================================================
11
+ /**
12
+ * Security Analytics Engine
13
+ */
14
+ export class SecurityAnalytics {
15
+ options;
16
+ events = [];
17
+ metricsHistory = new Map();
18
+ vulnerabilityHistory = new Map();
19
+ constructor(options = {}) {
20
+ this.options = {
21
+ retentionDays: options.retentionDays ?? 90,
22
+ enableRealtime: options.enableRealtime ?? true,
23
+ industryBenchmark: options.industryBenchmark ?? {
24
+ 'vulnerability-count': 50,
25
+ 'average-risk-score': 45,
26
+ 'mean-time-to-remediate': 30,
27
+ 'security-debt': 100,
28
+ 'fix-rate': 70,
29
+ 'detection-rate': 85,
30
+ 'false-positive-rate': 15,
31
+ 'coverage': 80,
32
+ },
33
+ customMetrics: options.customMetrics ?? new Map(),
34
+ };
35
+ // Initialize metrics history
36
+ const metricTypes = [
37
+ 'vulnerability-count',
38
+ 'average-risk-score',
39
+ 'mean-time-to-remediate',
40
+ 'security-debt',
41
+ 'fix-rate',
42
+ 'detection-rate',
43
+ 'false-positive-rate',
44
+ 'coverage',
45
+ ];
46
+ for (const type of metricTypes) {
47
+ this.metricsHistory.set(type, []);
48
+ }
49
+ }
50
+ /**
51
+ * Record an analytics event
52
+ */
53
+ recordEvent(event) {
54
+ const fullEvent = {
55
+ id: `EVT-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
56
+ ...event,
57
+ };
58
+ this.events.push(fullEvent);
59
+ // Prune old events
60
+ this.pruneOldEvents();
61
+ }
62
+ /**
63
+ * Record vulnerability discovery
64
+ */
65
+ recordVulnerability(vulnerability) {
66
+ this.vulnerabilityHistory.set(vulnerability.id, {
67
+ found: new Date(),
68
+ });
69
+ this.recordEvent({
70
+ type: 'vulnerability',
71
+ timestamp: new Date(),
72
+ data: {
73
+ vulnerabilityId: vulnerability.id,
74
+ type: vulnerability.type,
75
+ severity: vulnerability.severity,
76
+ file: vulnerability.location.file,
77
+ },
78
+ tags: ['discovery', vulnerability.severity, vulnerability.type],
79
+ });
80
+ }
81
+ /**
82
+ * Record vulnerability fix
83
+ */
84
+ recordFix(vulnerabilityId) {
85
+ const record = this.vulnerabilityHistory.get(vulnerabilityId);
86
+ if (record) {
87
+ record.fixed = new Date();
88
+ }
89
+ this.recordEvent({
90
+ type: 'fix',
91
+ timestamp: new Date(),
92
+ data: { vulnerabilityId },
93
+ tags: ['remediation'],
94
+ });
95
+ }
96
+ /**
97
+ * Record scan completion
98
+ */
99
+ recordScan(scanResults) {
100
+ this.recordEvent({
101
+ type: 'scan',
102
+ timestamp: new Date(),
103
+ data: scanResults,
104
+ tags: ['scan'],
105
+ });
106
+ }
107
+ /**
108
+ * Prune old events
109
+ */
110
+ pruneOldEvents() {
111
+ const cutoff = new Date();
112
+ cutoff.setDate(cutoff.getDate() - this.options.retentionDays);
113
+ this.events = this.events.filter(e => e.timestamp >= cutoff);
114
+ }
115
+ /**
116
+ * Get events by type
117
+ */
118
+ getEventsByType(type, since) {
119
+ return this.events.filter(e => e.type === type && (!since || e.timestamp >= since));
120
+ }
121
+ /**
122
+ * Calculate metric for a period
123
+ */
124
+ calculateMetric(type, period = 'day') {
125
+ const now = new Date();
126
+ const periodStart = this.getPeriodStart(now, period);
127
+ const prevPeriodStart = this.getPreviousPeriodStart(periodStart, period);
128
+ let value;
129
+ let unit = '';
130
+ const breakdown = {};
131
+ const periodEvents = this.events.filter(e => e.timestamp >= periodStart);
132
+ const prevPeriodEvents = this.events.filter(e => e.timestamp >= prevPeriodStart && e.timestamp < periodStart);
133
+ switch (type) {
134
+ case 'vulnerability-count': {
135
+ value = periodEvents.filter(e => e.type === 'vulnerability').length;
136
+ unit = 'count';
137
+ // Breakdown by severity
138
+ for (const event of periodEvents.filter(e => e.type === 'vulnerability')) {
139
+ const severity = event.data.severity;
140
+ breakdown[severity] = (breakdown[severity] || 0) + 1;
141
+ }
142
+ break;
143
+ }
144
+ case 'average-risk-score': {
145
+ const vulnEvents = periodEvents.filter(e => e.type === 'vulnerability');
146
+ value = vulnEvents.length > 0
147
+ ? this.calculateAverageRisk(vulnEvents)
148
+ : 0;
149
+ unit = 'score';
150
+ break;
151
+ }
152
+ case 'mean-time-to-remediate': {
153
+ value = this.calculateMTTR();
154
+ unit = 'days';
155
+ break;
156
+ }
157
+ case 'security-debt': {
158
+ value = this.calculateSecurityDebt();
159
+ unit = 'hours';
160
+ break;
161
+ }
162
+ case 'fix-rate': {
163
+ const fixed = periodEvents.filter(e => e.type === 'fix').length;
164
+ const found = periodEvents.filter(e => e.type === 'vulnerability').length;
165
+ value = found > 0 ? Math.round((fixed / found) * 100) : 100;
166
+ unit = '%';
167
+ break;
168
+ }
169
+ case 'detection-rate': {
170
+ // Simulated - would need baseline for real implementation
171
+ value = 85;
172
+ unit = '%';
173
+ break;
174
+ }
175
+ case 'false-positive-rate': {
176
+ // Simulated - would need feedback data
177
+ value = 10;
178
+ unit = '%';
179
+ break;
180
+ }
181
+ case 'coverage': {
182
+ const scans = periodEvents.filter(e => e.type === 'scan');
183
+ value = scans.length > 0
184
+ ? Math.round(scans.reduce((sum, e) => sum + (e.data.filesScanned || 0), 0) / scans.length)
185
+ : 0;
186
+ unit = 'files';
187
+ break;
188
+ }
189
+ default:
190
+ value = 0;
191
+ unit = '';
192
+ }
193
+ // Calculate change
194
+ const prevValue = this.calculatePreviousPeriodValue(type, prevPeriodEvents);
195
+ const changeValue = value - prevValue;
196
+ const changePercentage = prevValue !== 0
197
+ ? Math.round((changeValue / prevValue) * 100)
198
+ : 0;
199
+ const metric = {
200
+ type,
201
+ value,
202
+ unit,
203
+ timestamp: now,
204
+ period,
205
+ change: {
206
+ value: changeValue,
207
+ percentage: changePercentage,
208
+ direction: changeValue > 0 ? 'up' : changeValue < 0 ? 'down' : 'stable',
209
+ },
210
+ breakdown: Object.keys(breakdown).length > 0 ? breakdown : undefined,
211
+ };
212
+ // Store in history
213
+ this.metricsHistory.get(type)?.push(metric);
214
+ return metric;
215
+ }
216
+ /**
217
+ * Get period start date
218
+ */
219
+ getPeriodStart(date, period) {
220
+ const start = new Date(date);
221
+ switch (period) {
222
+ case 'hour':
223
+ start.setMinutes(0, 0, 0);
224
+ break;
225
+ case 'day':
226
+ start.setHours(0, 0, 0, 0);
227
+ break;
228
+ case 'week':
229
+ start.setDate(start.getDate() - start.getDay());
230
+ start.setHours(0, 0, 0, 0);
231
+ break;
232
+ case 'month':
233
+ start.setDate(1);
234
+ start.setHours(0, 0, 0, 0);
235
+ break;
236
+ case 'quarter':
237
+ start.setMonth(Math.floor(start.getMonth() / 3) * 3, 1);
238
+ start.setHours(0, 0, 0, 0);
239
+ break;
240
+ case 'year':
241
+ start.setMonth(0, 1);
242
+ start.setHours(0, 0, 0, 0);
243
+ break;
244
+ }
245
+ return start;
246
+ }
247
+ /**
248
+ * Get previous period start date
249
+ */
250
+ getPreviousPeriodStart(currentStart, period) {
251
+ const prev = new Date(currentStart);
252
+ switch (period) {
253
+ case 'hour':
254
+ prev.setHours(prev.getHours() - 1);
255
+ break;
256
+ case 'day':
257
+ prev.setDate(prev.getDate() - 1);
258
+ break;
259
+ case 'week':
260
+ prev.setDate(prev.getDate() - 7);
261
+ break;
262
+ case 'month':
263
+ prev.setMonth(prev.getMonth() - 1);
264
+ break;
265
+ case 'quarter':
266
+ prev.setMonth(prev.getMonth() - 3);
267
+ break;
268
+ case 'year':
269
+ prev.setFullYear(prev.getFullYear() - 1);
270
+ break;
271
+ }
272
+ return prev;
273
+ }
274
+ /**
275
+ * Calculate average risk from events
276
+ */
277
+ calculateAverageRisk(events) {
278
+ const severityScores = {
279
+ critical: 100,
280
+ high: 75,
281
+ medium: 50,
282
+ low: 25,
283
+ info: 10,
284
+ };
285
+ const total = events.reduce((sum, e) => {
286
+ const severity = e.data.severity;
287
+ return sum + (severityScores[severity] || 0);
288
+ }, 0);
289
+ return Math.round(total / events.length);
290
+ }
291
+ /**
292
+ * Calculate previous period value
293
+ */
294
+ calculatePreviousPeriodValue(type, events) {
295
+ switch (type) {
296
+ case 'vulnerability-count':
297
+ return events.filter(e => e.type === 'vulnerability').length;
298
+ case 'average-risk-score':
299
+ return this.calculateAverageRisk(events.filter(e => e.type === 'vulnerability'));
300
+ case 'fix-rate': {
301
+ const fixed = events.filter(e => e.type === 'fix').length;
302
+ const found = events.filter(e => e.type === 'vulnerability').length;
303
+ return found > 0 ? Math.round((fixed / found) * 100) : 100;
304
+ }
305
+ default:
306
+ return 0;
307
+ }
308
+ }
309
+ /**
310
+ * Calculate mean time to remediate
311
+ */
312
+ calculateMTTR() {
313
+ const fixedVulns = Array.from(this.vulnerabilityHistory.entries())
314
+ .filter(([, record]) => record.fixed);
315
+ if (fixedVulns.length === 0)
316
+ return 0;
317
+ const totalDays = fixedVulns.reduce((sum, [, record]) => {
318
+ const diffTime = (record.fixed.getTime() - record.found.getTime());
319
+ return sum + (diffTime / (1000 * 60 * 60 * 24));
320
+ }, 0);
321
+ return Math.round(totalDays / fixedVulns.length);
322
+ }
323
+ /**
324
+ * Calculate security debt
325
+ */
326
+ calculateSecurityDebt() {
327
+ const openVulns = Array.from(this.vulnerabilityHistory.entries())
328
+ .filter(([, record]) => !record.fixed);
329
+ // Estimate hours to fix based on count (simplified)
330
+ return openVulns.length * 4; // 4 hours per vulnerability average
331
+ }
332
+ /**
333
+ * Calculate trend
334
+ */
335
+ calculateTrend(metricType, dataPoints = 10) {
336
+ const history = this.metricsHistory.get(metricType) || [];
337
+ const recentHistory = history.slice(-dataPoints);
338
+ const points = recentHistory.map(m => ({
339
+ timestamp: m.timestamp,
340
+ value: m.value,
341
+ }));
342
+ // Calculate trend direction using linear regression
343
+ let direction = 'stable';
344
+ let strength = 0;
345
+ let forecast;
346
+ if (points.length >= 2) {
347
+ const regression = this.linearRegression(points.map((p, i) => [i, p.value]));
348
+ const slope = regression.slope;
349
+ if (Math.abs(slope) < 0.5) {
350
+ direction = 'stable';
351
+ }
352
+ else if (metricType === 'vulnerability-count' || metricType === 'average-risk-score') {
353
+ // Lower is better for these metrics
354
+ direction = slope < 0 ? 'improving' : 'declining';
355
+ }
356
+ else {
357
+ // Higher is better for fix-rate, coverage, etc.
358
+ direction = slope > 0 ? 'improving' : 'declining';
359
+ }
360
+ strength = Math.min(1, Math.abs(slope) / 10);
361
+ forecast = Math.round(regression.intercept + regression.slope * points.length);
362
+ }
363
+ // Generate insights
364
+ const insights = this.generateTrendInsights(metricType, direction, points);
365
+ return {
366
+ name: `${metricType} trend`,
367
+ metricType,
368
+ dataPoints: points,
369
+ direction,
370
+ strength,
371
+ forecast,
372
+ insights,
373
+ };
374
+ }
375
+ /**
376
+ * Simple linear regression
377
+ */
378
+ linearRegression(points) {
379
+ const n = points.length;
380
+ if (n === 0)
381
+ return { slope: 0, intercept: 0 };
382
+ let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
383
+ for (const [x, y] of points) {
384
+ sumX += x;
385
+ sumY += y;
386
+ sumXY += x * y;
387
+ sumX2 += x * x;
388
+ }
389
+ const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX) || 0;
390
+ const intercept = (sumY - slope * sumX) / n;
391
+ return { slope, intercept };
392
+ }
393
+ /**
394
+ * Generate trend insights
395
+ */
396
+ generateTrendInsights(metricType, direction, points) {
397
+ const insights = [];
398
+ const latest = points[points.length - 1]?.value ?? 0;
399
+ const benchmark = this.options.industryBenchmark[metricType];
400
+ // Direction insight
401
+ if (direction === 'improving') {
402
+ insights.push(`${metricType} is showing positive improvement`);
403
+ }
404
+ else if (direction === 'declining') {
405
+ insights.push(`${metricType} trend is concerning and needs attention`);
406
+ }
407
+ else {
408
+ insights.push(`${metricType} has remained stable`);
409
+ }
410
+ // Benchmark comparison
411
+ if (benchmark) {
412
+ if (metricType === 'vulnerability-count' || metricType === 'average-risk-score') {
413
+ if (latest < benchmark) {
414
+ insights.push(`Performance is better than industry average (${benchmark})`);
415
+ }
416
+ else if (latest > benchmark * 1.5) {
417
+ insights.push(`Significantly above industry average of ${benchmark}`);
418
+ }
419
+ }
420
+ else {
421
+ if (latest > benchmark) {
422
+ insights.push(`Exceeding industry benchmark of ${benchmark}`);
423
+ }
424
+ else if (latest < benchmark * 0.7) {
425
+ insights.push(`Below industry benchmark of ${benchmark}`);
426
+ }
427
+ }
428
+ }
429
+ return insights;
430
+ }
431
+ /**
432
+ * Get vulnerability statistics
433
+ */
434
+ getVulnerabilityStats(since) {
435
+ const vulnEvents = this.getEventsByType('vulnerability', since);
436
+ const fixEvents = this.getEventsByType('fix', since);
437
+ const bySeverity = {
438
+ critical: 0,
439
+ high: 0,
440
+ medium: 0,
441
+ low: 0,
442
+ info: 0,
443
+ };
444
+ const byType = {};
445
+ const byFile = {};
446
+ for (const event of vulnEvents) {
447
+ const severity = event.data.severity;
448
+ const type = event.data.type;
449
+ const file = event.data.file;
450
+ if (severity in bySeverity) {
451
+ bySeverity[severity]++;
452
+ }
453
+ byType[type] = (byType[type] || 0) + 1;
454
+ byFile[file] = (byFile[file] || 0) + 1;
455
+ }
456
+ // Calculate ages
457
+ const now = new Date();
458
+ const openVulns = Array.from(this.vulnerabilityHistory.entries())
459
+ .filter(([, record]) => !record.fixed);
460
+ let totalAge = 0;
461
+ let oldestAge = 0;
462
+ for (const [, record] of openVulns) {
463
+ const age = (now.getTime() - record.found.getTime()) / (1000 * 60 * 60 * 24);
464
+ totalAge += age;
465
+ oldestAge = Math.max(oldestAge, age);
466
+ }
467
+ return {
468
+ total: vulnEvents.length,
469
+ bySeverity,
470
+ byType,
471
+ byFile,
472
+ newInPeriod: vulnEvents.length,
473
+ fixedInPeriod: fixEvents.length,
474
+ averageAge: openVulns.length > 0 ? Math.round(totalAge / openVulns.length) : 0,
475
+ oldestAge: Math.round(oldestAge),
476
+ };
477
+ }
478
+ /**
479
+ * Generate security scorecard
480
+ */
481
+ generateScorecard() {
482
+ const categoryScores = [];
483
+ // Vulnerability Management Score
484
+ const vulnCount = this.calculateMetric('vulnerability-count', 'month');
485
+ const vulnScore = Math.max(0, 100 - vulnCount.value * 2);
486
+ categoryScores.push({
487
+ category: 'Vulnerability Management',
488
+ score: vulnScore,
489
+ weight: 0.3,
490
+ });
491
+ // Remediation Velocity Score
492
+ const mttr = this.calculateMetric('mean-time-to-remediate', 'month');
493
+ const remediationScore = Math.max(0, 100 - mttr.value * 3);
494
+ categoryScores.push({
495
+ category: 'Remediation Velocity',
496
+ score: remediationScore,
497
+ weight: 0.25,
498
+ });
499
+ // Fix Rate Score
500
+ const fixRate = this.calculateMetric('fix-rate', 'month');
501
+ categoryScores.push({
502
+ category: 'Fix Rate',
503
+ score: fixRate.value,
504
+ weight: 0.25,
505
+ });
506
+ // Detection Coverage Score
507
+ const coverage = this.calculateMetric('coverage', 'month');
508
+ const coverageScore = Math.min(100, coverage.value);
509
+ categoryScores.push({
510
+ category: 'Detection Coverage',
511
+ score: coverageScore,
512
+ weight: 0.2,
513
+ });
514
+ // Calculate overall score
515
+ const overallScore = Math.round(categoryScores.reduce((sum, cat) => sum + cat.score * cat.weight, 0));
516
+ // Determine grade
517
+ const grade = this.scoreToGrade(overallScore);
518
+ // Key findings
519
+ const keyFindings = [];
520
+ if (vulnCount.value > 10) {
521
+ keyFindings.push(`${vulnCount.value} vulnerabilities found this month`);
522
+ }
523
+ if (mttr.value > 7) {
524
+ keyFindings.push(`Average remediation time is ${mttr.value} days`);
525
+ }
526
+ // Improvements
527
+ const improvements = [];
528
+ for (const cat of categoryScores) {
529
+ if (cat.score < 50) {
530
+ improvements.push(`Improve ${cat.category} (current score: ${cat.score})`);
531
+ }
532
+ }
533
+ // Industry comparison
534
+ const industryComparison = {
535
+ percentile: Math.round(overallScore * 0.9), // Simplified
536
+ average: 55,
537
+ };
538
+ // Get historical scores
539
+ const history = this.getHistoricalScores();
540
+ return {
541
+ overallScore,
542
+ grade,
543
+ categoryScores,
544
+ keyFindings,
545
+ improvements,
546
+ industryComparison,
547
+ history,
548
+ };
549
+ }
550
+ /**
551
+ * Convert score to grade
552
+ */
553
+ scoreToGrade(score) {
554
+ if (score >= 90)
555
+ return 'A';
556
+ if (score >= 80)
557
+ return 'B';
558
+ if (score >= 70)
559
+ return 'C';
560
+ if (score >= 60)
561
+ return 'D';
562
+ return 'F';
563
+ }
564
+ /**
565
+ * Get historical scores
566
+ */
567
+ getHistoricalScores() {
568
+ // Simplified - would aggregate from metricsHistory
569
+ const history = [];
570
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
571
+ for (let i = 0; i < months.length; i++) {
572
+ history.push({
573
+ period: months[i],
574
+ score: Math.round(60 + Math.random() * 30),
575
+ });
576
+ }
577
+ return history;
578
+ }
579
+ /**
580
+ * Generate dashboard data
581
+ */
582
+ generateDashboard() {
583
+ const vulnStats = this.getVulnerabilityStats();
584
+ const scorecard = this.generateScorecard();
585
+ // Calculate metrics
586
+ const metrics = [
587
+ this.calculateMetric('vulnerability-count', 'day'),
588
+ this.calculateMetric('average-risk-score', 'day'),
589
+ this.calculateMetric('fix-rate', 'week'),
590
+ this.calculateMetric('mean-time-to-remediate', 'month'),
591
+ ];
592
+ // Calculate trends
593
+ const trends = [
594
+ this.calculateTrend('vulnerability-count'),
595
+ this.calculateTrend('average-risk-score'),
596
+ ];
597
+ // Get recent activity
598
+ const recentActivity = this.events
599
+ .slice(-10)
600
+ .map(e => ({
601
+ timestamp: e.timestamp,
602
+ type: e.type,
603
+ description: this.formatEventDescription(e),
604
+ }));
605
+ // Get top risks (simulated)
606
+ const topRisks = this.getEventsByType('vulnerability')
607
+ .filter(e => e.data.severity === 'critical' || e.data.severity === 'high')
608
+ .slice(-5)
609
+ .map(e => ({
610
+ vulnerability: {
611
+ id: e.data.vulnerabilityId,
612
+ type: e.data.type,
613
+ severity: e.data.severity,
614
+ description: `${e.data.type} vulnerability`,
615
+ recommendation: 'Review and fix the vulnerability',
616
+ confidence: 0.9,
617
+ ruleId: 'analytics',
618
+ detectedAt: e.timestamp,
619
+ message: `${e.data.type} vulnerability`,
620
+ location: {
621
+ file: e.data.file,
622
+ startLine: 1,
623
+ endLine: 1,
624
+ startColumn: 0,
625
+ endColumn: 0,
626
+ },
627
+ cwes: [],
628
+ fix: { description: '', code: '' },
629
+ },
630
+ riskScore: e.data.severity === 'critical' ? 95 : 75,
631
+ daysOpen: Math.round((new Date().getTime() - e.timestamp.getTime()) / (1000 * 60 * 60 * 24)),
632
+ }));
633
+ // Determine trend
634
+ const vulnTrend = trends.find(t => t.metricType === 'vulnerability-count');
635
+ const trend = vulnTrend?.direction || 'stable';
636
+ return {
637
+ summary: {
638
+ totalVulnerabilities: vulnStats.total,
639
+ criticalCount: vulnStats.bySeverity.critical,
640
+ overallRisk: metrics.find(m => m.type === 'average-risk-score')?.value || 0,
641
+ securityScore: scorecard.overallScore,
642
+ trend,
643
+ },
644
+ recentActivity,
645
+ topRisks,
646
+ metrics,
647
+ trends,
648
+ scorecard,
649
+ generatedAt: new Date(),
650
+ };
651
+ }
652
+ /**
653
+ * Format event description
654
+ */
655
+ formatEventDescription(event) {
656
+ switch (event.type) {
657
+ case 'vulnerability':
658
+ return `Found ${event.data.severity} severity ${event.data.type} vulnerability`;
659
+ case 'fix':
660
+ return `Fixed vulnerability ${event.data.vulnerabilityId}`;
661
+ case 'scan':
662
+ return `Scan completed: ${event.data.filesScanned} files, ${event.data.vulnerabilitiesFound} issues`;
663
+ default:
664
+ return `Event: ${event.type}`;
665
+ }
666
+ }
667
+ /**
668
+ * Generate analytics report
669
+ */
670
+ generateReport(startDate, endDate) {
671
+ const vulnStats = this.getVulnerabilityStats(startDate);
672
+ const scorecard = this.generateScorecard();
673
+ // Calculate key metrics for period
674
+ const keyMetrics = [
675
+ this.calculateMetric('vulnerability-count', 'month'),
676
+ this.calculateMetric('average-risk-score', 'month'),
677
+ this.calculateMetric('fix-rate', 'month'),
678
+ this.calculateMetric('mean-time-to-remediate', 'month'),
679
+ this.calculateMetric('security-debt', 'month'),
680
+ ];
681
+ // Generate executive summary
682
+ const executiveSummary = this.generateExecutiveSummary(vulnStats, scorecard);
683
+ // Generate recommendations
684
+ const recommendations = this.generateReportRecommendations(vulnStats, scorecard);
685
+ return {
686
+ id: `RPT-${Date.now()}`,
687
+ title: 'Security Analytics Report',
688
+ period: { start: startDate, end: endDate },
689
+ executiveSummary,
690
+ keyMetrics,
691
+ trends: [
692
+ this.calculateTrend('vulnerability-count'),
693
+ this.calculateTrend('average-risk-score'),
694
+ this.calculateTrend('fix-rate'),
695
+ ],
696
+ vulnerabilityStats: vulnStats,
697
+ riskSummary: {
698
+ totalVulnerabilities: vulnStats.total,
699
+ averageRiskScore: keyMetrics.find(m => m.type === 'average-risk-score')?.value || 0,
700
+ distribution: {
701
+ critical: vulnStats.bySeverity.critical,
702
+ high: vulnStats.bySeverity.high,
703
+ medium: vulnStats.bySeverity.medium,
704
+ low: vulnStats.bySeverity.low,
705
+ informational: vulnStats.bySeverity.info,
706
+ },
707
+ topRisks: [],
708
+ totalBusinessImpact: [],
709
+ securityPosture: scorecard.overallScore,
710
+ trend: 'stable',
711
+ },
712
+ recommendations,
713
+ generatedAt: new Date(),
714
+ };
715
+ }
716
+ /**
717
+ * Generate executive summary
718
+ */
719
+ generateExecutiveSummary(stats, scorecard) {
720
+ const parts = [];
721
+ parts.push(`Security Score: ${scorecard.overallScore}/100 (Grade: ${scorecard.grade})`);
722
+ parts.push(`Total vulnerabilities identified: ${stats.total}`);
723
+ parts.push(`Critical/High severity: ${stats.bySeverity.critical + stats.bySeverity.high}`);
724
+ if (stats.fixedInPeriod > 0) {
725
+ parts.push(`Vulnerabilities fixed this period: ${stats.fixedInPeriod}`);
726
+ }
727
+ if (stats.averageAge > 7) {
728
+ parts.push(`Average vulnerability age: ${stats.averageAge} days (attention needed)`);
729
+ }
730
+ return parts.join('. ') + '.';
731
+ }
732
+ /**
733
+ * Generate report recommendations
734
+ */
735
+ generateReportRecommendations(stats, scorecard) {
736
+ const recommendations = [];
737
+ if (stats.bySeverity.critical > 0) {
738
+ recommendations.push({
739
+ priority: 'high',
740
+ description: `Address ${stats.bySeverity.critical} critical vulnerabilities immediately`,
741
+ impact: 'Reduces critical risk exposure',
742
+ });
743
+ }
744
+ if (stats.averageAge > 14) {
745
+ recommendations.push({
746
+ priority: 'high',
747
+ description: 'Improve vulnerability remediation velocity',
748
+ impact: 'Reduces exposure window',
749
+ });
750
+ }
751
+ for (const cat of scorecard.categoryScores.filter(c => c.score < 60)) {
752
+ recommendations.push({
753
+ priority: cat.score < 40 ? 'high' : 'medium',
754
+ description: `Improve ${cat.category}`,
755
+ impact: `Current score: ${cat.score}/100`,
756
+ });
757
+ }
758
+ if (recommendations.length === 0) {
759
+ recommendations.push({
760
+ priority: 'low',
761
+ description: 'Continue current security practices',
762
+ impact: 'Maintain security posture',
763
+ });
764
+ }
765
+ return recommendations;
766
+ }
767
+ /**
768
+ * Get all metrics
769
+ */
770
+ getAllMetrics(period = 'day') {
771
+ const metricTypes = [
772
+ 'vulnerability-count',
773
+ 'average-risk-score',
774
+ 'mean-time-to-remediate',
775
+ 'security-debt',
776
+ 'fix-rate',
777
+ 'detection-rate',
778
+ 'false-positive-rate',
779
+ 'coverage',
780
+ ];
781
+ return metricTypes.map(type => this.calculateMetric(type, period));
782
+ }
783
+ /**
784
+ * Export analytics data
785
+ */
786
+ exportData() {
787
+ return {
788
+ events: this.events,
789
+ metricsHistory: Object.fromEntries(this.metricsHistory),
790
+ vulnerabilityHistory: Object.fromEntries(this.vulnerabilityHistory),
791
+ };
792
+ }
793
+ }
794
+ // ============================================================================
795
+ // Factory Functions
796
+ // ============================================================================
797
+ /**
798
+ * Create a SecurityAnalytics instance
799
+ */
800
+ export function createSecurityAnalytics(options) {
801
+ return new SecurityAnalytics(options);
802
+ }
803
+ /**
804
+ * Quick dashboard generation
805
+ */
806
+ export function generateDashboard(events) {
807
+ const analytics = createSecurityAnalytics();
808
+ for (const event of events) {
809
+ analytics.recordEvent(event);
810
+ }
811
+ return analytics.generateDashboard();
812
+ }
813
+ //# sourceMappingURL=security-analytics.js.map