@vibecheckai/cli 3.4.0 → 3.5.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 (228) hide show
  1. package/bin/registry.js +154 -338
  2. package/bin/runners/context/generators/mcp.js +13 -15
  3. package/bin/runners/context/proof-context.js +1 -248
  4. package/bin/runners/lib/analysis-core.js +180 -198
  5. package/bin/runners/lib/analyzers.js +223 -1669
  6. package/bin/runners/lib/cli-output.js +210 -242
  7. package/bin/runners/lib/detectors-v2.js +785 -547
  8. package/bin/runners/lib/entitlements-v2.js +458 -96
  9. package/bin/runners/lib/error-handler.js +9 -16
  10. package/bin/runners/lib/global-flags.js +0 -37
  11. package/bin/runners/lib/route-truth.js +322 -1167
  12. package/bin/runners/lib/scan-output.js +469 -448
  13. package/bin/runners/lib/ship-output.js +27 -280
  14. package/bin/runners/lib/terminal-ui.js +733 -231
  15. package/bin/runners/lib/truth.js +321 -1004
  16. package/bin/runners/lib/unified-output.js +158 -162
  17. package/bin/runners/lib/upsell.js +204 -104
  18. package/bin/runners/runAllowlist.js +324 -0
  19. package/bin/runners/runAuth.js +95 -324
  20. package/bin/runners/runCheckpoint.js +21 -39
  21. package/bin/runners/runContext.js +24 -136
  22. package/bin/runners/runDoctor.js +67 -115
  23. package/bin/runners/runEvidencePack.js +219 -0
  24. package/bin/runners/runFix.js +5 -6
  25. package/bin/runners/runGuard.js +118 -212
  26. package/bin/runners/runInit.js +2 -14
  27. package/bin/runners/runInstall.js +281 -0
  28. package/bin/runners/runLabs.js +341 -0
  29. package/bin/runners/runMcp.js +52 -130
  30. package/bin/runners/runPolish.js +20 -43
  31. package/bin/runners/runProve.js +3 -13
  32. package/bin/runners/runReality.js +0 -14
  33. package/bin/runners/runReport.js +2 -3
  34. package/bin/runners/runScan.js +44 -511
  35. package/bin/runners/runShip.js +14 -28
  36. package/bin/runners/runValidate.js +2 -19
  37. package/bin/runners/runWatch.js +54 -118
  38. package/bin/vibecheck.js +41 -148
  39. package/mcp-server/ARCHITECTURE.md +339 -0
  40. package/mcp-server/__tests__/cache.test.ts +313 -0
  41. package/mcp-server/__tests__/executor.test.ts +239 -0
  42. package/mcp-server/__tests__/fixtures/exclusion-test/.cache/webpack/cache.pack +1 -0
  43. package/mcp-server/__tests__/fixtures/exclusion-test/.next/server/chunk.js +3 -0
  44. package/mcp-server/__tests__/fixtures/exclusion-test/.turbo/cache.json +3 -0
  45. package/mcp-server/__tests__/fixtures/exclusion-test/.venv/lib/env.py +3 -0
  46. package/mcp-server/__tests__/fixtures/exclusion-test/dist/bundle.js +3 -0
  47. package/mcp-server/__tests__/fixtures/exclusion-test/package.json +5 -0
  48. package/mcp-server/__tests__/fixtures/exclusion-test/src/app.ts +5 -0
  49. package/mcp-server/__tests__/fixtures/exclusion-test/venv/lib/config.py +4 -0
  50. package/mcp-server/__tests__/ids.test.ts +345 -0
  51. package/mcp-server/__tests__/integration/tools.test.ts +410 -0
  52. package/mcp-server/__tests__/registry.test.ts +365 -0
  53. package/mcp-server/__tests__/sandbox.test.ts +323 -0
  54. package/mcp-server/__tests__/schemas.test.ts +372 -0
  55. package/mcp-server/benchmarks/run-benchmarks.ts +304 -0
  56. package/mcp-server/examples/doctor.request.json +14 -0
  57. package/mcp-server/examples/doctor.response.json +53 -0
  58. package/mcp-server/examples/error.response.json +15 -0
  59. package/mcp-server/examples/scan.request.json +14 -0
  60. package/mcp-server/examples/scan.response.json +108 -0
  61. package/mcp-server/handlers/tool-handler.ts +671 -0
  62. package/mcp-server/index-v3.ts +293 -0
  63. package/mcp-server/index.js +1072 -1573
  64. package/mcp-server/index.old.js +4137 -0
  65. package/mcp-server/lib/cache.ts +341 -0
  66. package/mcp-server/lib/errors.ts +346 -0
  67. package/mcp-server/lib/executor.ts +792 -0
  68. package/mcp-server/lib/ids.ts +238 -0
  69. package/mcp-server/lib/logger.ts +368 -0
  70. package/mcp-server/lib/metrics.ts +365 -0
  71. package/mcp-server/lib/sandbox.ts +337 -0
  72. package/mcp-server/lib/validator.ts +229 -0
  73. package/mcp-server/package-lock.json +165 -0
  74. package/mcp-server/package.json +32 -7
  75. package/mcp-server/premium-tools.js +2 -2
  76. package/mcp-server/registry/tools.json +476 -0
  77. package/mcp-server/schemas/error-envelope.schema.json +125 -0
  78. package/mcp-server/schemas/finding.schema.json +167 -0
  79. package/mcp-server/schemas/report-artifact.schema.json +88 -0
  80. package/mcp-server/schemas/run-request.schema.json +75 -0
  81. package/mcp-server/schemas/verdict.schema.json +168 -0
  82. package/mcp-server/tier-auth.d.ts +71 -0
  83. package/mcp-server/tier-auth.js +371 -183
  84. package/mcp-server/truth-context.js +90 -131
  85. package/mcp-server/truth-firewall-tools.js +1000 -1611
  86. package/mcp-server/tsconfig.json +34 -0
  87. package/mcp-server/vibecheck-tools.js +2 -2
  88. package/mcp-server/vitest.config.ts +16 -0
  89. package/package.json +3 -4
  90. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +0 -474
  91. package/bin/runners/lib/agent-firewall/change-packet/builder.js +0 -488
  92. package/bin/runners/lib/agent-firewall/change-packet/schema.json +0 -228
  93. package/bin/runners/lib/agent-firewall/change-packet/store.js +0 -200
  94. package/bin/runners/lib/agent-firewall/claims/claim-types.js +0 -21
  95. package/bin/runners/lib/agent-firewall/claims/extractor.js +0 -303
  96. package/bin/runners/lib/agent-firewall/claims/patterns.js +0 -24
  97. package/bin/runners/lib/agent-firewall/critic/index.js +0 -151
  98. package/bin/runners/lib/agent-firewall/critic/judge.js +0 -432
  99. package/bin/runners/lib/agent-firewall/critic/prompts.js +0 -305
  100. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +0 -88
  101. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +0 -75
  102. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +0 -127
  103. package/bin/runners/lib/agent-firewall/evidence/resolver.js +0 -102
  104. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +0 -213
  105. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +0 -145
  106. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +0 -19
  107. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +0 -87
  108. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +0 -184
  109. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +0 -163
  110. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +0 -107
  111. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +0 -68
  112. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +0 -66
  113. package/bin/runners/lib/agent-firewall/interceptor/base.js +0 -304
  114. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +0 -35
  115. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +0 -35
  116. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +0 -34
  117. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +0 -465
  118. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +0 -604
  119. package/bin/runners/lib/agent-firewall/lawbook/index.js +0 -304
  120. package/bin/runners/lib/agent-firewall/lawbook/registry.js +0 -514
  121. package/bin/runners/lib/agent-firewall/lawbook/schema.js +0 -420
  122. package/bin/runners/lib/agent-firewall/logger.js +0 -141
  123. package/bin/runners/lib/agent-firewall/policy/default-policy.json +0 -90
  124. package/bin/runners/lib/agent-firewall/policy/engine.js +0 -103
  125. package/bin/runners/lib/agent-firewall/policy/loader.js +0 -451
  126. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +0 -50
  127. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +0 -50
  128. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +0 -86
  129. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +0 -162
  130. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +0 -189
  131. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +0 -93
  132. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +0 -57
  133. package/bin/runners/lib/agent-firewall/policy/schema.json +0 -183
  134. package/bin/runners/lib/agent-firewall/policy/verdict.js +0 -54
  135. package/bin/runners/lib/agent-firewall/proposal/extractor.js +0 -394
  136. package/bin/runners/lib/agent-firewall/proposal/index.js +0 -212
  137. package/bin/runners/lib/agent-firewall/proposal/schema.js +0 -251
  138. package/bin/runners/lib/agent-firewall/proposal/validator.js +0 -386
  139. package/bin/runners/lib/agent-firewall/reality/index.js +0 -332
  140. package/bin/runners/lib/agent-firewall/reality/state.js +0 -625
  141. package/bin/runners/lib/agent-firewall/reality/watcher.js +0 -322
  142. package/bin/runners/lib/agent-firewall/risk/index.js +0 -173
  143. package/bin/runners/lib/agent-firewall/risk/scorer.js +0 -328
  144. package/bin/runners/lib/agent-firewall/risk/thresholds.js +0 -321
  145. package/bin/runners/lib/agent-firewall/risk/vectors.js +0 -421
  146. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +0 -472
  147. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +0 -346
  148. package/bin/runners/lib/agent-firewall/simulator/index.js +0 -181
  149. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +0 -380
  150. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +0 -661
  151. package/bin/runners/lib/agent-firewall/time-machine/index.js +0 -267
  152. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +0 -436
  153. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +0 -490
  154. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +0 -530
  155. package/bin/runners/lib/agent-firewall/truthpack/index.js +0 -67
  156. package/bin/runners/lib/agent-firewall/truthpack/loader.js +0 -137
  157. package/bin/runners/lib/agent-firewall/unblock/planner.js +0 -337
  158. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +0 -118
  159. package/bin/runners/lib/api-client.js +0 -269
  160. package/bin/runners/lib/authority-badge.js +0 -425
  161. package/bin/runners/lib/engines/accessibility-engine.js +0 -190
  162. package/bin/runners/lib/engines/api-consistency-engine.js +0 -162
  163. package/bin/runners/lib/engines/ast-cache.js +0 -99
  164. package/bin/runners/lib/engines/code-quality-engine.js +0 -255
  165. package/bin/runners/lib/engines/console-logs-engine.js +0 -115
  166. package/bin/runners/lib/engines/cross-file-analysis-engine.js +0 -268
  167. package/bin/runners/lib/engines/dead-code-engine.js +0 -198
  168. package/bin/runners/lib/engines/deprecated-api-engine.js +0 -226
  169. package/bin/runners/lib/engines/empty-catch-engine.js +0 -150
  170. package/bin/runners/lib/engines/file-filter.js +0 -131
  171. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +0 -251
  172. package/bin/runners/lib/engines/mock-data-engine.js +0 -272
  173. package/bin/runners/lib/engines/parallel-processor.js +0 -71
  174. package/bin/runners/lib/engines/performance-issues-engine.js +0 -265
  175. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +0 -243
  176. package/bin/runners/lib/engines/todo-fixme-engine.js +0 -115
  177. package/bin/runners/lib/engines/type-aware-engine.js +0 -152
  178. package/bin/runners/lib/engines/unsafe-regex-engine.js +0 -225
  179. package/bin/runners/lib/engines/vibecheck-engines/README.md +0 -53
  180. package/bin/runners/lib/engines/vibecheck-engines/index.js +0 -15
  181. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
  182. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
  183. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
  184. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
  185. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
  186. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
  187. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
  188. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +0 -139
  189. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
  190. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
  191. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
  192. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
  193. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
  194. package/bin/runners/lib/engines/vibecheck-engines/package.json +0 -13
  195. package/bin/runners/lib/exit-codes.js +0 -275
  196. package/bin/runners/lib/fingerprint.js +0 -377
  197. package/bin/runners/lib/help-formatter.js +0 -413
  198. package/bin/runners/lib/logger.js +0 -38
  199. package/bin/runners/lib/ship-output-enterprise.js +0 -239
  200. package/bin/runners/lib/unified-cli-output.js +0 -604
  201. package/bin/runners/runAgent.d.ts +0 -5
  202. package/bin/runners/runAgent.js +0 -161
  203. package/bin/runners/runApprove.js +0 -1200
  204. package/bin/runners/runClassify.js +0 -859
  205. package/bin/runners/runContext.d.ts +0 -4
  206. package/bin/runners/runFirewall.d.ts +0 -5
  207. package/bin/runners/runFirewall.js +0 -134
  208. package/bin/runners/runFirewallHook.d.ts +0 -5
  209. package/bin/runners/runFirewallHook.js +0 -56
  210. package/bin/runners/runPolish.d.ts +0 -4
  211. package/bin/runners/runProof.zip +0 -0
  212. package/bin/runners/runTruth.d.ts +0 -5
  213. package/bin/runners/runTruth.js +0 -101
  214. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  215. package/mcp-server/agent-firewall-interceptor.js +0 -500
  216. package/mcp-server/authority-tools.js +0 -569
  217. package/mcp-server/conductor/conflict-resolver.js +0 -588
  218. package/mcp-server/conductor/execution-planner.js +0 -544
  219. package/mcp-server/conductor/index.js +0 -377
  220. package/mcp-server/conductor/lock-manager.js +0 -615
  221. package/mcp-server/conductor/request-queue.js +0 -550
  222. package/mcp-server/conductor/session-manager.js +0 -500
  223. package/mcp-server/conductor/tools.js +0 -510
  224. package/mcp-server/lib/api-client.cjs +0 -13
  225. package/mcp-server/lib/logger.cjs +0 -30
  226. package/mcp-server/logger.js +0 -173
  227. package/mcp-server/tools-v3.js +0 -706
  228. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
