@quantracode/vibecheck 0.0.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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +839 -0
  3. package/dist/__tests__/cli.test.d.ts +2 -0
  4. package/dist/__tests__/cli.test.d.ts.map +1 -0
  5. package/dist/__tests__/cli.test.js +243 -0
  6. package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +36 -0
  7. package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +28 -0
  8. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +4 -0
  9. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +6 -0
  11. package/dist/__tests__/scanners/env-config.test.d.ts +2 -0
  12. package/dist/__tests__/scanners/env-config.test.d.ts.map +1 -0
  13. package/dist/__tests__/scanners/env-config.test.js +142 -0
  14. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +2 -0
  15. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +1 -0
  16. package/dist/__tests__/scanners/nextjs-middleware.test.js +193 -0
  17. package/dist/__tests__/scanners/scanner-packs.test.d.ts +2 -0
  18. package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +1 -0
  19. package/dist/__tests__/scanners/scanner-packs.test.js +126 -0
  20. package/dist/__tests__/scanners/unused-security-imports.test.d.ts +2 -0
  21. package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +1 -0
  22. package/dist/__tests__/scanners/unused-security-imports.test.js +145 -0
  23. package/dist/commands/demo-artifact.d.ts +7 -0
  24. package/dist/commands/demo-artifact.d.ts.map +1 -0
  25. package/dist/commands/demo-artifact.js +322 -0
  26. package/dist/commands/evaluate.d.ts +30 -0
  27. package/dist/commands/evaluate.d.ts.map +1 -0
  28. package/dist/commands/evaluate.js +258 -0
  29. package/dist/commands/explain.d.ts +12 -0
  30. package/dist/commands/explain.d.ts.map +1 -0
  31. package/dist/commands/explain.js +214 -0
  32. package/dist/commands/index.d.ts +7 -0
  33. package/dist/commands/index.d.ts.map +1 -0
  34. package/dist/commands/index.js +6 -0
  35. package/dist/commands/intent.d.ts +21 -0
  36. package/dist/commands/intent.d.ts.map +1 -0
  37. package/dist/commands/intent.js +192 -0
  38. package/dist/commands/scan.d.ts +44 -0
  39. package/dist/commands/scan.d.ts.map +1 -0
  40. package/dist/commands/scan.js +497 -0
  41. package/dist/commands/waivers.d.ts +30 -0
  42. package/dist/commands/waivers.d.ts.map +1 -0
  43. package/dist/commands/waivers.js +249 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +17 -0
  47. package/dist/phase3/index.d.ts +11 -0
  48. package/dist/phase3/index.d.ts.map +1 -0
  49. package/dist/phase3/index.js +12 -0
  50. package/dist/phase3/intent-miner.d.ts +32 -0
  51. package/dist/phase3/intent-miner.d.ts.map +1 -0
  52. package/dist/phase3/intent-miner.js +323 -0
  53. package/dist/phase3/proof-trace-builder.d.ts +42 -0
  54. package/dist/phase3/proof-trace-builder.d.ts.map +1 -0
  55. package/dist/phase3/proof-trace-builder.js +441 -0
  56. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +15 -0
  57. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +1 -0
  58. package/dist/phase3/scanners/auth-by-ui-server-gap.js +237 -0
  59. package/dist/phase3/scanners/comment-claim-unproven.d.ts +14 -0
  60. package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +1 -0
  61. package/dist/phase3/scanners/comment-claim-unproven.js +161 -0
  62. package/dist/phase3/scanners/index.d.ts +31 -0
  63. package/dist/phase3/scanners/index.d.ts.map +1 -0
  64. package/dist/phase3/scanners/index.js +40 -0
  65. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +14 -0
  66. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +1 -0
  67. package/dist/phase3/scanners/middleware-assumed-not-matching.js +172 -0
  68. package/dist/phase3/scanners/validation-claimed-missing.d.ts +15 -0
  69. package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +1 -0
  70. package/dist/phase3/scanners/validation-claimed-missing.js +204 -0
  71. package/dist/scanners/abuse/compute-abuse.d.ts +20 -0
  72. package/dist/scanners/abuse/compute-abuse.d.ts.map +1 -0
  73. package/dist/scanners/abuse/compute-abuse.js +509 -0
  74. package/dist/scanners/abuse/index.d.ts +12 -0
  75. package/dist/scanners/abuse/index.d.ts.map +1 -0
  76. package/dist/scanners/abuse/index.js +15 -0
  77. package/dist/scanners/auth/index.d.ts +5 -0
  78. package/dist/scanners/auth/index.d.ts.map +1 -0
  79. package/dist/scanners/auth/index.js +10 -0
  80. package/dist/scanners/auth/middleware-gap.d.ts +22 -0
  81. package/dist/scanners/auth/middleware-gap.d.ts.map +1 -0
  82. package/dist/scanners/auth/middleware-gap.js +203 -0
  83. package/dist/scanners/auth/unprotected-api-route.d.ts +12 -0
  84. package/dist/scanners/auth/unprotected-api-route.d.ts.map +1 -0
  85. package/dist/scanners/auth/unprotected-api-route.js +126 -0
  86. package/dist/scanners/config/index.d.ts +5 -0
  87. package/dist/scanners/config/index.d.ts.map +1 -0
  88. package/dist/scanners/config/index.js +10 -0
  89. package/dist/scanners/config/insecure-defaults.d.ts +12 -0
  90. package/dist/scanners/config/insecure-defaults.d.ts.map +1 -0
  91. package/dist/scanners/config/insecure-defaults.js +77 -0
  92. package/dist/scanners/config/undocumented-env.d.ts +24 -0
  93. package/dist/scanners/config/undocumented-env.d.ts.map +1 -0
  94. package/dist/scanners/config/undocumented-env.js +159 -0
  95. package/dist/scanners/crypto/index.d.ts +6 -0
  96. package/dist/scanners/crypto/index.d.ts.map +1 -0
  97. package/dist/scanners/crypto/index.js +11 -0
  98. package/dist/scanners/crypto/jwt-decode-unverified.d.ts +14 -0
  99. package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +1 -0
  100. package/dist/scanners/crypto/jwt-decode-unverified.js +87 -0
  101. package/dist/scanners/crypto/math-random-tokens.d.ts +13 -0
  102. package/dist/scanners/crypto/math-random-tokens.d.ts.map +1 -0
  103. package/dist/scanners/crypto/math-random-tokens.js +80 -0
  104. package/dist/scanners/crypto/weak-hashing.d.ts +11 -0
  105. package/dist/scanners/crypto/weak-hashing.d.ts.map +1 -0
  106. package/dist/scanners/crypto/weak-hashing.js +95 -0
  107. package/dist/scanners/env-config.d.ts +24 -0
  108. package/dist/scanners/env-config.d.ts.map +1 -0
  109. package/dist/scanners/env-config.js +164 -0
  110. package/dist/scanners/hallucinations/index.d.ts +4 -0
  111. package/dist/scanners/hallucinations/index.d.ts.map +1 -0
  112. package/dist/scanners/hallucinations/index.js +8 -0
  113. package/dist/scanners/hallucinations/unused-security-imports.d.ts +36 -0
  114. package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +1 -0
  115. package/dist/scanners/hallucinations/unused-security-imports.js +309 -0
  116. package/dist/scanners/helpers/ast-helpers.d.ts +6 -0
  117. package/dist/scanners/helpers/ast-helpers.d.ts.map +1 -0
  118. package/dist/scanners/helpers/ast-helpers.js +945 -0
  119. package/dist/scanners/helpers/context-builder.d.ts +17 -0
  120. package/dist/scanners/helpers/context-builder.d.ts.map +1 -0
  121. package/dist/scanners/helpers/context-builder.js +148 -0
  122. package/dist/scanners/helpers/index.d.ts +3 -0
  123. package/dist/scanners/helpers/index.d.ts.map +1 -0
  124. package/dist/scanners/helpers/index.js +2 -0
  125. package/dist/scanners/index.d.ts +30 -0
  126. package/dist/scanners/index.d.ts.map +1 -0
  127. package/dist/scanners/index.js +102 -0
  128. package/dist/scanners/middleware/index.d.ts +4 -0
  129. package/dist/scanners/middleware/index.d.ts.map +1 -0
  130. package/dist/scanners/middleware/index.js +7 -0
  131. package/dist/scanners/middleware/missing-rate-limit.d.ts +13 -0
  132. package/dist/scanners/middleware/missing-rate-limit.d.ts.map +1 -0
  133. package/dist/scanners/middleware/missing-rate-limit.js +140 -0
  134. package/dist/scanners/network/cors-misconfiguration.d.ts +14 -0
  135. package/dist/scanners/network/cors-misconfiguration.d.ts.map +1 -0
  136. package/dist/scanners/network/cors-misconfiguration.js +89 -0
  137. package/dist/scanners/network/index.d.ts +7 -0
  138. package/dist/scanners/network/index.d.ts.map +1 -0
  139. package/dist/scanners/network/index.js +18 -0
  140. package/dist/scanners/network/missing-timeout.d.ts +15 -0
  141. package/dist/scanners/network/missing-timeout.d.ts.map +1 -0
  142. package/dist/scanners/network/missing-timeout.js +93 -0
  143. package/dist/scanners/network/open-redirect.d.ts +15 -0
  144. package/dist/scanners/network/open-redirect.d.ts.map +1 -0
  145. package/dist/scanners/network/open-redirect.js +88 -0
  146. package/dist/scanners/network/ssrf-prone-fetch.d.ts +12 -0
  147. package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +1 -0
  148. package/dist/scanners/network/ssrf-prone-fetch.js +90 -0
  149. package/dist/scanners/nextjs-middleware.d.ts +26 -0
  150. package/dist/scanners/nextjs-middleware.d.ts.map +1 -0
  151. package/dist/scanners/nextjs-middleware.js +246 -0
  152. package/dist/scanners/privacy/debug-flags.d.ts +13 -0
  153. package/dist/scanners/privacy/debug-flags.d.ts.map +1 -0
  154. package/dist/scanners/privacy/debug-flags.js +124 -0
  155. package/dist/scanners/privacy/index.d.ts +6 -0
  156. package/dist/scanners/privacy/index.d.ts.map +1 -0
  157. package/dist/scanners/privacy/index.js +11 -0
  158. package/dist/scanners/privacy/over-broad-response.d.ts +15 -0
  159. package/dist/scanners/privacy/over-broad-response.d.ts.map +1 -0
  160. package/dist/scanners/privacy/over-broad-response.js +109 -0
  161. package/dist/scanners/privacy/sensitive-logging.d.ts +11 -0
  162. package/dist/scanners/privacy/sensitive-logging.d.ts.map +1 -0
  163. package/dist/scanners/privacy/sensitive-logging.js +78 -0
  164. package/dist/scanners/types.d.ts +456 -0
  165. package/dist/scanners/types.d.ts.map +1 -0
  166. package/dist/scanners/types.js +16 -0
  167. package/dist/scanners/unused-security-imports.d.ts +34 -0
  168. package/dist/scanners/unused-security-imports.d.ts.map +1 -0
  169. package/dist/scanners/unused-security-imports.js +206 -0
  170. package/dist/scanners/uploads/index.d.ts +5 -0
  171. package/dist/scanners/uploads/index.d.ts.map +1 -0
  172. package/dist/scanners/uploads/index.js +9 -0
  173. package/dist/scanners/uploads/missing-constraints.d.ts +15 -0
  174. package/dist/scanners/uploads/missing-constraints.d.ts.map +1 -0
  175. package/dist/scanners/uploads/missing-constraints.js +109 -0
  176. package/dist/scanners/uploads/public-path.d.ts +11 -0
  177. package/dist/scanners/uploads/public-path.d.ts.map +1 -0
  178. package/dist/scanners/uploads/public-path.js +87 -0
  179. package/dist/scanners/validation/client-side-only.d.ts +14 -0
  180. package/dist/scanners/validation/client-side-only.d.ts.map +1 -0
  181. package/dist/scanners/validation/client-side-only.js +140 -0
  182. package/dist/scanners/validation/ignored-validation.d.ts +12 -0
  183. package/dist/scanners/validation/ignored-validation.d.ts.map +1 -0
  184. package/dist/scanners/validation/ignored-validation.js +119 -0
  185. package/dist/scanners/validation/index.d.ts +5 -0
  186. package/dist/scanners/validation/index.d.ts.map +1 -0
  187. package/dist/scanners/validation/index.js +9 -0
  188. package/dist/utils/exclude-patterns.d.ts +35 -0
  189. package/dist/utils/exclude-patterns.d.ts.map +1 -0
  190. package/dist/utils/exclude-patterns.js +78 -0
  191. package/dist/utils/file-utils.d.ts +37 -0
  192. package/dist/utils/file-utils.d.ts.map +1 -0
  193. package/dist/utils/file-utils.js +77 -0
  194. package/dist/utils/fingerprint.d.ts +25 -0
  195. package/dist/utils/fingerprint.d.ts.map +1 -0
  196. package/dist/utils/fingerprint.js +28 -0
  197. package/dist/utils/git-info.d.ts +14 -0
  198. package/dist/utils/git-info.d.ts.map +1 -0
  199. package/dist/utils/git-info.js +55 -0
  200. package/dist/utils/index.d.ts +4 -0
  201. package/dist/utils/index.d.ts.map +1 -0
  202. package/dist/utils/index.js +3 -0
  203. package/dist/utils/progress.d.ts +42 -0
  204. package/dist/utils/progress.d.ts.map +1 -0
  205. package/dist/utils/progress.js +165 -0
  206. package/dist/utils/sarif-formatter.d.ts +92 -0
  207. package/dist/utils/sarif-formatter.d.ts.map +1 -0
  208. package/dist/utils/sarif-formatter.js +172 -0
  209. package/package.json +66 -0
