@quantracode/vibecheck 0.0.1 → 0.0.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 (208) hide show
  1. package/README.md +6 -6
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.js +7902 -8
  4. package/package.json +13 -7
  5. package/dist/__tests__/cli.test.d.ts +0 -2
  6. package/dist/__tests__/cli.test.d.ts.map +0 -1
  7. package/dist/__tests__/cli.test.js +0 -243
  8. package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +0 -36
  9. package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +0 -28
  10. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +0 -4
  11. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +0 -1
  12. package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +0 -6
  13. package/dist/__tests__/scanners/env-config.test.d.ts +0 -2
  14. package/dist/__tests__/scanners/env-config.test.d.ts.map +0 -1
  15. package/dist/__tests__/scanners/env-config.test.js +0 -142
  16. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +0 -2
  17. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +0 -1
  18. package/dist/__tests__/scanners/nextjs-middleware.test.js +0 -193
  19. package/dist/__tests__/scanners/scanner-packs.test.d.ts +0 -2
  20. package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +0 -1
  21. package/dist/__tests__/scanners/scanner-packs.test.js +0 -126
  22. package/dist/__tests__/scanners/unused-security-imports.test.d.ts +0 -2
  23. package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +0 -1
  24. package/dist/__tests__/scanners/unused-security-imports.test.js +0 -145
  25. package/dist/commands/demo-artifact.d.ts +0 -7
  26. package/dist/commands/demo-artifact.d.ts.map +0 -1
  27. package/dist/commands/demo-artifact.js +0 -322
  28. package/dist/commands/evaluate.d.ts +0 -30
  29. package/dist/commands/evaluate.d.ts.map +0 -1
  30. package/dist/commands/evaluate.js +0 -258
  31. package/dist/commands/explain.d.ts +0 -12
  32. package/dist/commands/explain.d.ts.map +0 -1
  33. package/dist/commands/explain.js +0 -214
  34. package/dist/commands/index.d.ts +0 -7
  35. package/dist/commands/index.d.ts.map +0 -1
  36. package/dist/commands/index.js +0 -6
  37. package/dist/commands/intent.d.ts +0 -21
  38. package/dist/commands/intent.d.ts.map +0 -1
  39. package/dist/commands/intent.js +0 -192
  40. package/dist/commands/scan.d.ts +0 -44
  41. package/dist/commands/scan.d.ts.map +0 -1
  42. package/dist/commands/scan.js +0 -497
  43. package/dist/commands/waivers.d.ts +0 -30
  44. package/dist/commands/waivers.d.ts.map +0 -1
  45. package/dist/commands/waivers.js +0 -249
  46. package/dist/index.d.ts.map +0 -1
  47. package/dist/phase3/index.d.ts +0 -11
  48. package/dist/phase3/index.d.ts.map +0 -1
  49. package/dist/phase3/index.js +0 -12
  50. package/dist/phase3/intent-miner.d.ts +0 -32
  51. package/dist/phase3/intent-miner.d.ts.map +0 -1
  52. package/dist/phase3/intent-miner.js +0 -323
  53. package/dist/phase3/proof-trace-builder.d.ts +0 -42
  54. package/dist/phase3/proof-trace-builder.d.ts.map +0 -1
  55. package/dist/phase3/proof-trace-builder.js +0 -441
  56. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +0 -15
  57. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +0 -1
  58. package/dist/phase3/scanners/auth-by-ui-server-gap.js +0 -237
  59. package/dist/phase3/scanners/comment-claim-unproven.d.ts +0 -14
  60. package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +0 -1
  61. package/dist/phase3/scanners/comment-claim-unproven.js +0 -161
  62. package/dist/phase3/scanners/index.d.ts +0 -31
  63. package/dist/phase3/scanners/index.d.ts.map +0 -1
  64. package/dist/phase3/scanners/index.js +0 -40
  65. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +0 -14
  66. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +0 -1
  67. package/dist/phase3/scanners/middleware-assumed-not-matching.js +0 -172
  68. package/dist/phase3/scanners/validation-claimed-missing.d.ts +0 -15
  69. package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +0 -1
  70. package/dist/phase3/scanners/validation-claimed-missing.js +0 -204
  71. package/dist/scanners/abuse/compute-abuse.d.ts +0 -20
  72. package/dist/scanners/abuse/compute-abuse.d.ts.map +0 -1
  73. package/dist/scanners/abuse/compute-abuse.js +0 -509
  74. package/dist/scanners/abuse/index.d.ts +0 -12
  75. package/dist/scanners/abuse/index.d.ts.map +0 -1
  76. package/dist/scanners/abuse/index.js +0 -15
  77. package/dist/scanners/auth/index.d.ts +0 -5
  78. package/dist/scanners/auth/index.d.ts.map +0 -1
  79. package/dist/scanners/auth/index.js +0 -10
  80. package/dist/scanners/auth/middleware-gap.d.ts +0 -22
  81. package/dist/scanners/auth/middleware-gap.d.ts.map +0 -1
  82. package/dist/scanners/auth/middleware-gap.js +0 -203
  83. package/dist/scanners/auth/unprotected-api-route.d.ts +0 -12
  84. package/dist/scanners/auth/unprotected-api-route.d.ts.map +0 -1
  85. package/dist/scanners/auth/unprotected-api-route.js +0 -126
  86. package/dist/scanners/config/index.d.ts +0 -5
  87. package/dist/scanners/config/index.d.ts.map +0 -1
  88. package/dist/scanners/config/index.js +0 -10
  89. package/dist/scanners/config/insecure-defaults.d.ts +0 -12
  90. package/dist/scanners/config/insecure-defaults.d.ts.map +0 -1
  91. package/dist/scanners/config/insecure-defaults.js +0 -77
  92. package/dist/scanners/config/undocumented-env.d.ts +0 -24
  93. package/dist/scanners/config/undocumented-env.d.ts.map +0 -1
  94. package/dist/scanners/config/undocumented-env.js +0 -159
  95. package/dist/scanners/crypto/index.d.ts +0 -6
  96. package/dist/scanners/crypto/index.d.ts.map +0 -1
  97. package/dist/scanners/crypto/index.js +0 -11
  98. package/dist/scanners/crypto/jwt-decode-unverified.d.ts +0 -14
  99. package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +0 -1
  100. package/dist/scanners/crypto/jwt-decode-unverified.js +0 -87
  101. package/dist/scanners/crypto/math-random-tokens.d.ts +0 -13
  102. package/dist/scanners/crypto/math-random-tokens.d.ts.map +0 -1
  103. package/dist/scanners/crypto/math-random-tokens.js +0 -80
  104. package/dist/scanners/crypto/weak-hashing.d.ts +0 -11
  105. package/dist/scanners/crypto/weak-hashing.d.ts.map +0 -1
  106. package/dist/scanners/crypto/weak-hashing.js +0 -95
  107. package/dist/scanners/env-config.d.ts +0 -24
  108. package/dist/scanners/env-config.d.ts.map +0 -1
  109. package/dist/scanners/env-config.js +0 -164
  110. package/dist/scanners/hallucinations/index.d.ts +0 -4
  111. package/dist/scanners/hallucinations/index.d.ts.map +0 -1
  112. package/dist/scanners/hallucinations/index.js +0 -8
  113. package/dist/scanners/hallucinations/unused-security-imports.d.ts +0 -36
  114. package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +0 -1
  115. package/dist/scanners/hallucinations/unused-security-imports.js +0 -309
  116. package/dist/scanners/helpers/ast-helpers.d.ts +0 -6
  117. package/dist/scanners/helpers/ast-helpers.d.ts.map +0 -1
  118. package/dist/scanners/helpers/ast-helpers.js +0 -945
  119. package/dist/scanners/helpers/context-builder.d.ts +0 -17
  120. package/dist/scanners/helpers/context-builder.d.ts.map +0 -1
  121. package/dist/scanners/helpers/context-builder.js +0 -148
  122. package/dist/scanners/helpers/index.d.ts +0 -3
  123. package/dist/scanners/helpers/index.d.ts.map +0 -1
  124. package/dist/scanners/helpers/index.js +0 -2
  125. package/dist/scanners/index.d.ts +0 -30
  126. package/dist/scanners/index.d.ts.map +0 -1
  127. package/dist/scanners/index.js +0 -102
  128. package/dist/scanners/middleware/index.d.ts +0 -4
  129. package/dist/scanners/middleware/index.d.ts.map +0 -1
  130. package/dist/scanners/middleware/index.js +0 -7
  131. package/dist/scanners/middleware/missing-rate-limit.d.ts +0 -13
  132. package/dist/scanners/middleware/missing-rate-limit.d.ts.map +0 -1
  133. package/dist/scanners/middleware/missing-rate-limit.js +0 -140
  134. package/dist/scanners/network/cors-misconfiguration.d.ts +0 -14
  135. package/dist/scanners/network/cors-misconfiguration.d.ts.map +0 -1
  136. package/dist/scanners/network/cors-misconfiguration.js +0 -89
  137. package/dist/scanners/network/index.d.ts +0 -7
  138. package/dist/scanners/network/index.d.ts.map +0 -1
  139. package/dist/scanners/network/index.js +0 -18
  140. package/dist/scanners/network/missing-timeout.d.ts +0 -15
  141. package/dist/scanners/network/missing-timeout.d.ts.map +0 -1
  142. package/dist/scanners/network/missing-timeout.js +0 -93
  143. package/dist/scanners/network/open-redirect.d.ts +0 -15
  144. package/dist/scanners/network/open-redirect.d.ts.map +0 -1
  145. package/dist/scanners/network/open-redirect.js +0 -88
  146. package/dist/scanners/network/ssrf-prone-fetch.d.ts +0 -12
  147. package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +0 -1
  148. package/dist/scanners/network/ssrf-prone-fetch.js +0 -90
  149. package/dist/scanners/nextjs-middleware.d.ts +0 -26
  150. package/dist/scanners/nextjs-middleware.d.ts.map +0 -1
  151. package/dist/scanners/nextjs-middleware.js +0 -246
  152. package/dist/scanners/privacy/debug-flags.d.ts +0 -13
  153. package/dist/scanners/privacy/debug-flags.d.ts.map +0 -1
  154. package/dist/scanners/privacy/debug-flags.js +0 -124
  155. package/dist/scanners/privacy/index.d.ts +0 -6
  156. package/dist/scanners/privacy/index.d.ts.map +0 -1
  157. package/dist/scanners/privacy/index.js +0 -11
  158. package/dist/scanners/privacy/over-broad-response.d.ts +0 -15
  159. package/dist/scanners/privacy/over-broad-response.d.ts.map +0 -1
  160. package/dist/scanners/privacy/over-broad-response.js +0 -109
  161. package/dist/scanners/privacy/sensitive-logging.d.ts +0 -11
  162. package/dist/scanners/privacy/sensitive-logging.d.ts.map +0 -1
  163. package/dist/scanners/privacy/sensitive-logging.js +0 -78
  164. package/dist/scanners/types.d.ts +0 -456
  165. package/dist/scanners/types.d.ts.map +0 -1
  166. package/dist/scanners/types.js +0 -16
  167. package/dist/scanners/unused-security-imports.d.ts +0 -34
  168. package/dist/scanners/unused-security-imports.d.ts.map +0 -1
  169. package/dist/scanners/unused-security-imports.js +0 -206
  170. package/dist/scanners/uploads/index.d.ts +0 -5
  171. package/dist/scanners/uploads/index.d.ts.map +0 -1
  172. package/dist/scanners/uploads/index.js +0 -9
  173. package/dist/scanners/uploads/missing-constraints.d.ts +0 -15
  174. package/dist/scanners/uploads/missing-constraints.d.ts.map +0 -1
  175. package/dist/scanners/uploads/missing-constraints.js +0 -109
  176. package/dist/scanners/uploads/public-path.d.ts +0 -11
  177. package/dist/scanners/uploads/public-path.d.ts.map +0 -1
  178. package/dist/scanners/uploads/public-path.js +0 -87
  179. package/dist/scanners/validation/client-side-only.d.ts +0 -14
  180. package/dist/scanners/validation/client-side-only.d.ts.map +0 -1
  181. package/dist/scanners/validation/client-side-only.js +0 -140
  182. package/dist/scanners/validation/ignored-validation.d.ts +0 -12
  183. package/dist/scanners/validation/ignored-validation.d.ts.map +0 -1
  184. package/dist/scanners/validation/ignored-validation.js +0 -119
  185. package/dist/scanners/validation/index.d.ts +0 -5
  186. package/dist/scanners/validation/index.d.ts.map +0 -1
  187. package/dist/scanners/validation/index.js +0 -9
  188. package/dist/utils/exclude-patterns.d.ts +0 -35
  189. package/dist/utils/exclude-patterns.d.ts.map +0 -1
  190. package/dist/utils/exclude-patterns.js +0 -78
  191. package/dist/utils/file-utils.d.ts +0 -37
  192. package/dist/utils/file-utils.d.ts.map +0 -1
  193. package/dist/utils/file-utils.js +0 -77
  194. package/dist/utils/fingerprint.d.ts +0 -25
  195. package/dist/utils/fingerprint.d.ts.map +0 -1
  196. package/dist/utils/fingerprint.js +0 -28
  197. package/dist/utils/git-info.d.ts +0 -14
  198. package/dist/utils/git-info.d.ts.map +0 -1
  199. package/dist/utils/git-info.js +0 -55
  200. package/dist/utils/index.d.ts +0 -4
  201. package/dist/utils/index.d.ts.map +0 -1
  202. package/dist/utils/index.js +0 -3
  203. package/dist/utils/progress.d.ts +0 -42
  204. package/dist/utils/progress.d.ts.map +0 -1
  205. package/dist/utils/progress.js +0 -165
  206. package/dist/utils/sarif-formatter.d.ts +0 -92
  207. package/dist/utils/sarif-formatter.d.ts.map +0 -1
  208. package/dist/utils/sarif-formatter.js +0 -172
