@vibecheckai/cli 3.5.0 → 3.5.2

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 (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -1,607 +0,0 @@
1
- /**
2
- * Framework Adapters - Plugin Architecture for Framework-Specific Detection
3
- *
4
- * This module provides a plugin system for framework-specific rules and patterns.
5
- * Each adapter can:
6
- *
7
- * 1. Define framework-specific detection patterns
8
- * 2. Override or suppress findings based on framework conventions
9
- * 3. Add framework-specific checks
10
- * 4. Provide framework-specific fix suggestions
11
- *
12
- * To add a new framework:
13
- * 1. Create a new adapter file in this directory (e.g., remix-adapter.js)
14
- * 2. Export it from this index.js
15
- * 3. The adapter will be auto-loaded based on detected framework
16
- */
17
-
18
- "use strict";
19
-
20
- const fs = require("fs");
21
- const path = require("path");
22
-
23
- // ═══════════════════════════════════════════════════════════════════════════════
24
- // ADAPTER INTERFACE
25
- // ═══════════════════════════════════════════════════════════════════════════════
26
-
27
- /**
28
- * Base adapter interface - all adapters should implement these methods
29
- */
30
- const AdapterInterface = {
31
- // Metadata
32
- name: "unknown",
33
- version: "1.0.0",
34
- frameworks: [], // e.g., ["nextjs", "next"]
35
-
36
- // Detection
37
- detect: (projectPath) => false,
38
- getVersion: (projectPath) => null,
39
-
40
- // Finding modification
41
- shouldSuppressFinding: (finding, context) => ({ suppress: false, reason: null }),
42
- modifyFinding: (finding, context) => finding,
43
-
44
- // Additional checks
45
- getAdditionalChecks: () => [],
46
-
47
- // Fix suggestions
48
- getFixSuggestion: (finding, context) => null,
49
- };
50
-
51
- // ═══════════════════════════════════════════════════════════════════════════════
52
- // BUILT-IN ADAPTERS
53
- // ═══════════════════════════════════════════════════════════════════════════════
54
-
55
- /**
56
- * Next.js Adapter - Handles Next.js specific patterns
57
- */
58
- const NextjsAdapter = {
59
- name: "nextjs",
60
- version: "1.0.0",
61
- frameworks: ["nextjs", "next"],
62
-
63
- detect(projectPath) {
64
- try {
65
- const pkgPath = path.join(projectPath, "package.json");
66
- if (fs.existsSync(pkgPath)) {
67
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
68
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
69
- return !!deps.next;
70
- }
71
- } catch {}
72
- return false;
73
- },
74
-
75
- getVersion(projectPath) {
76
- try {
77
- const pkgPath = path.join(projectPath, "package.json");
78
- if (fs.existsSync(pkgPath)) {
79
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
80
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
81
- const version = deps.next;
82
- if (version) {
83
- const match = version.match(/(\d+)\.(\d+)/);
84
- if (match) {
85
- return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
86
- }
87
- }
88
- }
89
- } catch {}
90
- return null;
91
- },
92
-
93
- shouldSuppressFinding(finding, context) {
94
- const { filePath, version } = context;
95
-
96
- // Next.js 13+ App Router: async components are valid
97
- if (version?.major >= 13) {
98
- if (finding.type === "async-without-await" && /\/app\//.test(filePath)) {
99
- // Server components can be async without await
100
- return { suppress: true, reason: "Async server components are valid in Next.js 13+ App Router" };
101
- }
102
-
103
- if (finding.type === "top-level-await" && /\/app\//.test(filePath)) {
104
- return { suppress: true, reason: "Top-level await is valid in Next.js 13+ server components" };
105
- }
106
- }
107
-
108
- // Next.js API routes: exports are expected patterns
109
- if (/\/api\//.test(filePath) || /\/app\/api\//.test(filePath)) {
110
- if (finding.type === "unused-export" && /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/.test(finding.name)) {
111
- return { suppress: true, reason: "HTTP method exports are standard Next.js API route patterns" };
112
- }
113
- }
114
-
115
- // Next.js page exports
116
- if (/\/pages\//.test(filePath) || /\/app\/.*page\.(tsx?|jsx?)$/.test(filePath)) {
117
- if (finding.type === "unused-export" && /^(getStaticProps|getServerSideProps|getStaticPaths|generateStaticParams|generateMetadata)$/.test(finding.name)) {
118
- return { suppress: true, reason: "Next.js data fetching exports are framework conventions" };
119
- }
120
- }
121
-
122
- // Server-only imports
123
- if (finding.type === "import-server-only" && version?.major >= 13) {
124
- return { suppress: true, reason: "server-only imports are valid in Next.js 13+ for server components" };
125
- }
126
-
127
- return { suppress: false, reason: null };
128
- },
129
-
130
- modifyFinding(finding, context) {
131
- // Add Next.js specific context to findings
132
- if (context.version?.major >= 13 && /\/app\//.test(context.filePath)) {
133
- finding.frameworkContext = "Next.js App Router";
134
- } else if (/\/pages\//.test(context.filePath)) {
135
- finding.frameworkContext = "Next.js Pages Router";
136
- }
137
- return finding;
138
- },
139
-
140
- getAdditionalChecks() {
141
- return [
142
- {
143
- id: "nextjs-missing-metadata",
144
- name: "Missing Metadata",
145
- description: "Page is missing metadata export for SEO",
146
- severity: "WARN",
147
- check: (code, filePath) => {
148
- if (!/\/app\/.*page\.(tsx?|jsx?)$/.test(filePath)) return null;
149
- if (/export\s+(const|async function)\s+generateMetadata/.test(code)) return null;
150
- if (/export\s+const\s+metadata/.test(code)) return null;
151
-
152
- return {
153
- type: "nextjs-missing-metadata",
154
- message: "Page is missing metadata export - important for SEO",
155
- severity: "WARN",
156
- confidence: 0.7,
157
- fixHint: "Add `export const metadata = { title: 'Page Title' }` or `export async function generateMetadata()`",
158
- };
159
- },
160
- },
161
- {
162
- id: "nextjs-client-only-in-server",
163
- name: "Client-Only in Server Component",
164
- description: "Using client-only features in server component",
165
- severity: "BLOCK",
166
- check: (code, filePath) => {
167
- if (!/\/app\//.test(filePath)) return null;
168
- if (/^['"]use client['"]/.test(code)) return null; // Client component
169
-
170
- const clientOnlyPatterns = [
171
- /\buseState\s*\(/,
172
- /\buseEffect\s*\(/,
173
- /\buseRef\s*\(/,
174
- /\bwindow\b/,
175
- /\bdocument\b/,
176
- /\blocalStorage\b/,
177
- /\bsessionStorage\b/,
178
- ];
179
-
180
- for (const pattern of clientOnlyPatterns) {
181
- if (pattern.test(code)) {
182
- return {
183
- type: "nextjs-client-only-in-server",
184
- message: `Using client-only feature in server component: ${pattern.source}`,
185
- severity: "BLOCK",
186
- confidence: 0.85,
187
- fixHint: "Add 'use client' directive at the top of the file, or move client-only code to a client component",
188
- };
189
- }
190
- }
191
-
192
- return null;
193
- },
194
- },
195
- ];
196
- },
197
-
198
- getFixSuggestion(finding, context) {
199
- if (finding.type === "nextjs-missing-metadata") {
200
- return {
201
- description: "Add metadata export",
202
- code: `export const metadata = {
203
- title: 'Page Title',
204
- description: 'Page description',
205
- };`,
206
- confidence: 0.9,
207
- autoApplicable: true,
208
- };
209
- }
210
-
211
- if (finding.type === "nextjs-client-only-in-server") {
212
- return {
213
- description: "Add 'use client' directive",
214
- code: `'use client';\n\n`,
215
- insertAt: "top",
216
- confidence: 0.8,
217
- autoApplicable: true,
218
- };
219
- }
220
-
221
- return null;
222
- },
223
- };
224
-
225
- /**
226
- * React Adapter - Handles React-specific patterns
227
- */
228
- const ReactAdapter = {
229
- name: "react",
230
- version: "1.0.0",
231
- frameworks: ["react"],
232
-
233
- detect(projectPath) {
234
- try {
235
- const pkgPath = path.join(projectPath, "package.json");
236
- if (fs.existsSync(pkgPath)) {
237
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
238
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
239
- return !!deps.react;
240
- }
241
- } catch {}
242
- return false;
243
- },
244
-
245
- getVersion(projectPath) {
246
- try {
247
- const pkgPath = path.join(projectPath, "package.json");
248
- if (fs.existsSync(pkgPath)) {
249
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
250
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
251
- const version = deps.react;
252
- if (version) {
253
- const match = version.match(/(\d+)\.(\d+)/);
254
- if (match) {
255
- return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
256
- }
257
- }
258
- }
259
- } catch {}
260
- return null;
261
- },
262
-
263
- shouldSuppressFinding(finding, context) {
264
- const { version } = context;
265
-
266
- // React 19+: use() hook is valid
267
- if (version?.major >= 19) {
268
- if (finding.type === "unknown-hook" && finding.message?.includes("use(")) {
269
- return { suppress: true, reason: "use() hook is valid in React 19+" };
270
- }
271
- }
272
-
273
- // React 18+: concurrent features are valid
274
- if (version?.major >= 18) {
275
- if (finding.type === "unknown-hook" && /use(Transition|DeferredValue|Id)/.test(finding.message)) {
276
- return { suppress: true, reason: `${finding.message} is valid in React 18+` };
277
- }
278
- }
279
-
280
- return { suppress: false, reason: null };
281
- },
282
-
283
- modifyFinding(finding, context) {
284
- return finding;
285
- },
286
-
287
- getAdditionalChecks() {
288
- return [];
289
- },
290
-
291
- getFixSuggestion(finding, context) {
292
- return null;
293
- },
294
- };
295
-
296
- /**
297
- * Express Adapter - Handles Express.js patterns
298
- */
299
- const ExpressAdapter = {
300
- name: "express",
301
- version: "1.0.0",
302
- frameworks: ["express"],
303
-
304
- detect(projectPath) {
305
- try {
306
- const pkgPath = path.join(projectPath, "package.json");
307
- if (fs.existsSync(pkgPath)) {
308
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
309
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
310
- return !!deps.express;
311
- }
312
- } catch {}
313
- return false;
314
- },
315
-
316
- getVersion(projectPath) {
317
- try {
318
- const pkgPath = path.join(projectPath, "package.json");
319
- if (fs.existsSync(pkgPath)) {
320
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
321
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
322
- const version = deps.express;
323
- if (version) {
324
- const match = version.match(/(\d+)\.(\d+)/);
325
- if (match) {
326
- return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
327
- }
328
- }
329
- }
330
- } catch {}
331
- return null;
332
- },
333
-
334
- shouldSuppressFinding(finding, context) {
335
- const { filePath } = context;
336
-
337
- // Express middleware pattern: (req, res, next) with unused next
338
- if (finding.type === "unused-parameter" && finding.name === "next") {
339
- if (/middleware|handler|route/i.test(filePath)) {
340
- return { suppress: true, reason: "Express middleware signature requires next parameter even if unused" };
341
- }
342
- }
343
-
344
- // Error handling middleware: (err, req, res, next)
345
- if (finding.type === "unused-parameter" && /^(err|error)$/.test(finding.name)) {
346
- return { suppress: true, reason: "Express error middleware requires error parameter even if unused" };
347
- }
348
-
349
- return { suppress: false, reason: null };
350
- },
351
-
352
- modifyFinding(finding, context) {
353
- return finding;
354
- },
355
-
356
- getAdditionalChecks() {
357
- return [
358
- {
359
- id: "express-missing-error-handler",
360
- name: "Missing Error Handler",
361
- description: "Express app has no error handling middleware",
362
- severity: "WARN",
363
- check: (code, filePath) => {
364
- if (!/\bapp\s*=\s*express\(\)/.test(code)) return null;
365
- if (/app\.use\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^)]+\)/.test(code)) return null; // 4-param middleware
366
-
367
- return {
368
- type: "express-missing-error-handler",
369
- message: "Express app may be missing error handling middleware",
370
- severity: "WARN",
371
- confidence: 0.6,
372
- fixHint: "Add error handling middleware: app.use((err, req, res, next) => { ... })",
373
- };
374
- },
375
- },
376
- ];
377
- },
378
-
379
- getFixSuggestion(finding, context) {
380
- return null;
381
- },
382
- };
383
-
384
- /**
385
- * Prisma Adapter - Handles Prisma ORM patterns
386
- */
387
- const PrismaAdapter = {
388
- name: "prisma",
389
- version: "1.0.0",
390
- frameworks: ["prisma"],
391
-
392
- detect(projectPath) {
393
- try {
394
- const pkgPath = path.join(projectPath, "package.json");
395
- if (fs.existsSync(pkgPath)) {
396
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
397
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
398
- return !!deps["@prisma/client"] || !!deps.prisma;
399
- }
400
- } catch {}
401
- return false;
402
- },
403
-
404
- getVersion(projectPath) {
405
- try {
406
- const pkgPath = path.join(projectPath, "package.json");
407
- if (fs.existsSync(pkgPath)) {
408
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
409
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
410
- const version = deps["@prisma/client"] || deps.prisma;
411
- if (version) {
412
- const match = version.match(/(\d+)\.(\d+)/);
413
- if (match) {
414
- return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
415
- }
416
- }
417
- }
418
- } catch {}
419
- return null;
420
- },
421
-
422
- shouldSuppressFinding(finding, context) {
423
- // Prisma queries are parameterized, skip SQL injection warnings
424
- if (finding.type === "sql-injection" || finding.type === "raw-sql") {
425
- if (/prisma\.\$queryRaw|prisma\.\$executeRaw/.test(context.code || "")) {
426
- return { suppress: true, reason: "Prisma parameterized queries are safe from SQL injection" };
427
- }
428
- }
429
-
430
- // Prisma client is generated, skip generated code warnings
431
- if (finding.filePath?.includes("@prisma/client") || finding.filePath?.includes("generated/prisma")) {
432
- return { suppress: true, reason: "Prisma client is generated code" };
433
- }
434
-
435
- return { suppress: false, reason: null };
436
- },
437
-
438
- modifyFinding(finding, context) {
439
- return finding;
440
- },
441
-
442
- getAdditionalChecks() {
443
- return [];
444
- },
445
-
446
- getFixSuggestion(finding, context) {
447
- return null;
448
- },
449
- };
450
-
451
- // ═══════════════════════════════════════════════════════════════════════════════
452
- // ADAPTER REGISTRY
453
- // ═══════════════════════════════════════════════════════════════════════════════
454
-
455
- const BUILT_IN_ADAPTERS = [
456
- NextjsAdapter,
457
- ReactAdapter,
458
- ExpressAdapter,
459
- PrismaAdapter,
460
- ];
461
-
462
- // Custom adapters can be registered here
463
- const customAdapters = [];
464
-
465
- /**
466
- * Register a custom adapter
467
- */
468
- function registerAdapter(adapter) {
469
- // Validate adapter interface
470
- const required = ["name", "frameworks", "detect"];
471
- for (const field of required) {
472
- if (!adapter[field]) {
473
- throw new Error(`Adapter missing required field: ${field}`);
474
- }
475
- }
476
- customAdapters.push(adapter);
477
- }
478
-
479
- /**
480
- * Get all adapters (built-in + custom)
481
- */
482
- function getAllAdapters() {
483
- return [...BUILT_IN_ADAPTERS, ...customAdapters];
484
- }
485
-
486
- /**
487
- * Detect which adapters are applicable for a project
488
- */
489
- function detectApplicableAdapters(projectPath) {
490
- const applicable = [];
491
-
492
- for (const adapter of getAllAdapters()) {
493
- try {
494
- if (adapter.detect(projectPath)) {
495
- const version = adapter.getVersion?.(projectPath) || null;
496
- applicable.push({ adapter, version });
497
- }
498
- } catch {
499
- // Skip adapters that fail detection
500
- }
501
- }
502
-
503
- return applicable;
504
- }
505
-
506
- /**
507
- * Apply all applicable adapters to a finding
508
- */
509
- function applyAdapters(finding, context = {}) {
510
- const { projectPath = ".", filePath = "", code = "" } = context;
511
-
512
- // Get applicable adapters
513
- const applicable = detectApplicableAdapters(projectPath);
514
-
515
- let modifiedFinding = { ...finding };
516
-
517
- for (const { adapter, version } of applicable) {
518
- const ctx = { ...context, version, filePath, code };
519
-
520
- // Check if finding should be suppressed
521
- const suppressResult = adapter.shouldSuppressFinding?.(modifiedFinding, ctx);
522
- if (suppressResult?.suppress) {
523
- return {
524
- finding: null,
525
- suppressed: true,
526
- reason: suppressResult.reason,
527
- adapter: adapter.name,
528
- };
529
- }
530
-
531
- // Modify finding
532
- modifiedFinding = adapter.modifyFinding?.(modifiedFinding, ctx) || modifiedFinding;
533
- }
534
-
535
- return {
536
- finding: modifiedFinding,
537
- suppressed: false,
538
- reason: null,
539
- adapter: null,
540
- };
541
- }
542
-
543
- /**
544
- * Get all additional checks from applicable adapters
545
- */
546
- function getAdditionalChecks(projectPath) {
547
- const checks = [];
548
- const applicable = detectApplicableAdapters(projectPath);
549
-
550
- for (const { adapter } of applicable) {
551
- const adapterChecks = adapter.getAdditionalChecks?.() || [];
552
- for (const check of adapterChecks) {
553
- checks.push({
554
- ...check,
555
- adapter: adapter.name,
556
- });
557
- }
558
- }
559
-
560
- return checks;
561
- }
562
-
563
- /**
564
- * Get fix suggestion from applicable adapters
565
- */
566
- function getFixSuggestion(finding, context = {}) {
567
- const { projectPath = "." } = context;
568
- const applicable = detectApplicableAdapters(projectPath);
569
-
570
- for (const { adapter, version } of applicable) {
571
- const ctx = { ...context, version };
572
- const suggestion = adapter.getFixSuggestion?.(finding, ctx);
573
- if (suggestion) {
574
- return {
575
- ...suggestion,
576
- adapter: adapter.name,
577
- };
578
- }
579
- }
580
-
581
- return null;
582
- }
583
-
584
- // ═══════════════════════════════════════════════════════════════════════════════
585
- // EXPORTS
586
- // ═══════════════════════════════════════════════════════════════════════════════
587
-
588
- module.exports = {
589
- // Adapter management
590
- registerAdapter,
591
- getAllAdapters,
592
- detectApplicableAdapters,
593
-
594
- // Finding processing
595
- applyAdapters,
596
- getAdditionalChecks,
597
- getFixSuggestion,
598
-
599
- // Built-in adapters (for direct use or customization)
600
- NextjsAdapter,
601
- ReactAdapter,
602
- ExpressAdapter,
603
- PrismaAdapter,
604
-
605
- // Interface (for reference)
606
- AdapterInterface,
607
- };