@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
package/README.md ADDED
@@ -0,0 +1,839 @@
1
+ # vibecheck
2
+
3
+ A deterministic, local-only security scanner for modern web applications. Designed to catch common security issues in Next.js, Express, and other Node.js projects with high precision and low false positives.
4
+
5
+ ## Quickstart (No Install)
6
+
7
+ Run VibeCheck instantly without installation:
8
+
9
+ ```bash
10
+ # Using npx
11
+ npx vibecheck scan --fail-on off --out vibecheck-scan.json
12
+
13
+ # Using pnpm dlx
14
+ pnpm dlx vibecheck scan --fail-on off --out vibecheck-scan.json
15
+ ```
16
+
17
+ ### Scan Another Folder
18
+
19
+ ```bash
20
+ npx vibecheck scan --target ../my-other-app --out scan.json
21
+ ```
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install -g vibecheck
27
+ # or
28
+ pnpm add -g vibecheck
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```bash
34
+ # Scan current directory
35
+ vibecheck scan
36
+
37
+ # Scan specific directory with output file
38
+ vibecheck scan ./my-project --out ./reports/scan.json
39
+
40
+ # Use --target as alternative to positional argument
41
+ vibecheck scan --target ./my-project
42
+
43
+ # Output in SARIF format (for GitHub Code Scanning)
44
+ vibecheck scan --format sarif
45
+
46
+ # Output both JSON and SARIF
47
+ vibecheck scan --format both
48
+
49
+ # Fail CI if medium or higher findings
50
+ vibecheck scan --fail-on medium
51
+
52
+ # Disable fail threshold (always exit 0)
53
+ vibecheck scan --fail-on off
54
+
55
+ # Exclude specific directories
56
+ vibecheck scan -e "**/legacy/**" -e "**/vendor/**"
57
+
58
+ # Include test files in scan (excluded by default)
59
+ vibecheck scan --include-tests
60
+
61
+ # Generate intent map with coverage metrics
62
+ vibecheck scan --emit-intent-map
63
+
64
+ # Explain a scan report
65
+ vibecheck explain ./scan.json
66
+ ```
67
+
68
+ ### Command Line Options
69
+
70
+ | Option | Description | Default |
71
+ |--------|-------------|---------|
72
+ | `-t, --target <path>` | Target directory to scan | Current directory |
73
+ | `-o, --out <path>` | Output file or directory | `vibecheck-artifacts/vibecheck-scan.json` |
74
+ | `-f, --format <format>` | Output format: `json`, `sarif`, or `both` | `json` |
75
+ | `--repo-name <name>` | Override repository name | Auto-detected |
76
+ | `--fail-on <threshold>` | Exit with non-zero if findings >= threshold | `high` |
77
+ | `-e, --exclude <glob>` | Glob pattern to exclude (repeatable) | See below |
78
+ | `--include-tests` | Include test files in scan | `false` |
79
+ | `--emit-intent-map` | Include route map and coverage metrics | `false` |
80
+ | `--changed` | Only scan changed files (not implemented) | `false` |
81
+
82
+ ### Default Excludes
83
+
84
+ The following patterns are excluded by default:
85
+
86
+ **Core excludes (always applied):**
87
+ - `node_modules`, `dist`, `.git`, `build`, `.next`, `coverage`
88
+ - `.turbo`, `.cache`, `out`, `.vercel`, `.netlify`
89
+
90
+ **Test excludes (skipped with `--include-tests`):**
91
+ - `__tests__/**`, `*.test.*`, `*.spec.*`
92
+ - `test/`, `tests/`, `fixtures/`, `__mocks__/`, `__fixtures__/`
93
+ - `cypress/`, `e2e/`, `*.stories.*`
94
+
95
+ ## Scanner Packs
96
+
97
+ VibeCheck organizes security rules into modular scanner packs. Each pack focuses on a specific security domain.
98
+
99
+ ### Auth Pack
100
+
101
+ Rules for authentication and authorization issues.
102
+
103
+ #### VC-AUTH-001: Unprotected State-Changing API Route
104
+
105
+ **Severity:** High / Critical
106
+ **Category:** auth
107
+
108
+ Detects Next.js App Router API route handlers (POST, PUT, PATCH, DELETE) that perform database operations without authentication checks.
109
+
110
+ **What it looks for:**
111
+ - Route handlers in `app/**/route.ts` files
112
+ - Handlers that use Prisma, Drizzle, or other database operations
113
+ - Missing calls to `getServerSession`, `auth()`, or similar auth checks
114
+
115
+ **Example (vulnerable):**
116
+ ```typescript
117
+ // app/api/users/route.ts
118
+ export async function POST(request: Request) {
119
+ const body = await request.json();
120
+ await prisma.user.create({ data: body }); // No auth check!
121
+ return Response.json({ success: true });
122
+ }
123
+ ```
124
+
125
+ **Example (safe):**
126
+ ```typescript
127
+ // app/api/users/route.ts
128
+ export async function POST(request: Request) {
129
+ const session = await getServerSession();
130
+ if (!session) {
131
+ return Response.json({ error: "Unauthorized" }, { status: 401 });
132
+ }
133
+ const body = await request.json();
134
+ await prisma.user.create({ data: body });
135
+ return Response.json({ success: true });
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ #### VC-MW-001: Middleware Matcher Gap
142
+
143
+ **Severity:** High
144
+ **Category:** middleware
145
+
146
+ Detects Next.js middleware that doesn't cover API routes, potentially leaving them unprotected.
147
+
148
+ **What it looks for:**
149
+ - `middleware.ts` files with `config.matcher` exports
150
+ - Matchers that exclude `/api` routes
151
+ - Projects using next-auth without middleware protection
152
+
153
+ **Example (vulnerable):**
154
+ ```typescript
155
+ // middleware.ts
156
+ export const config = {
157
+ matcher: ['/dashboard/:path*'] // Missing /api routes!
158
+ };
159
+ ```
160
+
161
+ **Example (safe):**
162
+ ```typescript
163
+ // middleware.ts
164
+ export const config = {
165
+ matcher: ['/api/:path*', '/dashboard/:path*']
166
+ };
167
+ ```
168
+
169
+ ---
170
+
171
+ ### Validation Pack
172
+
173
+ Rules for input validation issues.
174
+
175
+ #### VC-VAL-001: Validation Defined But Output Ignored
176
+
177
+ **Severity:** Medium
178
+ **Category:** validation
179
+
180
+ Detects cases where validation libraries (Zod, Yup, Joi) are called but the validated result is not used.
181
+
182
+ **What it looks for:**
183
+ - Calls to `.parse()`, `.validate()`, `.parseAsync()`, etc.
184
+ - Result not assigned to a variable
185
+ - Raw `request.body` or `req.body` used after validation
186
+
187
+ **Example (vulnerable):**
188
+ ```typescript
189
+ const schema = z.object({ name: z.string() });
190
+ schema.parse(body); // Result ignored!
191
+ await prisma.user.create({ data: body }); // Uses unvalidated body
192
+ ```
193
+
194
+ **Example (safe):**
195
+ ```typescript
196
+ const schema = z.object({ name: z.string() });
197
+ const validated = schema.parse(body);
198
+ await prisma.user.create({ data: validated });
199
+ ```
200
+
201
+ ---
202
+
203
+ ### Privacy Pack
204
+
205
+ Rules for data privacy and logging issues.
206
+
207
+ #### VC-PRIV-001: Sensitive Data Logged
208
+
209
+ **Severity:** High
210
+ **Category:** privacy
211
+
212
+ Detects logging statements that include sensitive variable names.
213
+
214
+ **What it looks for:**
215
+ - `console.log`, `console.info`, `console.debug`, `logger.info`, etc.
216
+ - Variables containing: password, secret, token, apiKey, creditCard, ssn, etc.
217
+
218
+ **Example (vulnerable):**
219
+ ```typescript
220
+ console.log("User login:", { email, password }); // Logs password!
221
+ ```
222
+
223
+ **Example (safe):**
224
+ ```typescript
225
+ console.log("User login:", { email, timestamp: Date.now() });
226
+ ```
227
+
228
+ ---
229
+
230
+ ### Config Pack
231
+
232
+ Rules for configuration and secrets management issues.
233
+
234
+ #### VC-CONFIG-001: Undocumented Environment Variable
235
+
236
+ **Severity:** Low
237
+ **Category:** config
238
+
239
+ Detects `process.env.VAR` references that aren't documented in `.env.example`.
240
+
241
+ ---
242
+
243
+ #### VC-CONFIG-002: Insecure Default Secret Fallback
244
+
245
+ **Severity:** Critical
246
+ **Category:** secrets
247
+
248
+ Detects hardcoded fallback values for security-critical environment variables.
249
+
250
+ **What it looks for:**
251
+ - `process.env.VAR || "fallback"` patterns
252
+ - Variables named: SECRET, KEY, TOKEN, PASSWORD, etc.
253
+ - Hardcoded string fallbacks
254
+
255
+ **Example (vulnerable):**
256
+ ```typescript
257
+ const jwtSecret = process.env.JWT_SECRET || "development-secret";
258
+ ```
259
+
260
+ **Example (safe):**
261
+ ```typescript
262
+ const jwtSecret = process.env.JWT_SECRET;
263
+ if (!jwtSecret) throw new Error("JWT_SECRET is required");
264
+ ```
265
+
266
+ ---
267
+
268
+ ### Network Pack
269
+
270
+ Rules for network security issues.
271
+
272
+ #### VC-NET-001: SSRF-Prone Fetch
273
+
274
+ **Severity:** High
275
+ **Category:** network
276
+
277
+ Detects fetch/axios calls where the URL is derived from user input without validation.
278
+
279
+ **What it looks for:**
280
+ - `fetch()` or `axios.get()` calls
281
+ - URL constructed from request parameters, query strings, or body
282
+ - No URL validation or allowlist checks
283
+
284
+ **Example (vulnerable):**
285
+ ```typescript
286
+ export async function GET(request: Request) {
287
+ const { searchParams } = new URL(request.url);
288
+ const url = searchParams.get("url");
289
+ const response = await fetch(url); // SSRF risk!
290
+ return Response.json(await response.json());
291
+ }
292
+ ```
293
+
294
+ **Example (safe):**
295
+ ```typescript
296
+ const ALLOWED_HOSTS = ["api.example.com", "cdn.example.com"];
297
+
298
+ export async function GET(request: Request) {
299
+ const { searchParams } = new URL(request.url);
300
+ const url = searchParams.get("url");
301
+ const parsed = new URL(url);
302
+ if (!ALLOWED_HOSTS.includes(parsed.hostname)) {
303
+ return Response.json({ error: "Invalid host" }, { status: 400 });
304
+ }
305
+ const response = await fetch(url);
306
+ return Response.json(await response.json());
307
+ }
308
+ ```
309
+
310
+ ---
311
+
312
+ ### Hallucinations Pack
313
+
314
+ Rules for detecting security libraries that are imported but not properly used.
315
+
316
+ #### VC-HALL-001: Security Library Imported But Not Used
317
+
318
+ **Severity:** Medium
319
+ **Category:** middleware
320
+
321
+ Detects security libraries (helmet, cors, csurf, etc.) that are imported but the import is never used.
322
+
323
+ **What it looks for:**
324
+ - Imports from security packages: helmet, cors, csurf, express-rate-limit, hpp, etc.
325
+ - Import identifier not referenced after the import statement
326
+
327
+ **Example (vulnerable):**
328
+ ```typescript
329
+ import helmet from "helmet"; // Imported but never used!
330
+ import cors from "cors";
331
+
332
+ const app = express();
333
+ app.use(cors());
334
+ // Missing: app.use(helmet());
335
+ ```
336
+
337
+ ---
338
+
339
+ #### VC-HALL-002: NextAuth Imported But Not Enforced
340
+
341
+ **Severity:** High
342
+ **Category:** auth
343
+
344
+ Detects next-auth imported but `getServerSession` never called, suggesting auth is configured but not enforced.
345
+
346
+ **What it looks for:**
347
+ - Imports from `next-auth` or `next-auth/next`
348
+ - No calls to `getServerSession` anywhere in the file
349
+
350
+ ---
351
+
352
+ ## Phase 2 Rules
353
+
354
+ ### Network Pack (Extended)
355
+
356
+ #### VC-NET-002: Open Redirect
357
+
358
+ **Severity:** High
359
+ **Category:** network
360
+
361
+ Detects server-side redirects where user-controlled input determines the destination.
362
+
363
+ **Two-signal requirement:** Must identify user-controlled source AND redirect call uses that value.
364
+
365
+ **Example (vulnerable):**
366
+ ```typescript
367
+ export async function GET(request: Request) {
368
+ const { searchParams } = new URL(request.url);
369
+ const next = searchParams.get("next");
370
+ return NextResponse.redirect(next!); // Open redirect!
371
+ }
372
+ ```
373
+
374
+ ---
375
+
376
+ #### VC-NET-003: Over-permissive CORS with Credentials
377
+
378
+ **Severity:** High
379
+ **Category:** network
380
+
381
+ Detects CORS configurations that combine `origin: "*"` with `credentials: true`.
382
+
383
+ **Example (vulnerable):**
384
+ ```typescript
385
+ cors({ origin: "*", credentials: true }) // Dangerous combination!
386
+ ```
387
+
388
+ ---
389
+
390
+ #### VC-NET-004: Missing Request Timeout
391
+
392
+ **Severity:** Low
393
+ **Category:** network
394
+
395
+ Detects fetch/axios calls without timeout in API route handlers.
396
+
397
+ **Example (vulnerable):**
398
+ ```typescript
399
+ const response = await fetch("https://external-api.com/data"); // No timeout!
400
+ ```
401
+
402
+ ---
403
+
404
+ ### Middleware Pack
405
+
406
+ #### VC-RATE-001: Missing Rate Limiting
407
+
408
+ **Severity:** Medium
409
+ **Category:** middleware
410
+ **Confidence:** 0.65
411
+
412
+ Detects unauthenticated state-changing endpoints without rate limiting.
413
+
414
+ **What it looks for:**
415
+ - POST/PUT/PATCH/DELETE handlers without auth checks
416
+ - Handlers with database writes or sensitive operations
417
+ - No rate limiting signals in handler or middleware
418
+
419
+ ---
420
+
421
+ ### Validation Pack (Extended)
422
+
423
+ #### VC-VAL-002: Client-Side Only Validation
424
+
425
+ **Severity:** Medium
426
+ **Category:** validation
427
+
428
+ Detects validation in frontend components but missing in API routes.
429
+
430
+ ---
431
+
432
+ ### Privacy Pack (Extended)
433
+
434
+ #### VC-PRIV-002: Over-broad API Response
435
+
436
+ **Severity:** Medium/High
437
+ **Category:** privacy
438
+
439
+ Detects Prisma queries returning full models without `select` restrictions.
440
+
441
+ **Example (vulnerable):**
442
+ ```typescript
443
+ const users = await prisma.user.findMany(); // Returns password hash!
444
+ return Response.json(users);
445
+ ```
446
+
447
+ **Example (safe):**
448
+ ```typescript
449
+ const users = await prisma.user.findMany({
450
+ select: { id: true, name: true, email: true }
451
+ });
452
+ return Response.json(users);
453
+ ```
454
+
455
+ ---
456
+
457
+ #### VC-PRIV-003: Debug Flags in Production
458
+
459
+ **Severity:** Medium
460
+ **Category:** config
461
+
462
+ Detects `debug: true` or `dev: true` in config files without NODE_ENV guards.
463
+
464
+ ---
465
+
466
+ ### Crypto Pack
467
+
468
+ #### VC-CRYPTO-001: Math.random for Tokens
469
+
470
+ **Severity:** High
471
+ **Category:** crypto
472
+
473
+ Detects Math.random used to generate tokens, keys, or session IDs.
474
+
475
+ **Example (vulnerable):**
476
+ ```typescript
477
+ const token = Math.random().toString(36).substring(2); // Predictable!
478
+ ```
479
+
480
+ **Example (safe):**
481
+ ```typescript
482
+ const token = crypto.randomBytes(32).toString('hex');
483
+ ```
484
+
485
+ ---
486
+
487
+ #### VC-CRYPTO-002: JWT Decode Without Verify
488
+
489
+ **Severity:** Critical
490
+ **Category:** crypto
491
+
492
+ Detects jwt.decode() used without jwt.verify() in the same file.
493
+
494
+ **Example (vulnerable):**
495
+ ```typescript
496
+ const payload = jwt.decode(token); // Signature not verified!
497
+ ```
498
+
499
+ **Example (safe):**
500
+ ```typescript
501
+ const payload = jwt.verify(token, secret); // Signature verified
502
+ ```
503
+
504
+ ---
505
+
506
+ #### VC-CRYPTO-003: Weak Password Hashing
507
+
508
+ **Severity:** High
509
+ **Category:** crypto
510
+
511
+ Detects MD5/SHA1 for passwords or bcrypt with saltRounds < 10.
512
+
513
+ **Example (vulnerable):**
514
+ ```typescript
515
+ crypto.createHash('md5').update(password).digest('hex'); // Weak!
516
+ bcrypt.hash(password, 5); // Too few rounds!
517
+ ```
518
+
519
+ ---
520
+
521
+ ### Uploads Pack
522
+
523
+ #### VC-UP-001: File Upload Without Constraints
524
+
525
+ **Severity:** High
526
+ **Category:** uploads
527
+
528
+ Detects file uploads without size or type validation.
529
+
530
+ **Example (vulnerable):**
531
+ ```typescript
532
+ const file = formData.get('file') as File;
533
+ // No size or type check before processing
534
+ ```
535
+
536
+ ---
537
+
538
+ #### VC-UP-002: Upload to Public Path
539
+
540
+ **Severity:** High
541
+ **Category:** uploads
542
+
543
+ Detects uploaded files written to public directories.
544
+
545
+ **Example (vulnerable):**
546
+ ```typescript
547
+ fs.writeFileSync(`public/uploads/${filename}`, buffer); // Publicly accessible!
548
+ ```
549
+
550
+ ---
551
+
552
+ ## Phase 3: Hallucination Detection Engine
553
+
554
+ Advanced cross-file analysis for detecting security intent vs implementation gaps.
555
+
556
+ ### Intent Command
557
+
558
+ Generate a security intent map baseline for your codebase:
559
+
560
+ ```bash
561
+ # Generate intent map
562
+ vibecheck intent ./my-project --out intent-map.json
563
+
564
+ # Include intent map in scan output
565
+ vibecheck scan ./my-project --emit-intent-map
566
+ ```
567
+
568
+ ### Hallucinations Pack (Phase 3)
569
+
570
+ #### VC-HALL-010: Comment Claims Protection But Unproven
571
+
572
+ **Severity:** Medium
573
+ **Category:** hallucinations
574
+ **Confidence:** 0.75
575
+
576
+ Detects comments that claim security protection but the implementation doesn't prove it.
577
+
578
+ **Example (vulnerable):**
579
+ ```typescript
580
+ // This route is protected by authentication middleware
581
+ export async function POST(request: Request) {
582
+ // No auth check here, and middleware doesn't cover /api routes
583
+ await prisma.user.create({ data: body });
584
+ }
585
+ ```
586
+
587
+ ---
588
+
589
+ #### VC-HALL-011: Middleware Assumed But Not Matching
590
+
591
+ **Severity:** High
592
+ **Category:** hallucinations
593
+ **Confidence:** 0.70
594
+
595
+ Detects routes that expect middleware protection but are not covered by matcher patterns.
596
+
597
+ **Example (vulnerable):**
598
+ ```typescript
599
+ // middleware.ts
600
+ export const config = {
601
+ matcher: ['/dashboard/:path*'], // Missing /api routes!
602
+ };
603
+
604
+ // app/api/users/route.ts
605
+ // Auth handled by middleware (but middleware doesn't cover this!)
606
+ export async function DELETE(request: Request) {
607
+ await prisma.user.delete({ where: { id } });
608
+ }
609
+ ```
610
+
611
+ ---
612
+
613
+ #### VC-HALL-012: Validation Claimed But Missing/Ignored
614
+
615
+ **Severity:** Medium
616
+ **Category:** hallucinations
617
+ **Confidence:** 0.80
618
+
619
+ Detects validation that is claimed but not properly implemented or used.
620
+
621
+ **Example (vulnerable):**
622
+ ```typescript
623
+ const schema = z.object({ name: z.string() });
624
+ schema.parse(body); // Result ignored!
625
+ await prisma.user.create({ data: body }); // Uses raw body
626
+ ```
627
+
628
+ ---
629
+
630
+ #### VC-AUTH-010: Auth-by-UI with Server Gap
631
+
632
+ **Severity:** Critical
633
+ **Category:** auth
634
+ **Confidence:** 0.85
635
+
636
+ Detects client-side auth checks without corresponding server-side protection.
637
+
638
+ **Example (vulnerable):**
639
+ ```tsx
640
+ // Client component
641
+ const { session } = useSession();
642
+ if (session) {
643
+ // Only render if logged in
644
+ await fetch('/api/users', { method: 'DELETE' }); // Server has no auth!
645
+ }
646
+ ```
647
+
648
+ ---
649
+
650
+ ### Coverage Metrics
651
+
652
+ With `--emit-intent-map`, the scan artifact includes coverage metrics:
653
+
654
+ - **authCoverage**: Percentage of state-changing routes with proven auth
655
+ - **validationCoverage**: Percentage of routes with request bodies that have validation
656
+ - **middlewareCoverage**: Percentage of routes covered by middleware matchers
657
+
658
+ ### Intent Map Structure
659
+
660
+ ```json
661
+ {
662
+ "routeMap": [
663
+ {
664
+ "routeId": "abc123",
665
+ "method": "POST",
666
+ "path": "/api/users",
667
+ "file": "app/api/users/route.ts",
668
+ "startLine": 10,
669
+ "endLine": 25
670
+ }
671
+ ],
672
+ "middlewareMap": [
673
+ {
674
+ "file": "middleware.ts",
675
+ "matchers": ["/api/:path*"],
676
+ "protectsApi": true,
677
+ "startLine": 15
678
+ }
679
+ ],
680
+ "intentMap": [
681
+ {
682
+ "intentId": "def456",
683
+ "type": "AUTH_ENFORCED",
684
+ "scope": "route",
685
+ "source": "comment",
686
+ "textEvidence": "// Protected by auth"
687
+ }
688
+ ],
689
+ "proofTraces": {
690
+ "abc123": {
691
+ "routeId": "abc123",
692
+ "authProven": true,
693
+ "validationProven": false,
694
+ "middlewareCovered": true,
695
+ "steps": [...]
696
+ }
697
+ },
698
+ "coverage": {
699
+ "authCoverage": 0.85,
700
+ "validationCoverage": 0.60,
701
+ "middlewareCoverage": 1.0
702
+ }
703
+ }
704
+ ```
705
+
706
+ ---
707
+
708
+ ## Output Formats
709
+
710
+ VibeCheck supports two output formats: JSON and SARIF.
711
+
712
+ ### JSON Format
713
+
714
+ The default format, defined by `@vibecheck/schema`:
715
+
716
+ ```json
717
+ {
718
+ "artifactVersion": "0.1",
719
+ "generatedAt": "2024-01-15T10:30:00.000Z",
720
+ "tool": {
721
+ "name": "vibecheck",
722
+ "version": "1.0.0"
723
+ },
724
+ "summary": {
725
+ "totalFindings": 2,
726
+ "bySeverity": { "critical": 0, "high": 1, "medium": 1, "low": 0, "info": 0 },
727
+ "byCategory": { "auth": 1, "validation": 1, ... }
728
+ },
729
+ "findings": [
730
+ {
731
+ "id": "f-abc123",
732
+ "severity": "high",
733
+ "confidence": 0.85,
734
+ "category": "auth",
735
+ "ruleId": "VC-AUTH-001",
736
+ "title": "Missing authentication on POST /api/users",
737
+ "description": "...",
738
+ "evidence": [
739
+ {
740
+ "file": "app/api/users/route.ts",
741
+ "startLine": 25,
742
+ "endLine": 30,
743
+ "snippet": "...",
744
+ "label": "Unprotected route handler"
745
+ }
746
+ ],
747
+ "remediation": {
748
+ "recommendedFix": "Add authentication middleware"
749
+ },
750
+ "fingerprint": "sha256:..."
751
+ }
752
+ ],
753
+ "metrics": {
754
+ "filesScanned": 50,
755
+ "linesOfCode": 5000,
756
+ "scanDurationMs": 1234
757
+ }
758
+ }
759
+ ```
760
+
761
+ ### SARIF Format
762
+
763
+ [SARIF (Static Analysis Results Interchange Format)](https://sarifweb.azurewebsites.net/) is an OASIS standard for static analysis tools. Use `--format sarif` to generate SARIF 2.1.0 output, compatible with:
764
+
765
+ - **GitHub Code Scanning** - Upload via `github/codeql-action/upload-sarif`
766
+ - **Azure DevOps** - Native SARIF support in security reports
767
+ - **VS Code** - SARIF Viewer extension
768
+ - **Other tools** - Any SARIF 2.1.0 compatible viewer
769
+
770
+ ```bash
771
+ # Generate SARIF for GitHub Code Scanning
772
+ vibecheck scan --format sarif --out results.sarif
773
+
774
+ # Upload to GitHub (in CI workflow)
775
+ - uses: github/codeql-action/upload-sarif@v2
776
+ with:
777
+ sarif_file: results.sarif
778
+ ```
779
+
780
+ ## Architecture
781
+
782
+ ```
783
+ packages/cli/src/
784
+ ├── commands/
785
+ │ ├── scan.ts # Main scan orchestrator
786
+ │ └── explain.ts # Report viewer
787
+ ├── scanners/
788
+ │ ├── types.ts # ScanContext, types
789
+ │ ├── helpers/
790
+ │ │ ├── ast-helpers.ts # ts-morph utilities
791
+ │ │ └── context-builder.ts # ScanContext factory
792
+ │ ├── auth/
793
+ │ │ ├── unprotected-api-route.ts # VC-AUTH-001
794
+ │ │ └── middleware-gap.ts # VC-MW-001
795
+ │ ├── validation/
796
+ │ │ └── ignored-validation.ts # VC-VAL-001
797
+ │ ├── privacy/
798
+ │ │ └── sensitive-logging.ts # VC-PRIV-001
799
+ │ ├── config/
800
+ │ │ ├── undocumented-env.ts # VC-CONFIG-001
801
+ │ │ └── insecure-defaults.ts # VC-CONFIG-002
802
+ │ ├── network/
803
+ │ │ └── ssrf-prone-fetch.ts # VC-NET-001
804
+ │ └── hallucinations/
805
+ │ └── unused-security-imports.ts # VC-HALL-001, VC-HALL-002
806
+ └── index.ts
807
+ ```
808
+
809
+ ## Design Principles
810
+
811
+ 1. **Deterministic** - No LLM calls, results are reproducible
812
+ 2. **Local-only** - All analysis runs on your machine
813
+ 3. **Low false positives** - Precision over recall
814
+ 4. **Framework-aware** - Built for Next.js, Express patterns
815
+ 5. **Schema-compliant** - Output conforms to `@vibecheck/schema`
816
+
817
+ ## Adding New Scanners
818
+
819
+ Each scanner must:
820
+
821
+ 1. Accept a `ScanContext` with repo info and AST helpers
822
+ 2. Return `Finding[]` conforming to the schema
823
+ 3. Generate deterministic fingerprints for deduplication
824
+ 4. Include clear evidence with file locations
825
+
826
+ ```typescript
827
+ import { type ScanContext } from "../types.js";
828
+ import { type Finding } from "@vibecheck/schema";
829
+
830
+ export async function scanMyRule(ctx: ScanContext): Promise<Finding[]> {
831
+ const findings: Finding[] = [];
832
+ // ... detection logic
833
+ return findings;
834
+ }
835
+ ```
836
+
837
+ ## License
838
+
839
+ MIT