@@ -1,203 +0,0 @@
1
- import { readFileSync, resolvePath } from "../../utils/file-utils.js";
2
- import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
3
- const RULE_MW_001 = "VC-MW-001";
4
- const RULE_AUTH_INFO_001 = "VC-AUTH-INFO-001";
5
- /**
6
- * Parse matcher config from middleware file
7
- *
8
- * Looks for patterns like:
9
- * - export const config = { matcher: '/api/:path*' }
10
- * - export const config = { matcher: ['/api/:path*', '/admin/:path*'] }
11
- */
12
- export function parseMatcherConfig(content) {
13
- // Look for config export with matcher
14
- const configMatch = content.match(/export\s+const\s+config\s*=\s*\{[^}]*matcher\s*:\s*([^}]+)\}/s);
15
- if (!configMatch) {
16
- return null; // No config export found
17
- }
18
- const matcherPart = configMatch[1];
19
- // Check for array matcher: ['/path1', '/path2']
20
- const arrayMatch = matcherPart.match(/\[([^\]]+)\]/);
21
- if (arrayMatch) {
22
- const items = arrayMatch[1];
23
- const paths = [];
24
- // Extract string literals
25
- const stringMatches = items.matchAll(/['"]([^'"]+)['"]/g);
26
- for (const m of stringMatches) {
27
- paths.push(m[1]);
28
- }
29
- return paths;
30
- }
31
- // Check for single string matcher: '/path'
32
- const stringMatch = matcherPart.match(/['"]([^'"]+)['"]/);
33
- if (stringMatch) {
34
- return [stringMatch[1]];
35
- }
36
- return null;
37
- }
38
- /**
39
- * Check if any matcher pattern covers /api routes
40
- */
41
- export function matcherCoversApi(matchers) {
42
- for (const matcher of matchers) {
43
- // Direct /api match
44
- if (matcher === "/api" || matcher === "/api/:path*") {
45
- return true;
46
- }
47
- // Pattern starts with /api
48
- if (matcher.startsWith("/api/") || matcher.startsWith("/api:")) {
49
- return true;
50
- }
51
- // Catch-all that would include /api
52
- if (matcher === "/:path*" || matcher === "/(.*)" || matcher === "/(.*)") {
53
- return true;
54
- }
55
- // Negation pattern - check if it's excluding something else but including api
56
- // e.g., '/((?!_next/static|_next/image|favicon.ico).*)'
57
- if (matcher.includes("(?!") && !matcher.includes("api")) {
58
- return true;
59
- }
60
- }
61
- return false;
62
- }
63
- /**
64
- * VC-MW-001: Middleware matcher gap for /api coverage
65
- * VC-AUTH-INFO-001: Missing middleware with next-auth
66
- *
67
- * Checks if middleware properly covers API routes
68
- */
69
- export async function scanMiddlewareGap(context) {
70
- const { repoRoot, fileIndex, repoMeta } = context;
71
- const findings = [];
72
- // Only run for Next.js projects
73
- if (repoMeta.framework !== "next") {
74
- return findings;
75
- }
76
- const hasApiRoutes = fileIndex.apiRouteFiles.length > 0;
77
- if (!hasApiRoutes) {
78
- return findings; // No API routes to protect
79
- }
80
- const middlewarePath = fileIndex.middlewareFile;
81
- if (!middlewarePath) {
82
- // No middleware file exists
83
- if (repoMeta.hasNextAuth) {
84
- // Has next-auth but no middleware - likely missing protection
85
- const evidence = [
86
- {
87
- file: "package.json",
88
- startLine: 1,
89
- endLine: 1,
90
- label: "next-auth dependency present",
91
- },
92
- ...fileIndex.apiRouteFiles.slice(0, 3).map((route) => ({
93
- file: route,
94
- startLine: 1,
95
- endLine: 1,
96
- label: "API route without middleware protection",
97
- })),
98
- ];
99
- const fingerprint = generateFingerprint({
100
- ruleId: RULE_AUTH_INFO_001,
101
- file: "middleware.ts",
102
- symbol: "missing",
103
- });
104
- findings.push({
105
- id: generateFindingId({
106
- ruleId: RULE_AUTH_INFO_001,
107
- file: "middleware.ts",
108
- symbol: "missing",
109
- }),
110
- severity: "medium",
111
- confidence: 0.7,
112
- category: "auth",
113
- ruleId: RULE_AUTH_INFO_001,
114
- title: "Next.js middleware missing with next-auth dependency",
115
- description: `This Next.js project uses next-auth but has no middleware.ts file. API routes (${fileIndex.apiRouteFiles.length} found) may lack server-side authentication enforcement. While next-auth provides session management, middleware is recommended for edge-level protection.`,
116
- evidence,
117
- remediation: {
118
- recommendedFix: "Create a middleware.ts file that checks authentication for protected routes. See: https://next-auth.js.org/configuration/nextjs#middleware",
119
- patch: `// middleware.ts
120
- import { withAuth } from "next-auth/middleware";
121
-
122
- export default withAuth({
123
- callbacks: {
124
- authorized: ({ token }) => !!token,
125
- },
126
- });
127
-
128
- export const config = {
129
- matcher: ["/api/:path*", "/dashboard/:path*"],
130
- };`,
131
- },
132
- links: {
133
- owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
134
- },
135
- fingerprint,
136
- });
137
- }
138
- return findings;
139
- }
140
- // Middleware exists - check if it covers API routes
141
- const middlewareContent = readFileSync(resolvePath(repoRoot, middlewarePath));
142
- if (!middlewareContent) {
143
- return findings;
144
- }
145
- const matchers = parseMatcherConfig(middlewareContent);
146
- // If no matcher config, middleware applies to all routes (except static)
147
- if (matchers === null) {
148
- return findings; // Likely covers everything
149
- }
150
- // Check if matchers cover /api
151
- if (!matcherCoversApi(matchers)) {
152
- const lines = middlewareContent.split("\n");
153
- let configLine = 1;
154
- for (let i = 0; i < lines.length; i++) {
155
- if (lines[i].includes("config") && lines[i].includes("matcher")) {
156
- configLine = i + 1;
157
- break;
158
- }
159
- }
160
- const evidence = [
161
- {
162
- file: middlewarePath,
163
- startLine: configLine,
164
- endLine: configLine,
165
- snippet: `matcher: ${JSON.stringify(matchers)}`,
166
- label: "Middleware matcher does not include /api routes",
167
- },
168
- ...fileIndex.apiRouteFiles.slice(0, 2).map((route) => ({
169
- file: route,
170
- startLine: 1,
171
- endLine: 1,
172
- label: "API route not covered by middleware",
173
- })),
174
- ];
175
- const fingerprint = generateFingerprint({
176
- ruleId: RULE_MW_001,
177
- file: middlewarePath,
178
- symbol: "matcher",
179
- });
180
- findings.push({
181
- id: generateFindingId({
182
- ruleId: RULE_MW_001,
183
- file: middlewarePath,
184
- symbol: "matcher",
185
- }),
186
- severity: "high",
187
- confidence: 0.85,
188
- category: "middleware",
189
- ruleId: RULE_MW_001,
190
- title: "Next.js middleware matcher does not cover API routes",
191
- description: `The middleware.ts file has a matcher configuration that does not include /api routes. Found ${fileIndex.apiRouteFiles.length} API route(s) that are not protected by middleware. Current matcher: ${JSON.stringify(matchers)}`,
192
- evidence,
193
- remediation: {
194
- recommendedFix: `Update the middleware matcher to include API routes. Example: matcher: ['/((?!_next/static|_next/image|favicon.ico).*)', '/api/:path*']`,
195
- },
196
- links: {
197
- owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
198
- },
199
- fingerprint,
200
- });
201
- }
202
- return findings;
203
- }
@@ -1,12 +0,0 @@
1
- import type { Finding } from "@vibecheck/schema";
2
- import type { ScanContext } from "../types.js";
3
- /**
4
- * VC-AUTH-001: Unprotected state-changing API route
5
- *
6
- * Identifies Next.js App Router route handlers that:
7
- * 1. Export POST/PUT/PATCH/DELETE methods
8
- * 2. Contain database write/delete operations or export functionality
9
- * 3. Do NOT contain any server-side auth checks
10
- */
11
- export declare function scanUnprotectedApiRoutes(context: ScanContext): Promise<Finding[]>;
12
- //# sourceMappingURL=unprotected-api-route.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"unprotected-api-route.d.ts","sourceRoot":"","sources":["../../../src/scanners/auth/unprotected-api-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAU,MAAM,aAAa,CAAC;AAqCvD;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAqGvF"}
@@ -1,126 +0,0 @@
1
- import { resolvePath } from "../../utils/file-utils.js";
2
- import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
3
- const RULE_ID = "VC-AUTH-001";
4
- /**
5
- * Methods that modify state and require authentication
6
- */
7
- const STATE_CHANGING_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
8
- /**
9
- * Extract route path from file path
10
- * e.g., app/api/users/route.ts -> /api/users
11
- */
12
- function extractRoutePath(filePath) {
13
- // Normalize slashes
14
- const normalized = filePath.replace(/\\/g, "/");
15
- // Extract from app/api/... pattern
16
- const match = normalized.match(/(?:app|src\/app)(\/api\/[^/]+(?:\/[^/]+)*?)\/route\.[tj]sx?$/);
17
- if (match) {
18
- return match[1];
19
- }
20
- // Fallback: just return the file path
21
- return normalized;
22
- }
23
- /**
24
- * Determine severity based on sink type
25
- */
26
- function getSeverity(sinks) {
27
- const hasCritical = sinks.some((s) => s.isCritical);
28
- return hasCritical ? "critical" : "high";
29
- }
30
- /**
31
- * VC-AUTH-001: Unprotected state-changing API route
32
- *
33
- * Identifies Next.js App Router route handlers that:
34
- * 1. Export POST/PUT/PATCH/DELETE methods
35
- * 2. Contain database write/delete operations or export functionality
36
- * 3. Do NOT contain any server-side auth checks
37
- */
38
- export async function scanUnprotectedApiRoutes(context) {
39
- const { repoRoot, fileIndex, helpers, repoMeta } = context;
40
- const findings = [];
41
- // Only scan Next.js projects
42
- if (repoMeta.framework !== "next") {
43
- return findings;
44
- }
45
- // Scan API route files
46
- for (const relPath of fileIndex.apiRouteFiles) {
47
- const absPath = resolvePath(repoRoot, relPath);
48
- const sourceFile = helpers.parseFile(absPath);
49
- if (!sourceFile)
50
- continue;
51
- const handlers = helpers.findRouteHandlers(sourceFile);
52
- for (const handler of handlers) {
53
- // Only check state-changing methods
54
- if (!STATE_CHANGING_METHODS.includes(handler.method)) {
55
- continue;
56
- }
57
- // Check for auth
58
- const hasAuth = helpers.containsAuthCheck(handler.functionNode);
59
- if (hasAuth) {
60
- continue;
61
- }
62
- // Find database sinks
63
- const sinks = helpers.findDbSinks(handler.functionNode);
64
- // Only flag if there are dangerous sinks
65
- if (sinks.length === 0) {
66
- continue;
67
- }
68
- const routePath = extractRoutePath(relPath);
69
- const severity = getSeverity(sinks);
70
- const evidence = [
71
- {
72
- file: relPath,
73
- startLine: handler.startLine,
74
- endLine: handler.endLine,
75
- snippet: helpers.getNodeText(handler.functionNode).slice(0, 200) + "...",
76
- label: `Unprotected ${handler.method} handler`,
77
- },
78
- ...sinks.slice(0, 2).map((sink) => ({
79
- file: relPath,
80
- startLine: sink.line,
81
- endLine: sink.line,
82
- snippet: sink.snippet,
83
- label: `${sink.kind} ${sink.operation} operation without auth check`,
84
- })),
85
- ];
86
- const fingerprint = generateFingerprint({
87
- ruleId: RULE_ID,
88
- file: relPath,
89
- symbol: handler.method,
90
- route: routePath,
91
- });
92
- const sinkOperations = sinks.map((s) => `${s.kind}.${s.operation}`).join(", ");
93
- findings.push({
94
- id: generateFindingId({
95
- ruleId: RULE_ID,
96
- file: relPath,
97
- symbol: handler.method,
98
- }),
99
- ruleId: RULE_ID,
100
- title: `Unprotected ${handler.method} route: ${routePath}`,
101
- description: `The API route ${routePath} exports a ${handler.method} handler that performs database operations (${sinkOperations}) without any authentication checks. An attacker could invoke this endpoint directly to modify or delete data without authorization.`,
102
- severity,
103
- confidence: 0.88,
104
- category: "auth",
105
- evidence,
106
- remediation: {
107
- recommendedFix: `Add authentication to the ${handler.method} handler. Check for a valid session using getServerSession(), auth(), or similar before performing database operations.`,
108
- patch: `// Add at the start of your handler:
109
- const session = await getServerSession(authOptions);
110
- if (!session) {
111
- return new Response(JSON.stringify({ error: "Unauthorized" }), {
112
- status: 401,
113
- headers: { "Content-Type": "application/json" }
114
- });
115
- }`,
116
- },
117
- links: {
118
- owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
119
- cwe: "https://cwe.mitre.org/data/definitions/306.html",
120
- },
121
- fingerprint,
122
- });
123
- }
124
- }
125
- return findings;
126
- }
@@ -1,5 +0,0 @@
1
- import type { ScannerPack } from "../types.js";
2
- export declare const configPack: ScannerPack;
3
- export { scanUndocumentedEnv, parseEnvExample, findEnvUsages } from "./undocumented-env.js";
4
- export { scanInsecureDefaults } from "./insecure-defaults.js";
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scanners/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,UAAU,EAAE,WAIxB,CAAC;AAGF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1,10 +0,0 @@
1
- import { scanUndocumentedEnv } from "./undocumented-env.js";
2
- import { scanInsecureDefaults } from "./insecure-defaults.js";
3
- export const configPack = {
4
- id: "config",
5
- name: "Configuration & Secrets",
6
- scanners: [scanUndocumentedEnv, scanInsecureDefaults],
7
- };
8
- // Re-export for testing
9
- export { scanUndocumentedEnv, parseEnvExample, findEnvUsages } from "./undocumented-env.js";
10
- export { scanInsecureDefaults } from "./insecure-defaults.js";
@@ -1,12 +0,0 @@
1
- import type { Finding } from "@vibecheck/schema";
2
- import type { ScanContext } from "../types.js";
3
- /**
4
- * VC-CONFIG-002: Insecure default secret fallback
5
- *
6
- * Detects patterns:
7
- * - process.env.* ?? "dev"
8
- * - process.env.* || "dev"
9
- * - Hardcoded secret strings assigned to SECRET/TOKEN/KEY/PASSWORD vars
10
- */
11
- export declare function scanInsecureDefaults(context: ScanContext): Promise<Finding[]>;
12
- //# sourceMappingURL=insecure-defaults.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"insecure-defaults.d.ts","sourceRoot":"","sources":["../../../src/scanners/config/insecure-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAwEnF"}
@@ -1,77 +0,0 @@
1
- import { resolvePath } from "../../utils/file-utils.js";
2
- import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
3
- const RULE_ID = "VC-CONFIG-002";
4
- /**
5
- * VC-CONFIG-002: Insecure default secret fallback
6
- *
7
- * Detects patterns:
8
- * - process.env.* ?? "dev"
9
- * - process.env.* || "dev"
10
- * - Hardcoded secret strings assigned to SECRET/TOKEN/KEY/PASSWORD vars
11
- */
12
- export async function scanInsecureDefaults(context) {
13
- const { repoRoot, fileIndex, helpers } = context;
14
- const findings = [];
15
- // Scan all source files
16
- for (const relPath of fileIndex.allSourceFiles) {
17
- const absPath = resolvePath(repoRoot, relPath);
18
- const sourceFile = helpers.parseFile(absPath);
19
- if (!sourceFile)
20
- continue;
21
- const insecureDefaults = helpers.findInsecureDefaults(sourceFile);
22
- for (const def of insecureDefaults) {
23
- const severity = def.isCritical ? "critical" : "medium";
24
- const evidence = [
25
- {
26
- file: relPath,
27
- startLine: def.line,
28
- endLine: def.line,
29
- snippet: def.snippet,
30
- label: def.isCritical
31
- ? `Critical secret with hardcoded fallback: ${def.envVar}`
32
- : `Environment variable with hardcoded fallback: ${def.envVar}`,
33
- },
34
- ];
35
- const fingerprint = generateFingerprint({
36
- ruleId: RULE_ID,
37
- file: relPath,
38
- symbol: def.envVar,
39
- startLine: def.line,
40
- });
41
- findings.push({
42
- id: generateFindingId({
43
- ruleId: RULE_ID,
44
- file: relPath,
45
- symbol: def.envVar,
46
- startLine: def.line,
47
- }),
48
- ruleId: RULE_ID,
49
- title: `Insecure default for ${def.envVar}`,
50
- description: def.isCritical
51
- ? `The secret ${def.envVar} has a hardcoded fallback value "${def.fallbackValue.slice(0, 20)}${def.fallbackValue.length > 20 ? "..." : ""}". This is a critical security issue as the fallback value may be used in production if the environment variable is not set, potentially exposing a weak or predictable secret.`
52
- : `The environment variable ${def.envVar} has a hardcoded fallback value. While this may be intended for development, it could lead to insecure defaults being used in production.`,
53
- severity,
54
- confidence: def.isCritical ? 0.95 : 0.8,
55
- category: "config",
56
- evidence,
57
- remediation: {
58
- recommendedFix: `Remove the hardcoded fallback and require the environment variable to be explicitly set. Add validation at startup to fail fast if required secrets are missing.`,
59
- patch: `// Instead of:
60
- const secret = process.env.${def.envVar} || "hardcoded";
61
-
62
- // Do:
63
- const secret = process.env.${def.envVar};
64
- if (!secret) {
65
- throw new Error("${def.envVar} environment variable is required");
66
- }`,
67
- },
68
- links: {
69
- cwe: "https://cwe.mitre.org/data/definitions/798.html",
70
- owasp: "https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/",
71
- },
72
- fingerprint,
73
- });
74
- }
75
- }
76
- return findings;
77
- }
@@ -1,24 +0,0 @@
1
- import type { Finding } from "@vibecheck/schema";
2
- import type { ScanContext } from "../types.js";
3
- interface EnvUsage {
4
- name: string;
5
- file: string;
6
- line: number;
7
- snippet: string;
8
- }
9
- /**
10
- * Parse .env.example file and extract defined variable names
11
- */
12
- export declare function parseEnvExample(content: string): Set<string>;
13
- /**
14
- * Find all process.env usages in source files
15
- */
16
- export declare function findEnvUsages(sourceFiles: string[], targetDir: string): EnvUsage[];
17
- /**
18
- * VC-CONFIG-001: Undocumented environment variable
19
- *
20
- * Finds process.env usage and checks if variables are documented in .env.example
21
- */
22
- export declare function scanUndocumentedEnv(context: ScanContext): Promise<Finding[]>;
23
- export {};
24
- //# sourceMappingURL=undocumented-env.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"undocumented-env.d.ts","sourceRoot":"","sources":["../../../src/scanners/config/undocumented-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,mBAAmB,CAAC;AAGzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoB/C,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,EAAE,MAAM,GAChB,QAAQ,EAAE,CA6CZ;AASD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAmFlF"}
@@ -1,159 +0,0 @@
1
- import { readFileSync, resolvePath } from "../../utils/file-utils.js";
2
- import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
3
- const RULE_ID = "VC-CONFIG-001";
4
- /**
5
- * Patterns that indicate a secret-like environment variable
6
- */
7
- const SECRET_PATTERNS = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|CREDENTIAL|API_KEY|AUTH/i;
8
- /**
9
- * Regex to find process.env.VARIABLE_NAME usage
10
- * Captures the variable name in group 1
11
- */
12
- const PROCESS_ENV_REGEX = /process\.env\.([A-Z][A-Z0-9_]*)/g;
13
- /**
14
- * Regex to find process.env["VARIABLE"] or process.env['VARIABLE'] usage
15
- */
16
- const PROCESS_ENV_BRACKET_REGEX = /process\.env\[['"]([A-Z][A-Z0-9_]*)['"]\]/g;
17
- /**
18
- * Parse .env.example file and extract defined variable names
19
- */
20
- export function parseEnvExample(content) {
21
- const vars = new Set();
22
- const lines = content.split("\n");
23
- for (const line of lines) {
24
- const trimmed = line.trim();
25
- // Skip comments and empty lines
26
- if (trimmed.startsWith("#") || trimmed === "")
27
- continue;
28
- // Match VAR_NAME= or VAR_NAME (without value)
29
- const match = trimmed.match(/^([A-Z][A-Z0-9_]*)(?:=|$)/);
30
- if (match) {
31
- vars.add(match[1]);
32
- }
33
- }
34
- return vars;
35
- }
36
- /**
37
- * Find all process.env usages in source files
38
- */
39
- export function findEnvUsages(sourceFiles, targetDir) {
40
- const usages = [];
41
- for (const relFile of sourceFiles) {
42
- const absPath = resolvePath(targetDir, relFile);
43
- const content = readFileSync(absPath);
44
- if (!content)
45
- continue;
46
- const lines = content.split("\n");
47
- // Find dot notation: process.env.VAR_NAME
48
- for (const match of content.matchAll(PROCESS_ENV_REGEX)) {
49
- const varName = match[1];
50
- const index = match.index;
51
- // Find line number
52
- const beforeMatch = content.slice(0, index);
53
- const lineNumber = beforeMatch.split("\n").length;
54
- usages.push({
55
- name: varName,
56
- file: relFile,
57
- line: lineNumber,
58
- snippet: lines[lineNumber - 1]?.trim() ?? "",
59
- });
60
- }
61
- // Find bracket notation: process.env["VAR_NAME"]
62
- for (const match of content.matchAll(PROCESS_ENV_BRACKET_REGEX)) {
63
- const varName = match[1];
64
- const index = match.index;
65
- const beforeMatch = content.slice(0, index);
66
- const lineNumber = beforeMatch.split("\n").length;
67
- usages.push({
68
- name: varName,
69
- file: relFile,
70
- line: lineNumber,
71
- snippet: lines[lineNumber - 1]?.trim() ?? "",
72
- });
73
- }
74
- }
75
- return usages;
76
- }
77
- /**
78
- * Determine severity based on variable name
79
- */
80
- function getSeverity(varName) {
81
- return SECRET_PATTERNS.test(varName) ? "high" : "medium";
82
- }
83
- /**
84
- * VC-CONFIG-001: Undocumented environment variable
85
- *
86
- * Finds process.env usage and checks if variables are documented in .env.example
87
- */
88
- export async function scanUndocumentedEnv(context) {
89
- const { repoRoot, fileIndex } = context;
90
- const findings = [];
91
- // Check for .env.example
92
- const envExamplePath = resolvePath(repoRoot, ".env.example");
93
- const envExampleContent = readFileSync(envExamplePath);
94
- // Get documented env vars (empty set if no .env.example)
95
- const documentedVars = envExampleContent
96
- ? parseEnvExample(envExampleContent)
97
- : new Set();
98
- // Find all env usages
99
- const usages = findEnvUsages(fileIndex.allSourceFiles, repoRoot);
100
- // Group usages by variable name
101
- const usagesByVar = new Map();
102
- for (const usage of usages) {
103
- const existing = usagesByVar.get(usage.name) ?? [];
104
- existing.push(usage);
105
- usagesByVar.set(usage.name, existing);
106
- }
107
- // Check each variable
108
- for (const [varName, varUsages] of usagesByVar) {
109
- if (documentedVars.has(varName)) {
110
- continue; // Variable is documented, no finding
111
- }
112
- const severity = getSeverity(varName);
113
- const firstUsage = varUsages[0];
114
- const evidence = varUsages.slice(0, 3).map((u) => ({
115
- file: u.file,
116
- startLine: u.line,
117
- endLine: u.line,
118
- snippet: u.snippet,
119
- label: `Usage of process.env.${varName}`,
120
- }));
121
- if (varUsages.length > 3) {
122
- evidence.push({
123
- file: varUsages[3].file,
124
- startLine: varUsages[3].line,
125
- endLine: varUsages[3].line,
126
- label: `...and ${varUsages.length - 3} more usages`,
127
- });
128
- }
129
- const fingerprint = generateFingerprint({
130
- ruleId: RULE_ID,
131
- file: firstUsage.file,
132
- symbol: varName,
133
- });
134
- const hasEnvExample = envExampleContent !== null;
135
- findings.push({
136
- id: generateFindingId({
137
- ruleId: RULE_ID,
138
- file: firstUsage.file,
139
- symbol: varName,
140
- }),
141
- severity,
142
- confidence: 0.85,
143
- category: "config",
144
- ruleId: RULE_ID,
145
- title: `Undocumented environment variable: ${varName}`,
146
- description: hasEnvExample
147
- ? `The environment variable "${varName}" is used in the codebase but is not listed in .env.example. This can lead to deployment issues or confusion for other developers.`
148
- : `The environment variable "${varName}" is used but no .env.example file exists. Consider creating one to document required configuration.`,
149
- evidence,
150
- remediation: {
151
- recommendedFix: hasEnvExample
152
- ? `Add "${varName}=" to .env.example with an appropriate default or placeholder value.`
153
- : `Create a .env.example file and add "${varName}=" with documentation.`,
154
- },
155
- fingerprint,
156
- });
157
- }
158
- return findings;
159
- }
@@ -1,6 +0,0 @@
1
- import type { ScannerPack } from "../types.js";
2
- export declare const cryptoPack: ScannerPack;
3
- export { scanMathRandomTokens } from "./math-random-tokens.js";
4
- export { scanJwtDecodeUnverified } from "./jwt-decode-unverified.js";
5
- export { scanWeakHashing } from "./weak-hashing.js";
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scanners/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,eAAO,MAAM,UAAU,EAAE,WAIxB,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,11 +0,0 @@
1
- import { scanMathRandomTokens } from "./math-random-tokens.js";
2
- import { scanJwtDecodeUnverified } from "./jwt-decode-unverified.js";
3
- import { scanWeakHashing } from "./weak-hashing.js";
4
- export const cryptoPack = {
5
- id: "crypto",
6
- name: "Cryptography Security",
7
- scanners: [scanMathRandomTokens, scanJwtDecodeUnverified, scanWeakHashing],
8
- };
9
- export { scanMathRandomTokens } from "./math-random-tokens.js";
10
- export { scanJwtDecodeUnverified } from "./jwt-decode-unverified.js";
11
- export { scanWeakHashing } from "./weak-hashing.js";