@@ -0,0 +1,309 @@
1
+ import { readFileSync, resolvePath } from "../../utils/file-utils.js";
2
+ import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
3
+ const RULE_ID = "VC-HALL-001";
4
+ const RULE_ID_002 = "VC-HALL-002";
5
+ const SECURITY_LIBS = [
6
+ {
7
+ name: "zod",
8
+ category: "validation",
9
+ severity: "medium",
10
+ description: "Schema validation library",
11
+ },
12
+ {
13
+ name: "yup",
14
+ category: "validation",
15
+ severity: "medium",
16
+ description: "Schema validation library",
17
+ },
18
+ {
19
+ name: "joi",
20
+ category: "validation",
21
+ severity: "medium",
22
+ description: "Schema validation library",
23
+ },
24
+ {
25
+ name: "helmet",
26
+ category: "middleware",
27
+ severity: "medium",
28
+ description: "Security headers middleware",
29
+ },
30
+ {
31
+ name: "cors",
32
+ category: "middleware",
33
+ severity: "low",
34
+ description: "CORS middleware",
35
+ },
36
+ {
37
+ name: "csurf",
38
+ category: "middleware",
39
+ severity: "medium",
40
+ description: "CSRF protection middleware",
41
+ },
42
+ {
43
+ name: "express-rate-limit",
44
+ category: "middleware",
45
+ severity: "medium",
46
+ description: "Rate limiting middleware",
47
+ },
48
+ {
49
+ name: "bcrypt",
50
+ category: "auth",
51
+ severity: "high",
52
+ description: "Password hashing library",
53
+ },
54
+ {
55
+ name: "argon2",
56
+ category: "auth",
57
+ severity: "high",
58
+ description: "Password hashing library",
59
+ },
60
+ ];
61
+ /**
62
+ * Find imports of security libraries in a file
63
+ */
64
+ export function findSecurityImports(content, libraries) {
65
+ const imports = [];
66
+ const lines = content.split("\n");
67
+ for (let i = 0; i < lines.length; i++) {
68
+ const line = lines[i];
69
+ for (const lib of libraries) {
70
+ // Check each library pattern
71
+ const patterns = [
72
+ // import X from 'lib'
73
+ new RegExp(`^\\s*import\\s+(\\w+)\\s+from\\s+['"]${lib}['"]`),
74
+ // import { x, y } from 'lib'
75
+ new RegExp(`^\\s*import\\s+\\{([^}]+)\\}\\s+from\\s+['"]${lib}['"]`),
76
+ // import * as X from 'lib'
77
+ new RegExp(`^\\s*import\\s+\\*\\s+as\\s+(\\w+)\\s+from\\s+['"]${lib}['"]`),
78
+ // import X, { y } from 'lib'
79
+ new RegExp(`^\\s*import\\s+(\\w+)\\s*,\\s*\\{([^}]+)\\}\\s+from\\s+['"]${lib}['"]`),
80
+ ];
81
+ for (let p = 0; p < patterns.length; p++) {
82
+ const match = line.match(patterns[p]);
83
+ if (match) {
84
+ const importedNames = [];
85
+ let isDefaultImport = false;
86
+ let isNamespaceImport = false;
87
+ if (p === 0) {
88
+ // Default import
89
+ importedNames.push(match[1]);
90
+ isDefaultImport = true;
91
+ }
92
+ else if (p === 1) {
93
+ // Named imports
94
+ const names = match[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
95
+ importedNames.push(...names.filter(Boolean));
96
+ }
97
+ else if (p === 2) {
98
+ // Namespace import
99
+ importedNames.push(match[1]);
100
+ isNamespaceImport = true;
101
+ }
102
+ else if (p === 3) {
103
+ // Default + named
104
+ importedNames.push(match[1]);
105
+ isDefaultImport = true;
106
+ const names = match[2].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
107
+ importedNames.push(...names.filter(Boolean));
108
+ }
109
+ imports.push({
110
+ library: lib,
111
+ importedNames,
112
+ line: i + 1,
113
+ snippet: line.trim(),
114
+ isDefaultImport,
115
+ isNamespaceImport,
116
+ });
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ }
122
+ return imports;
123
+ }
124
+ /**
125
+ * Check if any imported identifiers are used after the import line
126
+ */
127
+ export function checkIdentifierUsage(content, importLine, identifiers, isNamespaceImport) {
128
+ const lines = content.split("\n");
129
+ const afterImport = lines.slice(importLine).join("\n");
130
+ return identifiers.map((id) => {
131
+ // For namespace imports, check for namespace.something
132
+ if (isNamespaceImport) {
133
+ const namespaceRegex = new RegExp(`\\b${id}\\s*\\.`, "g");
134
+ return { identifier: id, used: namespaceRegex.test(afterImport) };
135
+ }
136
+ // For regular imports, check for identifier usage
137
+ const usageRegex = new RegExp(`\\b${id}\\b`, "g");
138
+ const matches = afterImport.match(usageRegex);
139
+ return { identifier: id, used: matches !== null && matches.length > 0 };
140
+ });
141
+ }
142
+ /**
143
+ * VC-HALL-001: Unused Security Imports
144
+ *
145
+ * Detects when security libraries are imported but not used
146
+ */
147
+ export async function scanUnusedSecurityImports(context) {
148
+ const { repoRoot, fileIndex } = context;
149
+ const findings = [];
150
+ const libNames = SECURITY_LIBS.map((l) => l.name);
151
+ for (const relFile of fileIndex.allSourceFiles) {
152
+ const absPath = resolvePath(repoRoot, relFile);
153
+ const content = readFileSync(absPath);
154
+ if (!content)
155
+ continue;
156
+ const imports = findSecurityImports(content, libNames);
157
+ for (const imp of imports) {
158
+ const libInfo = SECURITY_LIBS.find((l) => l.name === imp.library);
159
+ if (!libInfo)
160
+ continue;
161
+ const usageResults = checkIdentifierUsage(content, imp.line, imp.importedNames, imp.isNamespaceImport);
162
+ const unusedIdentifiers = usageResults
163
+ .filter((r) => !r.used)
164
+ .map((r) => r.identifier);
165
+ if (unusedIdentifiers.length === 0)
166
+ continue;
167
+ // All imported identifiers are unused
168
+ const allUnused = unusedIdentifiers.length === imp.importedNames.length;
169
+ const evidence = [
170
+ {
171
+ file: relFile,
172
+ startLine: imp.line,
173
+ endLine: imp.line,
174
+ snippet: imp.snippet,
175
+ label: allUnused
176
+ ? `Unused import of ${imp.library}`
177
+ : `Partially unused import: ${unusedIdentifiers.join(", ")}`,
178
+ },
179
+ ];
180
+ const fingerprint = generateFingerprint({
181
+ ruleId: RULE_ID,
182
+ file: relFile,
183
+ symbol: imp.library,
184
+ startLine: imp.line,
185
+ });
186
+ findings.push({
187
+ id: generateFindingId({
188
+ ruleId: RULE_ID,
189
+ file: relFile,
190
+ symbol: imp.library,
191
+ startLine: imp.line,
192
+ }),
193
+ severity: libInfo.severity,
194
+ confidence: allUnused ? 0.9 : 0.75,
195
+ category: libInfo.category,
196
+ ruleId: RULE_ID,
197
+ title: allUnused
198
+ ? `Imported ${libInfo.description.toLowerCase()} "${imp.library}" is not used`
199
+ : `Partially unused import from "${imp.library}"`,
200
+ description: allUnused
201
+ ? `The security library "${imp.library}" is imported but none of its exports are used. This may indicate incomplete implementation of ${libInfo.category} functionality.`
202
+ : `Some exports from "${imp.library}" (${unusedIdentifiers.join(", ")}) are imported but not used. This may indicate incomplete implementation.`,
203
+ evidence,
204
+ remediation: {
205
+ recommendedFix: allUnused
206
+ ? `Either implement ${libInfo.category} using "${imp.library}" or remove the unused import.`
207
+ : `Remove unused imports (${unusedIdentifiers.join(", ")}) or implement their usage.`,
208
+ },
209
+ fingerprint,
210
+ });
211
+ }
212
+ }
213
+ return findings;
214
+ }
215
+ /**
216
+ * VC-HALL-002: next-auth present but not enforced
217
+ *
218
+ * Detects when next-auth is installed but there's no middleware
219
+ * or server-side auth enforcement in route handlers
220
+ */
221
+ export async function scanNextAuthNotEnforced(context) {
222
+ const { repoRoot, fileIndex, repoMeta, helpers } = context;
223
+ const findings = [];
224
+ // Only check if next-auth is present
225
+ if (!repoMeta.hasNextAuth) {
226
+ return findings;
227
+ }
228
+ // Check if middleware exists and uses next-auth
229
+ if (fileIndex.middlewareFile) {
230
+ const content = readFileSync(resolvePath(repoRoot, fileIndex.middlewareFile));
231
+ if (content && /next-auth|withAuth|getToken/.test(content)) {
232
+ return findings; // Middleware seems to enforce auth
233
+ }
234
+ }
235
+ // Check API routes for auth enforcement
236
+ let routesWithoutAuth = 0;
237
+ let totalRoutes = 0;
238
+ for (const relPath of fileIndex.apiRouteFiles) {
239
+ const absPath = resolvePath(repoRoot, relPath);
240
+ const sourceFile = helpers.parseFile(absPath);
241
+ if (!sourceFile)
242
+ continue;
243
+ const handlers = helpers.findRouteHandlers(sourceFile);
244
+ for (const handler of handlers) {
245
+ totalRoutes++;
246
+ if (!helpers.containsAuthCheck(handler.functionNode)) {
247
+ routesWithoutAuth++;
248
+ }
249
+ }
250
+ }
251
+ // If most routes lack auth checks, emit finding
252
+ if (totalRoutes > 0 && routesWithoutAuth / totalRoutes > 0.5) {
253
+ const evidence = [
254
+ {
255
+ file: "package.json",
256
+ startLine: 1,
257
+ endLine: 1,
258
+ label: "next-auth is installed",
259
+ },
260
+ ];
261
+ if (!fileIndex.middlewareFile) {
262
+ evidence.push({
263
+ file: "middleware.ts",
264
+ startLine: 1,
265
+ endLine: 1,
266
+ label: "No middleware file exists",
267
+ });
268
+ }
269
+ const fingerprint = generateFingerprint({
270
+ ruleId: RULE_ID_002,
271
+ file: "package.json",
272
+ symbol: "next-auth",
273
+ });
274
+ findings.push({
275
+ id: generateFindingId({
276
+ ruleId: RULE_ID_002,
277
+ file: "package.json",
278
+ symbol: "next-auth",
279
+ }),
280
+ ruleId: RULE_ID_002,
281
+ title: "next-auth installed but not enforced in most routes",
282
+ description: `next-auth is installed but ${routesWithoutAuth} of ${totalRoutes} API route handlers (${Math.round(routesWithoutAuth / totalRoutes * 100)}%) lack authentication checks. This may indicate incomplete auth implementation.`,
283
+ severity: "medium",
284
+ confidence: 0.75,
285
+ category: "auth",
286
+ evidence,
287
+ remediation: {
288
+ recommendedFix: "Add authentication middleware or explicit auth checks to API routes. See https://next-auth.js.org/configuration/nextjs",
289
+ patch: `// middleware.ts
290
+ import { withAuth } from "next-auth/middleware";
291
+
292
+ export default withAuth({
293
+ callbacks: {
294
+ authorized: ({ token }) => !!token,
295
+ },
296
+ });
297
+
298
+ export const config = {
299
+ matcher: ["/api/:path*"],
300
+ };`,
301
+ },
302
+ links: {
303
+ owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
304
+ },
305
+ fingerprint,
306
+ });
307
+ }
308
+ return findings;
309
+ }
@@ -0,0 +1,6 @@
1
+ import type { AstHelpers, FileProgressCallback } from "../types.js";
2
+ /**
3
+ * Create AST helpers with a ts-morph project
4
+ */
5
+ export declare function createAstHelpers(repoRoot: string, totalFiles?: number, onFileProgress?: FileProgressCallback): AstHelpers;
6
+ //# sourceMappingURL=ast-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-helpers.d.ts","sourceRoot":"","sources":["../../../src/scanners/helpers/ast-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAiBV,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAgDrB;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,oBAAoB,GACpC,UAAU,CA4hCZ"}