@vibecheckai/cli 3.2.2 → 3.2.4

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 (170) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  6. package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
  7. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
  8. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
  9. package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
  10. package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
  11. package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
  12. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
  14. package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
  15. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  16. package/bin/runners/lib/analyzers.js +606 -325
  17. package/bin/runners/lib/auth-truth.js +193 -193
  18. package/bin/runners/lib/backup.js +62 -62
  19. package/bin/runners/lib/billing.js +107 -107
  20. package/bin/runners/lib/claims.js +118 -118
  21. package/bin/runners/lib/cli-ui.js +540 -540
  22. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  23. package/bin/runners/lib/contracts/env-contract.js +181 -181
  24. package/bin/runners/lib/contracts/external-contract.js +206 -206
  25. package/bin/runners/lib/contracts/guard.js +168 -168
  26. package/bin/runners/lib/contracts/index.js +89 -89
  27. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  28. package/bin/runners/lib/contracts/route-contract.js +199 -199
  29. package/bin/runners/lib/contracts.js +804 -804
  30. package/bin/runners/lib/detect.js +89 -89
  31. package/bin/runners/lib/doctor/autofix.js +254 -254
  32. package/bin/runners/lib/doctor/index.js +37 -37
  33. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  34. package/bin/runners/lib/doctor/modules/index.js +46 -46
  35. package/bin/runners/lib/doctor/modules/network.js +250 -250
  36. package/bin/runners/lib/doctor/modules/project.js +312 -312
  37. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  38. package/bin/runners/lib/doctor/modules/security.js +348 -348
  39. package/bin/runners/lib/doctor/modules/system.js +213 -213
  40. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  41. package/bin/runners/lib/doctor/reporter.js +262 -262
  42. package/bin/runners/lib/doctor/service.js +262 -262
  43. package/bin/runners/lib/doctor/types.js +113 -113
  44. package/bin/runners/lib/doctor/ui.js +263 -263
  45. package/bin/runners/lib/doctor-v2.js +608 -608
  46. package/bin/runners/lib/drift.js +425 -425
  47. package/bin/runners/lib/enforcement.js +72 -72
  48. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  49. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  50. package/bin/runners/lib/engines/ast-cache.js +99 -0
  51. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  52. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  53. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  54. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  55. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  56. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  57. package/bin/runners/lib/engines/file-filter.js +131 -0
  58. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  59. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  60. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  61. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  62. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  63. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  64. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  65. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  66. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  67. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  68. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  69. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  70. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  71. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  72. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  73. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  74. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  75. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  76. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  77. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  78. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  79. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  80. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  81. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  82. package/bin/runners/lib/enterprise-detect.js +603 -603
  83. package/bin/runners/lib/enterprise-init.js +942 -942
  84. package/bin/runners/lib/env-resolver.js +417 -417
  85. package/bin/runners/lib/env-template.js +66 -66
  86. package/bin/runners/lib/env.js +189 -189
  87. package/bin/runners/lib/extractors/client-calls.js +990 -990
  88. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  89. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  90. package/bin/runners/lib/extractors/index.js +363 -363
  91. package/bin/runners/lib/extractors/next-routes.js +524 -524
  92. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  93. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  94. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  95. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  96. package/bin/runners/lib/findings-schema.js +281 -281
  97. package/bin/runners/lib/firewall-prompt.js +50 -50
  98. package/bin/runners/lib/global-flags.js +213 -213
  99. package/bin/runners/lib/graph/graph-builder.js +265 -265
  100. package/bin/runners/lib/graph/html-renderer.js +413 -413
  101. package/bin/runners/lib/graph/index.js +32 -32
  102. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  103. package/bin/runners/lib/graph/static-extractor.js +518 -518
  104. package/bin/runners/lib/html-report.js +650 -650
  105. package/bin/runners/lib/interactive-menu.js +1496 -1496
  106. package/bin/runners/lib/llm.js +75 -75
  107. package/bin/runners/lib/meter.js +61 -61
  108. package/bin/runners/lib/missions/evidence.js +126 -126
  109. package/bin/runners/lib/patch.js +40 -40
  110. package/bin/runners/lib/permissions/auth-model.js +213 -213
  111. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  112. package/bin/runners/lib/permissions/index.js +45 -45
  113. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  114. package/bin/runners/lib/pkgjson.js +28 -28
  115. package/bin/runners/lib/policy.js +295 -295
  116. package/bin/runners/lib/preflight.js +142 -142
  117. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  118. package/bin/runners/lib/reality/index.js +318 -318
  119. package/bin/runners/lib/reality/request-hashing.js +416 -416
  120. package/bin/runners/lib/reality/request-mapper.js +453 -453
  121. package/bin/runners/lib/reality/safety-rails.js +463 -463
  122. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  123. package/bin/runners/lib/reality/toast-detector.js +393 -393
  124. package/bin/runners/lib/reality-findings.js +84 -84
  125. package/bin/runners/lib/receipts.js +179 -179
  126. package/bin/runners/lib/redact.js +29 -29
  127. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  128. package/bin/runners/lib/replay/index.js +263 -263
  129. package/bin/runners/lib/replay/player.js +348 -348
  130. package/bin/runners/lib/replay/recorder.js +331 -331
  131. package/bin/runners/lib/report-output.js +187 -187
  132. package/bin/runners/lib/report.js +135 -135
  133. package/bin/runners/lib/route-detection.js +1140 -1140
  134. package/bin/runners/lib/sandbox/index.js +59 -59
  135. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  136. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  137. package/bin/runners/lib/sandbox/worktree.js +174 -174
  138. package/bin/runners/lib/scan-output.js +525 -190
  139. package/bin/runners/lib/schema-validator.js +350 -350
  140. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  141. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  142. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  143. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  144. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  145. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  146. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  147. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  148. package/bin/runners/lib/schemas/validator.js +438 -438
  149. package/bin/runners/lib/score-history.js +282 -282
  150. package/bin/runners/lib/share-pack.js +239 -239
  151. package/bin/runners/lib/snippets.js +67 -67
  152. package/bin/runners/lib/status-output.js +253 -253
  153. package/bin/runners/lib/terminal-ui.js +351 -271
  154. package/bin/runners/lib/upsell.js +510 -510
  155. package/bin/runners/lib/usage.js +153 -153
  156. package/bin/runners/lib/validate-patch.js +156 -156
  157. package/bin/runners/lib/verdict-engine.js +628 -628
  158. package/bin/runners/reality/engine.js +917 -917
  159. package/bin/runners/reality/flows.js +122 -122
  160. package/bin/runners/reality/report.js +378 -378
  161. package/bin/runners/reality/session.js +193 -193
  162. package/bin/runners/runGuard.js +168 -168
  163. package/bin/runners/runProof.zip +0 -0
  164. package/bin/runners/runProve.js +8 -0
  165. package/bin/runners/runReality.js +14 -0
  166. package/bin/runners/runScan.js +17 -1
  167. package/bin/runners/runTruth.js +15 -3
  168. package/mcp-server/tier-auth.js +4 -4
  169. package/mcp-server/tools/index.js +72 -72
  170. package/package.json +1 -1
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Performance Issues Detection Engine
3
+ * Detects memory leaks, inefficient loops, large bundle sizes, and performance anti-patterns
4
+ */
5
+
6
+ const { getAST } = require("./ast-cache");
7
+ const traverse = require("@babel/traverse").default;
8
+ const t = require("@babel/types");
9
+ const { shouldExcludeFile } = require("./file-filter");
10
+
11
+ /**
12
+ * Analyze a file for performance issues
13
+ */
14
+ function analyzePerformanceIssues(code, filePath) {
15
+ const findings = [];
16
+
17
+ // Skip excluded files
18
+ if (shouldExcludeFile(filePath)) return findings;
19
+
20
+ const ast = getAST(code, filePath);
21
+ if (!ast) return findings;
22
+
23
+ const lines = code.split("\n");
24
+
25
+ // Memory leaks: Event listeners not removed
26
+ traverse(ast, {
27
+ CallExpression(path) {
28
+ const node = path.node;
29
+
30
+ // Check for addEventListener without corresponding removeEventListener
31
+ if (t.isMemberExpression(node.callee)) {
32
+ const prop = node.callee.property;
33
+
34
+ if (t.isIdentifier(prop) && prop.name === "addEventListener") {
35
+ // Check if there's a corresponding removeEventListener in the same scope
36
+ const scope = path.scope;
37
+ const hasRemoveListener = scope.getAllBindings().some((binding, name) => {
38
+ return name.includes("removeEventListener") || name.includes("removeListener");
39
+ });
40
+
41
+ // Also check if it's in a useEffect cleanup (React)
42
+ const parent = path.findParent(p =>
43
+ t.isCallExpression(p.node) &&
44
+ t.isIdentifier(p.node.callee, { name: "useEffect" })
45
+ );
46
+
47
+ if (!hasRemoveListener && !parent) {
48
+ const line = node.loc.start.line;
49
+ findings.push({
50
+ type: "memory_leak",
51
+ severity: "WARN",
52
+ category: "Performance",
53
+ file: filePath,
54
+ line,
55
+ column: node.loc.start.column,
56
+ title: "Potential memory leak: Event listener not removed",
57
+ message: "addEventListener called without corresponding removeEventListener",
58
+ codeSnippet: lines[line - 1]?.trim(),
59
+ confidence: "med",
60
+ });
61
+ }
62
+ }
63
+ }
64
+ },
65
+ });
66
+
67
+ // Inefficient loops: nested loops with O(n²) complexity
68
+ // Only flag if depth >= 4 (3 levels is often acceptable)
69
+ let loopDepth = 0;
70
+ traverse(ast, {
71
+ enter(path) {
72
+ if (t.isForStatement(path.node) ||
73
+ t.isForInStatement(path.node) ||
74
+ t.isForOfStatement(path.node) ||
75
+ t.isWhileStatement(path.node)) {
76
+ loopDepth++;
77
+
78
+ // Only flag deeply nested loops (4+ levels)
79
+ if (loopDepth >= 4) {
80
+ const line = path.node.loc.start.line;
81
+ findings.push({
82
+ type: "nested_loops",
83
+ severity: "WARN",
84
+ category: "Performance",
85
+ file: filePath,
86
+ line,
87
+ column: path.node.loc.start.column,
88
+ title: "Deeply nested loops detected",
89
+ message: `Nested loop depth: ${loopDepth} - consider optimizing with early returns or data structures`,
90
+ codeSnippet: lines[line - 1]?.trim(),
91
+ confidence: "med",
92
+ });
93
+ }
94
+ }
95
+ },
96
+ exit(path) {
97
+ if (t.isForStatement(path.node) ||
98
+ t.isForInStatement(path.node) ||
99
+ t.isForOfStatement(path.node) ||
100
+ t.isWhileStatement(path.node)) {
101
+ loopDepth--;
102
+ }
103
+ },
104
+ });
105
+
106
+ // Large array operations without pagination
107
+ // Only flag if it's clearly a large dataset from API
108
+ traverse(ast, {
109
+ CallExpression(path) {
110
+ const node = path.node;
111
+
112
+ // Array methods that process all elements
113
+ if (t.isMemberExpression(node.callee)) {
114
+ const prop = node.callee.property;
115
+
116
+ if (t.isIdentifier(prop) &&
117
+ ["map", "filter", "forEach", "reduce"].includes(prop.name)) {
118
+
119
+ // Only flag if array comes directly from fetch AND there's no pagination logic nearby
120
+ const obj = node.callee.object;
121
+ if (t.isCallExpression(obj) &&
122
+ t.isIdentifier(obj.callee, { name: "fetch" })) {
123
+
124
+ // Check if there's pagination logic in the same function
125
+ const functionParent = path.findParent(p =>
126
+ t.isFunctionDeclaration(p.node) ||
127
+ t.isArrowFunctionExpression(p.node) ||
128
+ t.isFunctionExpression(p.node)
129
+ );
130
+
131
+ if (functionParent) {
132
+ const funcCode = code.substring(functionParent.node.start, functionParent.node.end);
133
+ const hasPagination = /page|limit|offset|pagination/i.test(funcCode);
134
+
135
+ if (!hasPagination) {
136
+ const line = node.loc.start.line;
137
+ findings.push({
138
+ type: "large_array_operation",
139
+ severity: "WARN",
140
+ category: "Performance",
141
+ file: filePath,
142
+ line,
143
+ column: node.loc.start.column,
144
+ title: "Large array operation without pagination",
145
+ message: "Processing entire dataset from API - consider pagination or server-side filtering",
146
+ codeSnippet: lines[line - 1]?.trim(),
147
+ confidence: "low",
148
+ });
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ },
155
+ });
156
+
157
+ // Synchronous file operations in async contexts
158
+ traverse(ast, {
159
+ CallExpression(path) {
160
+ const node = path.node;
161
+
162
+ if (t.isMemberExpression(node.callee) &&
163
+ t.isIdentifier(node.callee.object, { name: "fs" })) {
164
+ const prop = node.callee.property;
165
+
166
+ if (t.isIdentifier(prop) && prop.name.endsWith("Sync")) {
167
+ // Check if we're in an async function
168
+ const asyncParent = path.findParent(p =>
169
+ t.isFunctionDeclaration(p.node) || t.isArrowFunctionExpression(p.node)
170
+ );
171
+
172
+ if (asyncParent && asyncParent.node.async) {
173
+ const line = node.loc.start.line;
174
+ findings.push({
175
+ type: "sync_in_async",
176
+ severity: "WARN",
177
+ category: "Performance",
178
+ file: filePath,
179
+ line,
180
+ column: node.loc.start.column,
181
+ title: "Synchronous operation in async context",
182
+ message: `Using ${prop.name} in async function - use async version instead`,
183
+ codeSnippet: lines[line - 1]?.trim(),
184
+ confidence: "med",
185
+ });
186
+ }
187
+ }
188
+ }
189
+ },
190
+ });
191
+
192
+ // Unnecessary re-renders: setState in render
193
+ traverse(ast, {
194
+ CallExpression(path) {
195
+ const node = path.node;
196
+
197
+ if (t.isMemberExpression(node.callee)) {
198
+ const prop = node.callee.property;
199
+
200
+ if (t.isIdentifier(prop) &&
201
+ (prop.name.startsWith("set") || prop.name === "forceUpdate")) {
202
+
203
+ // Check if we're in a render function or component body
204
+ const functionParent = path.findParent(p =>
205
+ t.isFunctionDeclaration(p.node) || t.isArrowFunctionExpression(p.node)
206
+ );
207
+
208
+ if (functionParent) {
209
+ const funcName = functionParent.node.id?.name || "";
210
+ if (funcName.includes("render") || funcName.includes("Render")) {
211
+ const line = node.loc.start.line;
212
+ findings.push({
213
+ type: "setstate_in_render",
214
+ severity: "WARN",
215
+ category: "Performance",
216
+ file: filePath,
217
+ line,
218
+ column: node.loc.start.column,
219
+ title: "State update in render function",
220
+ message: "Calling setState in render causes infinite re-render loop",
221
+ codeSnippet: lines[line - 1]?.trim(),
222
+ confidence: "high",
223
+ });
224
+ }
225
+ }
226
+ }
227
+ }
228
+ },
229
+ });
230
+
231
+ // Large bundle imports: importing entire libraries
232
+ traverse(ast, {
233
+ ImportDeclaration(path) {
234
+ const node = path.node;
235
+
236
+ // Check for wildcard imports from large libraries
237
+ if (node.specifiers.some(s => t.isImportNamespaceSpecifier(s))) {
238
+ const source = node.source.value;
239
+ const largeLibraries = ["lodash", "moment", "rxjs", "ramda"];
240
+
241
+ if (largeLibraries.some(lib => source.includes(lib))) {
242
+ const line = node.loc.start.line;
243
+ findings.push({
244
+ type: "large_import",
245
+ severity: "WARN",
246
+ category: "Performance",
247
+ file: filePath,
248
+ line,
249
+ column: node.loc.start.column,
250
+ title: "Large library import",
251
+ message: `Importing entire ${source} library - use tree-shaking or import specific functions`,
252
+ codeSnippet: lines[line - 1]?.trim(),
253
+ confidence: "med",
254
+ });
255
+ }
256
+ }
257
+ },
258
+ });
259
+
260
+ return findings;
261
+ }
262
+
263
+ module.exports = {
264
+ analyzePerformanceIssues,
265
+ };
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Security Vulnerabilities Detection Engine
3
+ * Detects SQL injection, XSS, command injection, path traversal, and other security issues
4
+ */
5
+
6
+ const { getAST } = require("./ast-cache");
7
+ const traverse = require("@babel/traverse").default;
8
+ const t = require("@babel/types");
9
+ const { shouldExcludeFile } = require("./file-filter");
10
+
11
+ /**
12
+ * Analyze a file for security vulnerabilities
13
+ */
14
+ function analyzeSecurityVulnerabilities(code, filePath) {
15
+ const findings = [];
16
+
17
+ // Skip excluded files
18
+ if (shouldExcludeFile(filePath)) return findings;
19
+
20
+ const ast = getAST(code, filePath);
21
+ if (!ast) return findings;
22
+
23
+ const lines = code.split("\n");
24
+
25
+ // SQL Injection patterns
26
+ traverse(ast, {
27
+ CallExpression(path) {
28
+ const node = path.node;
29
+
30
+ // Check for SQL query construction with user input
31
+ if (t.isMemberExpression(node.callee)) {
32
+ const obj = node.callee.object;
33
+ const prop = node.callee.property;
34
+
35
+ // Database query methods
36
+ if (t.isIdentifier(prop) &&
37
+ ["query", "execute", "exec", "run"].includes(prop.name)) {
38
+
39
+ // Check if arguments contain template literals or string concatenation
40
+ for (const arg of node.arguments) {
41
+ if (t.isTemplateLiteral(arg) ||
42
+ (t.isBinaryExpression(arg) && arg.operator === "+")) {
43
+ const line = node.loc.start.line;
44
+ findings.push({
45
+ type: "sql_injection",
46
+ severity: "BLOCK",
47
+ category: "Security",
48
+ file: filePath,
49
+ line,
50
+ column: node.loc.start.column,
51
+ title: "Potential SQL injection vulnerability",
52
+ message: "SQL query constructed with user input without parameterization",
53
+ codeSnippet: lines[line - 1]?.trim(),
54
+ confidence: "high",
55
+ });
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ },
62
+ });
63
+
64
+ // XSS vulnerabilities
65
+ traverse(ast, {
66
+ CallExpression(path) {
67
+ const node = path.node;
68
+
69
+ // Dangerous DOM manipulation methods
70
+ if (t.isMemberExpression(node.callee)) {
71
+ const obj = node.callee.object;
72
+ const prop = node.callee.property;
73
+
74
+ if (t.isIdentifier(prop) &&
75
+ ["innerHTML", "outerHTML", "insertAdjacentHTML"].includes(prop.name)) {
76
+
77
+ // Check if argument comes from user input or URL params
78
+ for (const arg of node.arguments) {
79
+ if (t.isIdentifier(arg) || t.isMemberExpression(arg)) {
80
+ const line = node.loc.start.line;
81
+ findings.push({
82
+ type: "xss",
83
+ severity: "BLOCK",
84
+ category: "Security",
85
+ file: filePath,
86
+ line,
87
+ column: node.loc.start.column,
88
+ title: "Potential XSS vulnerability",
89
+ message: `Using ${prop.name} with potentially unsafe content`,
90
+ codeSnippet: lines[line - 1]?.trim(),
91
+ confidence: "med",
92
+ });
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ },
99
+ });
100
+
101
+ // Command injection
102
+ traverse(ast, {
103
+ CallExpression(path) {
104
+ const node = path.node;
105
+
106
+ // Dangerous command execution methods
107
+ if (t.isIdentifier(node.callee)) {
108
+ const dangerousMethods = ["exec", "spawn", "execSync", "spawnSync"];
109
+
110
+ if (dangerousMethods.includes(node.callee.name)) {
111
+ // Check if arguments contain user input
112
+ for (const arg of node.arguments) {
113
+ if (t.isTemplateLiteral(arg) ||
114
+ (t.isBinaryExpression(arg) && arg.operator === "+")) {
115
+ const line = node.loc.start.line;
116
+ findings.push({
117
+ type: "command_injection",
118
+ severity: "BLOCK",
119
+ category: "Security",
120
+ file: filePath,
121
+ line,
122
+ column: node.loc.start.column,
123
+ title: "Potential command injection vulnerability",
124
+ message: "Command execution with potentially unsafe user input",
125
+ codeSnippet: lines[line - 1]?.trim(),
126
+ confidence: "high",
127
+ });
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ },
134
+ });
135
+
136
+ // Path traversal
137
+ traverse(ast, {
138
+ CallExpression(path) {
139
+ const node = path.node;
140
+
141
+ // File system operations
142
+ if (t.isMemberExpression(node.callee) &&
143
+ t.isIdentifier(node.callee.object, { name: "fs" })) {
144
+ const prop = node.callee.property;
145
+
146
+ if (t.isIdentifier(prop) &&
147
+ ["readFile", "writeFile", "readFileSync", "writeFileSync", "unlink"].includes(prop.name)) {
148
+
149
+ // Check if path contains user input or "../"
150
+ for (const arg of node.arguments) {
151
+ if (t.isTemplateLiteral(arg)) {
152
+ const template = code.substring(arg.start, arg.end);
153
+ if (template.includes("../") || template.includes("..\\")) {
154
+ const line = node.loc.start.line;
155
+ findings.push({
156
+ type: "path_traversal",
157
+ severity: "BLOCK",
158
+ category: "Security",
159
+ file: filePath,
160
+ line,
161
+ column: node.loc.start.column,
162
+ title: "Potential path traversal vulnerability",
163
+ message: "File operation with path containing '..' - validate and sanitize paths",
164
+ codeSnippet: lines[line - 1]?.trim(),
165
+ confidence: "high",
166
+ });
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ },
174
+ });
175
+
176
+ // Insecure random number generation
177
+ traverse(ast, {
178
+ CallExpression(path) {
179
+ const node = path.node;
180
+
181
+ if (t.isMemberExpression(node.callee) &&
182
+ t.isIdentifier(node.callee.object, { name: "Math" }) &&
183
+ t.isIdentifier(node.callee.property, { name: "random" })) {
184
+
185
+ // Check if used for security-sensitive purposes (crypto, tokens, etc.)
186
+ const parent = path.parentPath;
187
+ if (parent && parent.isCallExpression()) {
188
+ const parentCallee = parent.node.callee;
189
+ if (t.isMemberExpression(parentCallee) &&
190
+ t.isIdentifier(parentCallee.object, { name: "crypto" })) {
191
+ const line = node.loc.start.line;
192
+ findings.push({
193
+ type: "insecure_random",
194
+ severity: "WARN",
195
+ category: "Security",
196
+ file: filePath,
197
+ line,
198
+ column: node.loc.start.column,
199
+ title: "Insecure random number generation",
200
+ message: "Math.random() is not cryptographically secure - use crypto.randomBytes()",
201
+ codeSnippet: lines[line - 1]?.trim(),
202
+ confidence: "high",
203
+ });
204
+ }
205
+ }
206
+ }
207
+ },
208
+ });
209
+
210
+ // Weak encryption algorithms
211
+ const weakCryptoPatterns = [
212
+ /crypto\.createHash\s*\(\s*['"]md5['"]/i,
213
+ /crypto\.createHash\s*\(\s*['"]sha1['"]/i,
214
+ /crypto\.createCipher\s*\(/i, // Deprecated, insecure
215
+ ];
216
+
217
+ for (let i = 0; i < lines.length; i++) {
218
+ const line = lines[i];
219
+ for (const pattern of weakCryptoPatterns) {
220
+ if (pattern.test(line)) {
221
+ findings.push({
222
+ type: "weak_crypto",
223
+ severity: "WARN",
224
+ category: "Security",
225
+ file: filePath,
226
+ line: i + 1,
227
+ column: 0,
228
+ title: "Weak or deprecated encryption algorithm",
229
+ message: "MD5, SHA1, or createCipher are insecure - use modern algorithms",
230
+ codeSnippet: line.trim(),
231
+ confidence: "high",
232
+ });
233
+ break;
234
+ }
235
+ }
236
+ }
237
+
238
+ return findings;
239
+ }
240
+
241
+ module.exports = {
242
+ analyzeSecurityVulnerabilities,
243
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * TODO/FIXME Detection Engine
3
+ * Uses AST analysis to detect TODO/FIXME comments with better context
4
+ */
5
+
6
+ const { getAST, parseCode } = require("./ast-cache");
7
+ const traverse = require("@babel/traverse").default;
8
+ const t = require("@babel/types");
9
+ const { shouldExcludeFile } = require("./file-filter");
10
+
11
+ /**
12
+ * Check if comment contains TODO/FIXME markers
13
+ */
14
+ function extractTodoMarkers(comment) {
15
+ const text = comment.value || "";
16
+ const markers = [];
17
+
18
+ // Block comments can have multiple markers
19
+ const patterns = [
20
+ { rx: /\bTODO\b[\s:]/i, type: "TODO", severity: "WARN" },
21
+ { rx: /\bFIXME\b[\s:]/i, type: "FIXME", severity: "WARN" },
22
+ { rx: /\bHACK\b[\s:]/i, type: "HACK", severity: "WARN" },
23
+ { rx: /\bXXX\b[\s:]/i, type: "XXX", severity: "WARN" },
24
+ { rx: /\bBUG\b[\s:]/i, type: "BUG", severity: "BLOCK" },
25
+ { rx: /\bBROKEN\b[\s:]/i, type: "BROKEN", severity: "BLOCK" },
26
+ { rx: /\bURGENT\b[\s:]/i, type: "URGENT", severity: "BLOCK" },
27
+ { rx: /\bSECURITY\b[\s:]/i, type: "SECURITY", severity: "BLOCK" },
28
+ { rx: /\bDANGER\b[\s:]/i, type: "DANGER", severity: "BLOCK" },
29
+ ];
30
+
31
+ for (const { rx, type, severity } of patterns) {
32
+ if (rx.test(text)) {
33
+ const match = text.match(rx);
34
+ const afterMarker = text.substring(text.indexOf(match[0]) + match[0].length).trim();
35
+ markers.push({
36
+ type,
37
+ severity,
38
+ text: afterMarker.substring(0, 100), // First 100 chars
39
+ fullText: text,
40
+ });
41
+ }
42
+ }
43
+
44
+ return markers;
45
+ }
46
+
47
+ /**
48
+ * Analyze a file for TODO/FIXME comments
49
+ */
50
+ function analyzeTodoFixme(code, filePath) {
51
+ const findings = [];
52
+
53
+ // Skip excluded files
54
+ if (shouldExcludeFile(filePath)) return findings;
55
+
56
+ const ast = getAST(code, filePath);
57
+ if (!ast) return findings;
58
+
59
+ const lines = code.split("\n");
60
+ const MAX_FINDINGS = 20;
61
+
62
+ // Process all comments
63
+ const comments = ast.comments || [];
64
+ let todoCount = 0;
65
+ let fixmeCount = 0;
66
+
67
+ for (const comment of comments) {
68
+ const markers = extractTodoMarkers(comment);
69
+
70
+ for (const marker of markers) {
71
+ if (marker.type === "TODO") todoCount++;
72
+ if (marker.type === "FIXME") fixmeCount++;
73
+
74
+ if (findings.length < MAX_FINDINGS) {
75
+ const line = comment.loc.start.line;
76
+ const snippet = lines[line - 1]?.trim() || "";
77
+
78
+ findings.push({
79
+ type: marker.type.toLowerCase(),
80
+ severity: marker.severity,
81
+ category: "TodoFixme",
82
+ file: filePath,
83
+ line,
84
+ column: comment.loc.start.column,
85
+ title: `${marker.type} comment`,
86
+ message: marker.text || marker.type,
87
+ codeSnippet: snippet.substring(0, 80),
88
+ confidence: "high",
89
+ });
90
+ }
91
+ }
92
+ }
93
+
94
+ // Add summary if there are many
95
+ const totalTodos = todoCount + fixmeCount;
96
+ if (totalTodos > MAX_FINDINGS) {
97
+ findings.push({
98
+ type: "summary",
99
+ severity: "WARN",
100
+ category: "TodoFixme",
101
+ file: filePath,
102
+ line: 0,
103
+ title: `${totalTodos} TODO/FIXME comments found (${totalTodos - MAX_FINDINGS} more not shown)`,
104
+ message: `Found ${totalTodos} TODO/FIXME comments in this file`,
105
+ confidence: "high",
106
+ });
107
+ }
108
+
109
+ return findings;
110
+ }
111
+
112
+ module.exports = {
113
+ analyzeTodoFixme,
114
+ parseCode,
115
+ };