@@ -1,661 +0,0 @@
1
- /**
2
- * Time Machine Incident Correlator
3
- *
4
- * Links incidents to their causal chain.
5
- * Finds the "first bad change" that led to problems.
6
- *
7
- * Codename: Time Machine
8
- */
9
-
10
- "use strict";
11
-
12
- const fs = require("fs");
13
- const path = require("path");
14
-
15
- /**
16
- * @typedef {Object} Incident
17
- * @property {string} id - Incident ID
18
- * @property {string} title - Incident title
19
- * @property {string} description - Incident description
20
- * @property {Date} reportedAt - When reported
21
- * @property {string} severity - Incident severity
22
- * @property {string[]} affectedFiles - Files affected
23
- * @property {string[]} affectedServices - Services affected
24
- * @property {string} status - open, investigating, resolved
25
- */
26
-
27
- /**
28
- * @typedef {Object} CausalLink
29
- * @property {string} fromEvent - Source event ID
30
- * @property {string} toEvent - Target event ID
31
- * @property {string} relationship - Type of causal relationship
32
- * @property {number} confidence - Confidence score (0-1)
33
- * @property {string[]} evidence - Evidence supporting the link
34
- */
35
-
36
- /**
37
- * @typedef {Object} IncidentReport
38
- * @property {Incident} incident - The incident
39
- * @property {Object[]} causalChain - Chain of events leading to incident
40
- * @property {Object} rootCause - Identified root cause
41
- * @property {Object[]} contributingFactors - Other contributing events
42
- * @property {Object} recommendations - Recommendations to prevent recurrence
43
- * @property {Date} generatedAt - When report was generated
44
- */
45
-
46
- /**
47
- * Relationship types for causal links
48
- */
49
- const RELATIONSHIP_TYPES = {
50
- DIRECT_CAUSE: "direct_cause", // A directly caused B
51
- CONTRIBUTING: "contributing", // A contributed to B
52
- CORRELATING: "correlating", // A and B happened together
53
- OVERRIDE_LED_TO: "override_led_to", // Override led to issue
54
- HIGH_RISK_CHANGE: "high_risk_change", // High risk change preceded issue
55
- };
56
-
57
- /**
58
- * Incident Correlator class
59
- */
60
- class IncidentCorrelator {
61
- constructor(options = {}) {
62
- this.projectRoot = options.projectRoot || process.cwd();
63
- this.incidentsDir = path.join(this.projectRoot, ".vibecheck", "incidents");
64
- this.packetsDir = path.join(this.projectRoot, ".vibecheck", "packets");
65
- this.auditDir = path.join(this.projectRoot, ".vibecheck", "audit");
66
-
67
- // Correlation settings
68
- this.lookbackWindow = options.lookbackWindow || 24 * 60 * 60 * 1000; // 24 hours
69
- this.minConfidence = options.minConfidence || 0.5;
70
- }
71
-
72
- /**
73
- * Record a new incident
74
- * @param {Object} incidentData - Incident data
75
- * @returns {Incident} Created incident
76
- */
77
- recordIncident(incidentData) {
78
- const incident = {
79
- id: incidentData.id || `INC-${Date.now()}-${Math.random().toString(36).slice(2, 6).toUpperCase()}`,
80
- title: incidentData.title || "Unnamed incident",
81
- description: incidentData.description || "",
82
- reportedAt: new Date().toISOString(),
83
- severity: incidentData.severity || "medium",
84
- affectedFiles: incidentData.affectedFiles || [],
85
- affectedServices: incidentData.affectedServices || [],
86
- status: "open",
87
- metadata: incidentData.metadata || {},
88
- };
89
-
90
- // Save incident
91
- this.saveIncident(incident);
92
-
93
- return incident;
94
- }
95
-
96
- /**
97
- * Save an incident to disk
98
- * @param {Incident} incident - Incident to save
99
- */
100
- saveIncident(incident) {
101
- if (!fs.existsSync(this.incidentsDir)) {
102
- fs.mkdirSync(this.incidentsDir, { recursive: true });
103
- }
104
-
105
- // Save as individual file
106
- fs.writeFileSync(
107
- path.join(this.incidentsDir, `${incident.id}.json`),
108
- JSON.stringify(incident, null, 2)
109
- );
110
-
111
- // Also append to incidents log
112
- const logPath = path.join(this.incidentsDir, "incidents.jsonl");
113
- fs.appendFileSync(logPath, JSON.stringify(incident) + "\n");
114
- }
115
-
116
- /**
117
- * Load an incident by ID
118
- * @param {string} incidentId - Incident ID
119
- * @returns {Incident|null} Incident or null
120
- */
121
- loadIncident(incidentId) {
122
- const incidentPath = path.join(this.incidentsDir, `${incidentId}.json`);
123
-
124
- if (fs.existsSync(incidentPath)) {
125
- try {
126
- return JSON.parse(fs.readFileSync(incidentPath, "utf-8"));
127
- } catch {
128
- return null;
129
- }
130
- }
131
-
132
- return null;
133
- }
134
-
135
- /**
136
- * Correlate an incident with firewall events
137
- * @param {string} incidentId - Incident ID
138
- * @param {Object} options - Correlation options
139
- * @returns {IncidentReport} Incident report
140
- */
141
- async correlateIncident(incidentId, options = {}) {
142
- const incident = this.loadIncident(incidentId);
143
- if (!incident) {
144
- throw new Error(`Incident ${incidentId} not found`);
145
- }
146
-
147
- const lookback = options.lookbackWindow || this.lookbackWindow;
148
- const incidentTime = new Date(incident.reportedAt);
149
- const lookbackTime = new Date(incidentTime.getTime() - lookback);
150
-
151
- // Load events in the lookback window
152
- const events = await this.loadEventsInWindow(lookbackTime, incidentTime);
153
-
154
- // Filter to events affecting the same files/services
155
- const relevantEvents = this.filterRelevantEvents(events, incident);
156
-
157
- // Build causal chain
158
- const causalChain = this.buildCausalChain(relevantEvents, incident);
159
-
160
- // Identify root cause
161
- const rootCause = this.identifyRootCause(causalChain, relevantEvents);
162
-
163
- // Find contributing factors
164
- const contributingFactors = this.findContributingFactors(relevantEvents, rootCause);
165
-
166
- // Generate recommendations
167
- const recommendations = this.generateRecommendations(incident, rootCause, contributingFactors);
168
-
169
- const report = {
170
- incident,
171
- causalChain,
172
- rootCause,
173
- contributingFactors,
174
- recommendations,
175
- eventsAnalyzed: events.length,
176
- relevantEvents: relevantEvents.length,
177
- generatedAt: new Date().toISOString(),
178
- };
179
-
180
- // Save report
181
- this.saveReport(incidentId, report);
182
-
183
- return report;
184
- }
185
-
186
- /**
187
- * Load events in a time window
188
- * @param {Date} start - Start time
189
- * @param {Date} end - End time
190
- * @returns {Object[]} Events
191
- */
192
- async loadEventsInWindow(start, end) {
193
- const events = [];
194
-
195
- // Load from firewall audit
196
- const firewallLog = path.join(this.auditDir, "firewall-events.jsonl");
197
- if (fs.existsSync(firewallLog)) {
198
- const content = fs.readFileSync(firewallLog, "utf-8");
199
- const lines = content.trim().split("\n").filter(l => l);
200
-
201
- for (const line of lines) {
202
- try {
203
- const event = JSON.parse(line);
204
- const eventTime = new Date(event.timestamp);
205
-
206
- if (eventTime >= start && eventTime <= end) {
207
- events.push({
208
- ...event,
209
- source: "firewall",
210
- });
211
- }
212
- } catch {
213
- // Skip invalid lines
214
- }
215
- }
216
- }
217
-
218
- // Load from change packets
219
- if (fs.existsSync(this.packetsDir)) {
220
- const packets = fs.readdirSync(this.packetsDir)
221
- .filter(f => f.endsWith(".json"));
222
-
223
- for (const packetFile of packets) {
224
- try {
225
- const packet = JSON.parse(
226
- fs.readFileSync(path.join(this.packetsDir, packetFile), "utf-8")
227
- );
228
-
229
- const packetTime = new Date(packet.timestamp || packet.createdAt);
230
-
231
- if (packetTime >= start && packetTime <= end) {
232
- events.push({
233
- ...packet,
234
- source: "packet",
235
- });
236
- }
237
- } catch {
238
- // Skip invalid packets
239
- }
240
- }
241
- }
242
-
243
- // Sort by timestamp
244
- events.sort((a, b) =>
245
- new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
246
- );
247
-
248
- return events;
249
- }
250
-
251
- /**
252
- * Filter events relevant to an incident
253
- * @param {Object[]} events - All events
254
- * @param {Incident} incident - The incident
255
- * @returns {Object[]} Relevant events
256
- */
257
- filterRelevantEvents(events, incident) {
258
- const affectedFiles = new Set(incident.affectedFiles || []);
259
- const affectedServices = new Set(incident.affectedServices || []);
260
-
261
- return events.filter(event => {
262
- // Check file match
263
- const eventFiles = this.extractFilesFromEvent(event);
264
- const hasFileMatch = eventFiles.some(f => {
265
- for (const affected of affectedFiles) {
266
- if (f.includes(affected) || affected.includes(f)) {
267
- return true;
268
- }
269
- }
270
- return false;
271
- });
272
-
273
- if (hasFileMatch) return true;
274
-
275
- // Check service match
276
- const eventServices = event.services || [];
277
- const hasServiceMatch = eventServices.some(s => affectedServices.has(s));
278
-
279
- if (hasServiceMatch) return true;
280
-
281
- // Include high-risk events
282
- if ((event.riskScore || 0) >= 70) return true;
283
-
284
- // Include overrides
285
- if (event.overrideUsed || event.proof?.overrideUsed) return true;
286
-
287
- return false;
288
- });
289
- }
290
-
291
- /**
292
- * Extract files from an event
293
- * @param {Object} event - Event
294
- * @returns {string[]} Files
295
- */
296
- extractFilesFromEvent(event) {
297
- const files = [];
298
-
299
- if (event.file) files.push(event.file);
300
- if (event.filePath) files.push(event.filePath);
301
-
302
- for (const op of event.operations || []) {
303
- if (op.path) files.push(op.path);
304
- if (op.file) files.push(op.file);
305
- }
306
-
307
- return files;
308
- }
309
-
310
- /**
311
- * Build causal chain from events to incident
312
- * @param {Object[]} events - Relevant events
313
- * @param {Incident} incident - The incident
314
- * @returns {CausalLink[]} Causal chain
315
- */
316
- buildCausalChain(events, incident) {
317
- const chain = [];
318
-
319
- // Sort events by time (newest last)
320
- const sorted = [...events].sort((a, b) =>
321
- new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
322
- );
323
-
324
- // Find events that could have caused the incident
325
- for (const event of sorted) {
326
- const confidence = this.calculateCausalConfidence(event, incident);
327
-
328
- if (confidence >= this.minConfidence) {
329
- const link = {
330
- fromEvent: event.id || event.changeId,
331
- toEvent: incident.id,
332
- relationship: this.determineRelationship(event, incident),
333
- confidence,
334
- evidence: this.gatherEvidence(event, incident),
335
- timestamp: event.timestamp,
336
- };
337
-
338
- chain.push(link);
339
- }
340
- }
341
-
342
- // Link events to each other in the chain
343
- for (let i = 0; i < chain.length - 1; i++) {
344
- // Check if earlier events caused later events
345
- const earlier = events.find(e => e.id === chain[i].fromEvent);
346
- const later = events.find(e => e.id === chain[i + 1].fromEvent);
347
-
348
- if (earlier && later && this.eventsCausally Related(earlier, later)) {
349
- chain.splice(i + 1, 0, {
350
- fromEvent: chain[i].fromEvent,
351
- toEvent: chain[i + 1].fromEvent,
352
- relationship: RELATIONSHIP_TYPES.CONTRIBUTING,
353
- confidence: 0.6,
354
- evidence: ["Events affected same files"],
355
- });
356
- }
357
- }
358
-
359
- return chain;
360
- }
361
-
362
- /**
363
- * Check if two events are causally related
364
- * @param {Object} earlier - Earlier event
365
- * @param {Object} later - Later event
366
- * @returns {boolean} Are related
367
- */
368
- eventsCausallyRelated(earlier, later) {
369
- // Same file affected
370
- const earlierFiles = new Set(this.extractFilesFromEvent(earlier));
371
- const laterFiles = this.extractFilesFromEvent(later);
372
-
373
- return laterFiles.some(f => earlierFiles.has(f));
374
- }
375
-
376
- /**
377
- * Calculate confidence that event caused incident
378
- * @param {Object} event - Event
379
- * @param {Incident} incident - Incident
380
- * @returns {number} Confidence score
381
- */
382
- calculateCausalConfidence(event, incident) {
383
- let confidence = 0;
384
-
385
- // Override events are highly suspicious
386
- if (event.overrideUsed || event.proof?.overrideUsed) {
387
- confidence += 0.4;
388
- }
389
-
390
- // High risk score
391
- const riskScore = event.riskScore || event.proof?.riskScore || 0;
392
- if (riskScore >= 70) {
393
- confidence += 0.3;
394
- } else if (riskScore >= 50) {
395
- confidence += 0.2;
396
- }
397
-
398
- // File match
399
- const eventFiles = this.extractFilesFromEvent(event);
400
- const hasFileMatch = eventFiles.some(f =>
401
- incident.affectedFiles?.some(af => f.includes(af) || af.includes(f))
402
- );
403
-
404
- if (hasFileMatch) {
405
- confidence += 0.3;
406
- }
407
-
408
- // Temporal proximity (closer = more likely)
409
- const eventTime = new Date(event.timestamp).getTime();
410
- const incidentTime = new Date(incident.reportedAt).getTime();
411
- const hoursBefore = (incidentTime - eventTime) / (1000 * 60 * 60);
412
-
413
- if (hoursBefore <= 1) {
414
- confidence += 0.2;
415
- } else if (hoursBefore <= 6) {
416
- confidence += 0.1;
417
- }
418
-
419
- return Math.min(confidence, 1);
420
- }
421
-
422
- /**
423
- * Determine the relationship type between event and incident
424
- * @param {Object} event - Event
425
- * @param {Incident} incident - Incident
426
- * @returns {string} Relationship type
427
- */
428
- determineRelationship(event, incident) {
429
- if (event.overrideUsed || event.proof?.overrideUsed) {
430
- return RELATIONSHIP_TYPES.OVERRIDE_LED_TO;
431
- }
432
-
433
- const riskScore = event.riskScore || event.proof?.riskScore || 0;
434
- if (riskScore >= 70) {
435
- return RELATIONSHIP_TYPES.HIGH_RISK_CHANGE;
436
- }
437
-
438
- const eventFiles = this.extractFilesFromEvent(event);
439
- const hasFileMatch = eventFiles.some(f =>
440
- incident.affectedFiles?.some(af => f.includes(af) || af.includes(f))
441
- );
442
-
443
- if (hasFileMatch) {
444
- return RELATIONSHIP_TYPES.DIRECT_CAUSE;
445
- }
446
-
447
- return RELATIONSHIP_TYPES.CONTRIBUTING;
448
- }
449
-
450
- /**
451
- * Gather evidence for a causal link
452
- * @param {Object} event - Event
453
- * @param {Incident} incident - Incident
454
- * @returns {string[]} Evidence
455
- */
456
- gatherEvidence(event, incident) {
457
- const evidence = [];
458
-
459
- if (event.overrideUsed || event.proof?.overrideUsed) {
460
- evidence.push(`Override was used: ${event.proof?.overrideReason || "No reason given"}`);
461
- }
462
-
463
- const riskScore = event.riskScore || event.proof?.riskScore || 0;
464
- if (riskScore > 0) {
465
- evidence.push(`Risk score was ${riskScore}`);
466
- }
467
-
468
- const eventFiles = this.extractFilesFromEvent(event);
469
- const matchingFiles = eventFiles.filter(f =>
470
- incident.affectedFiles?.some(af => f.includes(af) || af.includes(f))
471
- );
472
-
473
- if (matchingFiles.length > 0) {
474
- evidence.push(`Modified files: ${matchingFiles.join(", ")}`);
475
- }
476
-
477
- if (event.verdict === "BLOCK") {
478
- evidence.push("Change was initially blocked by firewall");
479
- }
480
-
481
- return evidence;
482
- }
483
-
484
- /**
485
- * Identify the root cause from the causal chain
486
- * @param {CausalLink[]} chain - Causal chain
487
- * @param {Object[]} events - All relevant events
488
- * @returns {Object|null} Root cause
489
- */
490
- identifyRootCause(chain, events) {
491
- if (chain.length === 0) return null;
492
-
493
- // Sort by confidence and find the first event (chronologically) with high confidence
494
- const highConfidence = chain
495
- .filter(link => link.confidence >= 0.7)
496
- .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
497
-
498
- if (highConfidence.length > 0) {
499
- const rootLink = highConfidence[0];
500
- const rootEvent = events.find(e =>
501
- e.id === rootLink.fromEvent || e.changeId === rootLink.fromEvent
502
- );
503
-
504
- return {
505
- event: rootEvent,
506
- link: rootLink,
507
- reason: `First high-confidence change (${Math.round(rootLink.confidence * 100)}%)`,
508
- };
509
- }
510
-
511
- // Fall back to the first event in the chain
512
- const firstLink = chain[0];
513
- const firstEvent = events.find(e =>
514
- e.id === firstLink.fromEvent || e.changeId === firstLink.fromEvent
515
- );
516
-
517
- return {
518
- event: firstEvent,
519
- link: firstLink,
520
- reason: "First change in causal chain",
521
- };
522
- }
523
-
524
- /**
525
- * Find contributing factors beyond root cause
526
- * @param {Object[]} events - All relevant events
527
- * @param {Object} rootCause - Identified root cause
528
- * @returns {Object[]} Contributing factors
529
- */
530
- findContributingFactors(events, rootCause) {
531
- if (!rootCause) return [];
532
-
533
- return events
534
- .filter(e => {
535
- const eventId = e.id || e.changeId;
536
- return eventId !== rootCause.link?.fromEvent;
537
- })
538
- .filter(e => {
539
- // Include events with some risk
540
- const riskScore = e.riskScore || e.proof?.riskScore || 0;
541
- return riskScore >= 30 || e.overrideUsed || e.proof?.overrideUsed;
542
- })
543
- .slice(0, 5); // Limit to top 5
544
- }
545
-
546
- /**
547
- * Generate recommendations based on analysis
548
- * @param {Incident} incident - The incident
549
- * @param {Object} rootCause - Root cause
550
- * @param {Object[]} contributingFactors - Contributing factors
551
- * @returns {Object} Recommendations
552
- */
553
- generateRecommendations(incident, rootCause, contributingFactors) {
554
- const recommendations = {
555
- immediate: [],
556
- shortTerm: [],
557
- longTerm: [],
558
- };
559
-
560
- if (rootCause?.event?.overrideUsed || rootCause?.event?.proof?.overrideUsed) {
561
- recommendations.immediate.push({
562
- action: "Review override policy",
563
- reason: "Incident was caused by an overridden firewall block",
564
- priority: "high",
565
- });
566
-
567
- recommendations.shortTerm.push({
568
- action: "Add invariant rule to prevent similar overrides",
569
- reason: `Consider blocking changes to ${incident.affectedFiles?.join(", ")}`,
570
- priority: "medium",
571
- });
572
- }
573
-
574
- const highRiskChanges = contributingFactors.filter(e =>
575
- (e.riskScore || e.proof?.riskScore || 0) >= 70
576
- );
577
-
578
- if (highRiskChanges.length > 0) {
579
- recommendations.immediate.push({
580
- action: "Review high-risk changes",
581
- reason: `${highRiskChanges.length} high-risk changes preceded this incident`,
582
- priority: "high",
583
- });
584
- }
585
-
586
- recommendations.longTerm.push({
587
- action: "Add automated tests for affected functionality",
588
- reason: "Prevent regression of the fixed issue",
589
- priority: "medium",
590
- });
591
-
592
- if (incident.affectedFiles?.length > 0) {
593
- recommendations.longTerm.push({
594
- action: `Consider protecting ${incident.affectedFiles[0]}`,
595
- reason: "Add to lawbook as a sensitive file",
596
- priority: "low",
597
- });
598
- }
599
-
600
- return recommendations;
601
- }
602
-
603
- /**
604
- * Save an incident report
605
- * @param {string} incidentId - Incident ID
606
- * @param {IncidentReport} report - Report to save
607
- */
608
- saveReport(incidentId, report) {
609
- const reportsDir = path.join(this.incidentsDir, "reports");
610
-
611
- if (!fs.existsSync(reportsDir)) {
612
- fs.mkdirSync(reportsDir, { recursive: true });
613
- }
614
-
615
- fs.writeFileSync(
616
- path.join(reportsDir, `${incidentId}-report.json`),
617
- JSON.stringify(report, null, 2)
618
- );
619
- }
620
-
621
- /**
622
- * Get all incidents
623
- * @returns {Incident[]} All incidents
624
- */
625
- getAllIncidents() {
626
- const incidents = [];
627
-
628
- if (!fs.existsSync(this.incidentsDir)) {
629
- return incidents;
630
- }
631
-
632
- const files = fs.readdirSync(this.incidentsDir)
633
- .filter(f => f.endsWith(".json") && !f.includes("-report"));
634
-
635
- for (const file of files) {
636
- try {
637
- const incident = JSON.parse(
638
- fs.readFileSync(path.join(this.incidentsDir, file), "utf-8")
639
- );
640
- incidents.push(incident);
641
- } catch {
642
- // Skip invalid files
643
- }
644
- }
645
-
646
- return incidents.sort((a, b) =>
647
- new Date(b.reportedAt).getTime() - new Date(a.reportedAt).getTime()
648
- );
649
- }
650
- }
651
-
652
- /**
653
- * Create an incident correlator instance
654
- * @param {Object} options - Options
655
- * @returns {IncidentCorrelator} Incident correlator
656
- */
657
- function createIncidentCorrelator(options = {}) {
658
- return new IncidentCorrelator(options);
659
- }
660
-
661
- module.exports = { IncidentCorrelator, createIncidentCorrelator, RELATIONSHIP_TYPES };