@quantracode/vibecheck 0.4.2 → 0.5.0

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.
package/README.md CHANGED
@@ -167,6 +167,7 @@ vibecheck view
167
167
  | `--apply-fixes` | Apply patches from findings after scan | `false` |
168
168
  | `--force` | Skip confirmation when applying patches | `false` |
169
169
  | `-r, --rules <path>` | Load custom YAML rules from directory or file | - |
170
+ | `--no-enhance` | Disable AI-native developer enhancements | `false` |
170
171
 
171
172
  ### Default Excludes
172
173
 
@@ -629,6 +630,27 @@ With `--emit-intent-map`, the scan artifact includes coverage metrics:
629
630
  ```bash
630
631
  # Generate intent map
631
632
  vibecheck scan ./my-project --emit-intent-map --out scan.json
633
+
634
+ # Disable AI-native developer enhancements
635
+ vibecheck scan ./my-project --no-enhance
636
+ ```
637
+
638
+ ## AI-Native Developer Enhancements
639
+
640
+ By default, VibeCheck enriches findings with AI-native developer features:
641
+
642
+ - **Plain English Explanations**: "What's wrong" and "Why it matters" in simple terms
643
+ - **Severity Context**: Human-readable urgency levels ("Fix immediately before deploying", etc.)
644
+ - **Fix Steps**: Step-by-step remediation guides with code examples
645
+ - **AI Prompts**: Ready-to-copy prompts for Claude, ChatGPT, or other AI assistants
646
+ - **Code Comparison**: Before/after code snippets showing the fix
647
+
648
+ These enhancements appear in the web viewer and are included in the artifact JSON under `finding.enhancements`.
649
+
650
+ To disable enhancements (for smaller artifacts or CI pipelines):
651
+
652
+ ```bash
653
+ vibecheck scan --no-enhance
632
654
  ```
633
655
 
634
656
  ## Architecture
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- declare const CLI_VERSION = "0.4.2";
2
+ declare const CLI_VERSION = "0.5.0";
3
3
 
4
4
  export { CLI_VERSION };
package/dist/index.js CHANGED
@@ -190,6 +190,75 @@ var RemediationSchema = z4.object({
190
190
  recommendedFix: z4.string(),
191
191
  patch: z4.string().optional()
192
192
  });
193
+ var UrgencySchema = z4.enum([
194
+ "Fix immediately before deploying",
195
+ "Fix before next release",
196
+ "Should fix soon",
197
+ "Good to fix eventually",
198
+ "Nice to have"
199
+ ]);
200
+ var PlainEnglishSchema = z4.object({
201
+ /** What's wrong in simple terms (e.g., "Anyone can delete data without logging in") */
202
+ problem: z4.string(),
203
+ /** Why it matters - real-world impact */
204
+ impact: z4.string(),
205
+ /** What could happen if exploited (optional worst case) */
206
+ worstCase: z4.string().optional()
207
+ });
208
+ var CodeChangeSchema = z4.object({
209
+ /** Line number in the "after" code */
210
+ line: z4.number(),
211
+ /** Explanation of what this change does */
212
+ explanation: z4.string()
213
+ });
214
+ var CodeComparisonSchema = z4.object({
215
+ /** Current vulnerable code */
216
+ before: z4.string(),
217
+ /** Secure version of the code */
218
+ after: z4.string(),
219
+ /** Language for syntax highlighting */
220
+ language: z4.string().default("typescript"),
221
+ /** Line-by-line explanation of changes */
222
+ changes: z4.array(CodeChangeSchema).optional()
223
+ });
224
+ var FixStepSchema = z4.object({
225
+ /** Step number */
226
+ step: z4.number(),
227
+ /** Title of this step */
228
+ title: z4.string(),
229
+ /** Detailed action to take */
230
+ action: z4.string(),
231
+ /** Optional code to copy */
232
+ code: z4.string().optional(),
233
+ /** Optional command to run */
234
+ command: z4.string().optional(),
235
+ /** How to verify this step worked */
236
+ verification: z4.string().optional()
237
+ });
238
+ var AIPromptSchema = z4.object({
239
+ /** Pre-formatted prompt for AI assistants */
240
+ template: z4.string(),
241
+ /** Suggested follow-up questions */
242
+ followUpQuestions: z4.array(z4.string()).optional()
243
+ });
244
+ var SeverityContextSchema = z4.object({
245
+ /** Plain English urgency */
246
+ urgency: UrgencySchema,
247
+ /** Why this severity level was assigned */
248
+ reasoning: z4.string()
249
+ });
250
+ var FindingEnhancementsSchema = z4.object({
251
+ /** Plain English explanation */
252
+ plainEnglish: PlainEnglishSchema.optional(),
253
+ /** Before/after code comparison */
254
+ codeComparison: CodeComparisonSchema.optional(),
255
+ /** Step-by-step fix instructions */
256
+ fixSteps: z4.array(FixStepSchema).optional(),
257
+ /** AI-friendly prompt for getting help */
258
+ aiPrompt: AIPromptSchema.optional(),
259
+ /** Contextual severity explanation */
260
+ severityContext: SeverityContextSchema.optional()
261
+ });
193
262
  var ReferenceLinksSchema = z4.object({
194
263
  owasp: z4.string().url().optional(),
195
264
  cwe: z4.string().url().optional()
@@ -215,7 +284,9 @@ var FindingSchema = z4.object({
215
284
  /** Optional correlation data for cross-pack findings (Phase 4) */
216
285
  correlationData: CorrelationDataSchema.optional(),
217
286
  /** References to related finding IDs/fingerprints (Phase 4) */
218
- relatedFindings: z4.array(z4.string()).optional()
287
+ relatedFindings: z4.array(z4.string()).optional(),
288
+ /** AI-native developer enhancements for clearer understanding */
289
+ enhancements: FindingEnhancementsSchema.optional()
219
290
  });
220
291
 
221
292
  // ../schema/dist/schemas/supply-chain.js
@@ -659,7 +730,7 @@ function validateArtifact(json) {
659
730
  }
660
731
 
661
732
  // src/constants.ts
662
- var CLI_VERSION = "0.4.2";
733
+ var CLI_VERSION = "0.5.0";
663
734
 
664
735
  // src/utils/file-utils.ts
665
736
  import fs from "fs";
@@ -983,6 +1054,562 @@ Found ${patchableFindings.length} finding(s) with patches.
983
1054
  };
984
1055
  }
985
1056
 
1057
+ // src/utils/finding-enhancer.ts
1058
+ var SEVERITY_TO_URGENCY = {
1059
+ critical: "Fix immediately before deploying",
1060
+ high: "Fix before next release",
1061
+ medium: "Should fix soon",
1062
+ low: "Good to fix eventually",
1063
+ info: "Nice to have"
1064
+ };
1065
+ var SEVERITY_REASONING = {
1066
+ critical: "This issue could allow attackers to take control of your application or access all user data. It needs immediate attention.",
1067
+ high: "This issue could lead to unauthorized access or data exposure. It should be fixed before any production release.",
1068
+ medium: "This issue represents a security weakness that could be exploited under certain conditions. Plan to fix it soon.",
1069
+ low: "This is a minor security concern that's good to address but unlikely to be exploited on its own.",
1070
+ info: "This is a best practice recommendation that improves your overall security posture."
1071
+ };
1072
+ var CATEGORY_IMPACTS = {
1073
+ auth: {
1074
+ impact: "Anyone on the internet could access or modify protected data without logging in.",
1075
+ worstCase: "Attackers could steal user accounts, delete data, or impersonate users."
1076
+ },
1077
+ validation: {
1078
+ impact: "Users could submit malicious or invalid data that breaks your app or exploits other systems.",
1079
+ worstCase: "Attackers could inject code, corrupt databases, or crash your servers."
1080
+ },
1081
+ middleware: {
1082
+ impact: "Security protections you think are in place might not be running on some pages.",
1083
+ worstCase: "Attackers could bypass your security by accessing unprotected routes directly."
1084
+ },
1085
+ secrets: {
1086
+ impact: "Sensitive credentials could be exposed to anyone who can access the code.",
1087
+ worstCase: "Attackers could use exposed API keys to access your cloud services and rack up charges."
1088
+ },
1089
+ injection: {
1090
+ impact: "User input could be executed as code or database commands.",
1091
+ worstCase: "Attackers could read your entire database, modify data, or execute system commands."
1092
+ },
1093
+ privacy: {
1094
+ impact: "Sensitive user information might be logged or exposed unintentionally.",
1095
+ worstCase: "User data could be leaked, leading to regulatory fines and loss of trust."
1096
+ },
1097
+ config: {
1098
+ impact: "Misconfigured settings could weaken your application's security.",
1099
+ worstCase: "Default credentials or insecure settings could give attackers easy access."
1100
+ },
1101
+ network: {
1102
+ impact: "Network requests might be misconfigured, allowing attackers to intercept or redirect them.",
1103
+ worstCase: "Attackers could steal data in transit, redirect users to malicious sites, or access internal systems."
1104
+ },
1105
+ crypto: {
1106
+ impact: "Cryptographic operations might use weak algorithms that are easily broken.",
1107
+ worstCase: "Attackers could crack passwords, forge tokens, or decrypt sensitive data."
1108
+ },
1109
+ uploads: {
1110
+ impact: "File uploads might not be properly validated, allowing dangerous files.",
1111
+ worstCase: "Attackers could upload malware, execute code on your server, or fill up storage."
1112
+ },
1113
+ hallucinations: {
1114
+ impact: "Security protections that appear in comments or code might not actually work.",
1115
+ worstCase: "You might think you're protected when you're actually vulnerable."
1116
+ },
1117
+ abuse: {
1118
+ impact: "Expensive operations could be triggered without proper limits.",
1119
+ worstCase: "Attackers could rack up huge API bills or crash your services with resource exhaustion."
1120
+ },
1121
+ correlation: {
1122
+ impact: "Multiple security issues combine to create a more serious vulnerability.",
1123
+ worstCase: "The combination of weaknesses could create an attack path that's worse than any single issue."
1124
+ },
1125
+ authorization: {
1126
+ impact: "Users might be able to access or modify resources they shouldn't have permission for.",
1127
+ worstCase: "Regular users could access admin features or other users' private data."
1128
+ },
1129
+ lifecycle: {
1130
+ impact: "Data validation rules aren't consistent across create, update, and delete operations.",
1131
+ worstCase: "Attackers could bypass validation by using a different operation than expected."
1132
+ },
1133
+ "supply-chain": {
1134
+ impact: "Third-party dependencies might introduce vulnerabilities or malicious code.",
1135
+ worstCase: "A compromised package could steal credentials or inject malware into your app."
1136
+ },
1137
+ other: {
1138
+ impact: "This is a security concern that doesn't fit into standard categories.",
1139
+ worstCase: "The specific risk depends on the context of this finding."
1140
+ }
1141
+ };
1142
+ var RULE_PROBLEM_DESCRIPTIONS = {
1143
+ "VC-AUTH-001": "This API endpoint can be accessed by anyone without logging in",
1144
+ "VC-AUTH-002": "A security middleware that should protect routes isn't being applied",
1145
+ "VC-VAL-001": "User input is being trusted without checking if it's valid",
1146
+ "VC-VAL-002": "Validation code exists but its errors are being ignored",
1147
+ "VC-NET-001": "Cross-origin requests are allowed from any website",
1148
+ "VC-NET-002": "Network requests can hang forever without timing out",
1149
+ "VC-NET-003": "Users can be redirected to any external URL",
1150
+ "VC-NET-004": "User input is used in URL requests without validation",
1151
+ "VC-CRY-001": "Passwords or sensitive data use outdated hashing algorithms",
1152
+ "VC-CRY-002": "JWT tokens are decoded without verifying the signature",
1153
+ "VC-CRY-003": "Random tokens are generated using predictable Math.random()",
1154
+ "VC-PRV-001": "Debug mode is enabled, exposing sensitive information",
1155
+ "VC-PRV-002": "API responses include more data than users should see",
1156
+ "VC-PRV-003": "Sensitive data like passwords or tokens are being logged",
1157
+ "VC-UPL-001": "File uploads don't have size or type restrictions",
1158
+ "VC-UPL-002": "Uploaded files are stored in publicly accessible locations",
1159
+ "VC-MID-001": "API routes are missing rate limiting",
1160
+ "VC-HAL-001": "Security imports are declared but never used",
1161
+ "VC-ABS-001": "Expensive AI/compute operations lack rate limiting or auth",
1162
+ "VC-CFG-001": "Configuration uses insecure default values",
1163
+ "VC-CFG-002": "Environment variables are used but not documented",
1164
+ "VC-AZN-001": "Admin routes don't check if the user has admin privileges",
1165
+ "VC-AZN-002": "Role requirements are declared but not enforced",
1166
+ "VC-AZN-003": "Resources can be accessed without ownership verification",
1167
+ "VC-AZN-004": "Client-provided IDs are trusted without verification",
1168
+ "VC-LCY-001": "Create and update operations have different validation rules",
1169
+ "VC-LCY-002": "Delete operations are missing rate limits",
1170
+ "VC-LCY-003": "Validation schemas don't match between similar operations",
1171
+ "VC-SUP-001": "Using deprecated packages with known vulnerabilities",
1172
+ "VC-SUP-002": "Multiple authentication systems may conflict",
1173
+ "VC-SUP-003": "Package.json uses overly permissive version ranges",
1174
+ "VC-SUP-004": "Suspicious postinstall scripts in dependencies",
1175
+ "VC-SUP-005": "Security-critical packages have outdated versions"
1176
+ };
1177
+ function generatePlainEnglish(finding) {
1178
+ const problem = RULE_PROBLEM_DESCRIPTIONS[finding.ruleId] || `${finding.title.replace(/VC-[A-Z]+-\d{3}:?\s*/g, "")}`;
1179
+ const categoryInfo = CATEGORY_IMPACTS[finding.category] || CATEGORY_IMPACTS.other;
1180
+ return {
1181
+ problem,
1182
+ impact: categoryInfo.impact,
1183
+ worstCase: categoryInfo.worstCase
1184
+ };
1185
+ }
1186
+ function generateSeverityContext(finding) {
1187
+ return {
1188
+ urgency: SEVERITY_TO_URGENCY[finding.severity],
1189
+ reasoning: SEVERITY_REASONING[finding.severity]
1190
+ };
1191
+ }
1192
+ function generateCodeComparison(finding) {
1193
+ const primaryEvidence = finding.evidence[0];
1194
+ if (!primaryEvidence?.snippet) {
1195
+ return void 0;
1196
+ }
1197
+ if (finding.remediation?.patch) {
1198
+ const patch = finding.remediation.patch;
1199
+ const lines = patch.split("\n");
1200
+ const addedLines = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).map((line) => line.slice(1));
1201
+ if (addedLines.length > 0) {
1202
+ const before = primaryEvidence.snippet.replace(/\.{3}$/, "").trim();
1203
+ const after = addedLines.join("\n") + "\n" + before;
1204
+ const file = primaryEvidence.file;
1205
+ let language = "typescript";
1206
+ if (file.endsWith(".js") || file.endsWith(".jsx")) {
1207
+ language = "javascript";
1208
+ } else if (file.endsWith(".py")) {
1209
+ language = "python";
1210
+ } else if (file.endsWith(".go")) {
1211
+ language = "go";
1212
+ }
1213
+ return {
1214
+ before,
1215
+ after,
1216
+ language,
1217
+ changes: addedLines.map((line, i) => ({
1218
+ line: i + 1,
1219
+ explanation: `Added: ${line.trim().slice(0, 50)}${line.length > 50 ? "..." : ""}`
1220
+ }))
1221
+ };
1222
+ }
1223
+ }
1224
+ return void 0;
1225
+ }
1226
+ var CATEGORY_FIX_STEPS = {
1227
+ auth: [
1228
+ {
1229
+ step: 1,
1230
+ title: "Add authentication check",
1231
+ action: "Import your auth library and add a session check at the start of the function.",
1232
+ code: `const session = await getServerSession(authOptions);
1233
+ if (!session) {
1234
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
1235
+ status: 401
1236
+ });
1237
+ }`,
1238
+ verification: "Try accessing the endpoint without being logged in - you should get a 401 error."
1239
+ },
1240
+ {
1241
+ step: 2,
1242
+ title: "Test the protection",
1243
+ action: "Verify the endpoint now requires authentication.",
1244
+ command: "curl -X POST http://localhost:3000/api/your-endpoint",
1245
+ verification: "You should receive a 401 Unauthorized response."
1246
+ }
1247
+ ],
1248
+ validation: [
1249
+ {
1250
+ step: 1,
1251
+ title: "Define a validation schema",
1252
+ action: "Create a Zod schema that describes valid input.",
1253
+ code: `import { z } from "zod";
1254
+
1255
+ const inputSchema = z.object({
1256
+ name: z.string().min(1).max(100),
1257
+ email: z.string().email(),
1258
+ });`
1259
+ },
1260
+ {
1261
+ step: 2,
1262
+ title: "Validate incoming data",
1263
+ action: "Parse the input through your schema before using it.",
1264
+ code: `const result = inputSchema.safeParse(body);
1265
+ if (!result.success) {
1266
+ return new Response(JSON.stringify({ error: result.error.issues }), {
1267
+ status: 400
1268
+ });
1269
+ }
1270
+ const { name, email } = result.data;`,
1271
+ verification: "Send malformed data and verify you get a 400 error with validation details."
1272
+ }
1273
+ ],
1274
+ middleware: [
1275
+ {
1276
+ step: 1,
1277
+ title: "Check middleware configuration",
1278
+ action: "Open your middleware file and verify the matcher patterns.",
1279
+ verification: "Ensure the route you want to protect is included in the matcher array."
1280
+ },
1281
+ {
1282
+ step: 2,
1283
+ title: "Add the route to protected paths",
1284
+ action: "Update the middleware matcher to include this route.",
1285
+ code: `export const config = {
1286
+ matcher: [
1287
+ '/api/:path*', // Protect all API routes
1288
+ '/dashboard/:path*', // Protect dashboard
1289
+ ],
1290
+ };`
1291
+ }
1292
+ ],
1293
+ secrets: [
1294
+ {
1295
+ step: 1,
1296
+ title: "Move secret to environment variable",
1297
+ action: "Remove the hardcoded secret and use an environment variable instead.",
1298
+ code: `// Before: const apiKey = "sk-abc123...";
1299
+ // After:
1300
+ const apiKey = process.env.API_KEY;
1301
+ if (!apiKey) {
1302
+ throw new Error("API_KEY environment variable is required");
1303
+ }`
1304
+ },
1305
+ {
1306
+ step: 2,
1307
+ title: "Update your .env file",
1308
+ action: "Add the secret to your .env file (never commit this to git).",
1309
+ command: "echo 'API_KEY=your-secret-key' >> .env"
1310
+ },
1311
+ {
1312
+ step: 3,
1313
+ title: "Add .env to .gitignore",
1314
+ action: "Make sure your secrets file is not tracked by git.",
1315
+ command: "echo '.env' >> .gitignore",
1316
+ verification: "Run 'git status' and verify .env is not listed as a tracked file."
1317
+ }
1318
+ ],
1319
+ injection: [
1320
+ {
1321
+ step: 1,
1322
+ title: "Use parameterized queries",
1323
+ action: "Never concatenate user input directly into queries.",
1324
+ code: `// Before: db.query(\`SELECT * FROM users WHERE id = \${userId}\`);
1325
+ // After:
1326
+ db.query("SELECT * FROM users WHERE id = ?", [userId]);`
1327
+ },
1328
+ {
1329
+ step: 2,
1330
+ title: "Validate and sanitize input",
1331
+ action: "Always validate input types and sanitize special characters.",
1332
+ verification: `Try entering SQL injection payloads like "' OR '1'='1" and verify they're handled safely.`
1333
+ }
1334
+ ],
1335
+ privacy: [
1336
+ {
1337
+ step: 1,
1338
+ title: "Remove sensitive data from logs",
1339
+ action: "Never log passwords, tokens, or personal information.",
1340
+ code: `// Before: console.log("User login:", { email, password });
1341
+ // After:
1342
+ console.log("User login:", { email, password: "[REDACTED]" });`
1343
+ },
1344
+ {
1345
+ step: 2,
1346
+ title: "Filter response data",
1347
+ action: "Only return the fields that the client actually needs.",
1348
+ code: `// Before: return user;
1349
+ // After:
1350
+ const { password, ssn, ...safeUser } = user;
1351
+ return safeUser;`
1352
+ }
1353
+ ],
1354
+ config: [
1355
+ {
1356
+ step: 1,
1357
+ title: "Review configuration defaults",
1358
+ action: "Check all config values and replace insecure defaults.",
1359
+ verification: "Look for 'password', 'secret', 'admin', or 'test' in your config files."
1360
+ },
1361
+ {
1362
+ step: 2,
1363
+ title: "Use environment variables",
1364
+ action: "Move all sensitive configuration to environment variables."
1365
+ }
1366
+ ],
1367
+ network: [
1368
+ {
1369
+ step: 1,
1370
+ title: "Configure CORS properly",
1371
+ action: "Restrict allowed origins to only the domains you trust.",
1372
+ code: `const corsOptions = {
1373
+ origin: ['https://yourdomain.com'],
1374
+ methods: ['GET', 'POST'],
1375
+ credentials: true,
1376
+ };`
1377
+ },
1378
+ {
1379
+ step: 2,
1380
+ title: "Add request timeouts",
1381
+ action: "Set timeouts on all network requests to prevent hanging.",
1382
+ code: `const response = await fetch(url, {
1383
+ signal: AbortSignal.timeout(10000) // 10 second timeout
1384
+ });`
1385
+ }
1386
+ ],
1387
+ crypto: [
1388
+ {
1389
+ step: 1,
1390
+ title: "Use modern algorithms",
1391
+ action: "Replace MD5/SHA1 with bcrypt for passwords or SHA-256 for hashing.",
1392
+ code: `// For passwords, use bcrypt:
1393
+ import bcrypt from 'bcrypt';
1394
+ const hash = await bcrypt.hash(password, 12);`
1395
+ },
1396
+ {
1397
+ step: 2,
1398
+ title: "Use crypto.randomUUID for tokens",
1399
+ action: "Never use Math.random() for security-sensitive values.",
1400
+ code: `// Before: const token = Math.random().toString(36);
1401
+ // After:
1402
+ const token = crypto.randomUUID();`
1403
+ }
1404
+ ],
1405
+ uploads: [
1406
+ {
1407
+ step: 1,
1408
+ title: "Validate file types",
1409
+ action: "Only allow specific file types that you expect.",
1410
+ code: `const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf'];
1411
+ if (!ALLOWED_TYPES.includes(file.type)) {
1412
+ throw new Error('File type not allowed');
1413
+ }`
1414
+ },
1415
+ {
1416
+ step: 2,
1417
+ title: "Limit file size",
1418
+ action: "Set a maximum file size to prevent storage abuse.",
1419
+ code: `const MAX_SIZE = 5 * 1024 * 1024; // 5MB
1420
+ if (file.size > MAX_SIZE) {
1421
+ throw new Error('File too large');
1422
+ }`
1423
+ },
1424
+ {
1425
+ step: 3,
1426
+ title: "Store in secure location",
1427
+ action: "Store uploads outside the public directory and serve through an API.",
1428
+ verification: "Verify uploaded files cannot be accessed directly via URL."
1429
+ }
1430
+ ],
1431
+ hallucinations: [
1432
+ {
1433
+ step: 1,
1434
+ title: "Verify security code is working",
1435
+ action: "Check that imported security functions are actually being called.",
1436
+ verification: "Add logging or step through with a debugger to confirm the code executes."
1437
+ },
1438
+ {
1439
+ step: 2,
1440
+ title: "Remove unused imports",
1441
+ action: "If the security code isn't needed, remove the import to avoid confusion."
1442
+ }
1443
+ ],
1444
+ abuse: [
1445
+ {
1446
+ step: 1,
1447
+ title: "Add rate limiting",
1448
+ action: "Limit how often users can call expensive endpoints.",
1449
+ code: `import rateLimit from 'express-rate-limit';
1450
+
1451
+ const limiter = rateLimit({
1452
+ windowMs: 60 * 1000, // 1 minute
1453
+ max: 10, // 10 requests per minute
1454
+ });`
1455
+ },
1456
+ {
1457
+ step: 2,
1458
+ title: "Require authentication",
1459
+ action: "Make sure only logged-in users can access expensive operations."
1460
+ },
1461
+ {
1462
+ step: 3,
1463
+ title: "Add request size limits",
1464
+ action: "Limit input size to prevent resource exhaustion.",
1465
+ code: `app.use(express.json({ limit: '100kb' }));`
1466
+ }
1467
+ ],
1468
+ correlation: [
1469
+ {
1470
+ step: 1,
1471
+ title: "Review related findings",
1472
+ action: "This finding is connected to other security issues. Review them together."
1473
+ },
1474
+ {
1475
+ step: 2,
1476
+ title: "Fix the root cause",
1477
+ action: "Address the underlying issue that enables this combination of vulnerabilities."
1478
+ }
1479
+ ],
1480
+ authorization: [
1481
+ {
1482
+ step: 1,
1483
+ title: "Add role/permission check",
1484
+ action: "Verify the user has permission before performing the action.",
1485
+ code: `if (!user.roles.includes('admin')) {
1486
+ return new Response('Forbidden', { status: 403 });
1487
+ }`
1488
+ },
1489
+ {
1490
+ step: 2,
1491
+ title: "Add ownership verification",
1492
+ action: "Verify the user owns the resource they're trying to access.",
1493
+ code: `const resource = await db.findById(resourceId);
1494
+ if (resource.ownerId !== session.user.id) {
1495
+ return new Response('Forbidden', { status: 403 });
1496
+ }`
1497
+ }
1498
+ ],
1499
+ lifecycle: [
1500
+ {
1501
+ step: 1,
1502
+ title: "Align validation rules",
1503
+ action: "Use the same validation schema for create and update operations.",
1504
+ code: `// Define once, use everywhere:
1505
+ const userSchema = z.object({
1506
+ name: z.string().min(1),
1507
+ email: z.string().email(),
1508
+ });`
1509
+ },
1510
+ {
1511
+ step: 2,
1512
+ title: "Test all operations",
1513
+ action: "Verify that validation works the same for create, update, and delete.",
1514
+ verification: "Try submitting invalid data through each operation type."
1515
+ }
1516
+ ],
1517
+ "supply-chain": [
1518
+ {
1519
+ step: 1,
1520
+ title: "Update dependencies",
1521
+ action: "Update vulnerable packages to their latest secure versions.",
1522
+ command: "pnpm update <package-name>"
1523
+ },
1524
+ {
1525
+ step: 2,
1526
+ title: "Review package security",
1527
+ action: "Check the package on npm for security advisories.",
1528
+ verification: "Visit the package page on npmjs.com and check for deprecation warnings."
1529
+ }
1530
+ ],
1531
+ other: [
1532
+ {
1533
+ step: 1,
1534
+ title: "Review the finding",
1535
+ action: "Carefully read the description and evidence to understand the issue."
1536
+ },
1537
+ {
1538
+ step: 2,
1539
+ title: "Apply the recommended fix",
1540
+ action: "Follow the remediation guidance provided in the finding details."
1541
+ }
1542
+ ]
1543
+ };
1544
+ function generateFixSteps(finding) {
1545
+ const categorySteps = CATEGORY_FIX_STEPS[finding.category] || CATEGORY_FIX_STEPS.other;
1546
+ if (finding.remediation?.recommendedFix) {
1547
+ return [
1548
+ {
1549
+ step: 1,
1550
+ title: "Apply the recommended fix",
1551
+ action: finding.remediation.recommendedFix,
1552
+ code: finding.remediation.patch || void 0
1553
+ },
1554
+ ...categorySteps.map((step) => ({
1555
+ ...step,
1556
+ step: step.step + 1
1557
+ }))
1558
+ ];
1559
+ }
1560
+ return categorySteps;
1561
+ }
1562
+ function generateAIPrompt(finding) {
1563
+ const template = `I have a security finding in my codebase that I need help fixing.
1564
+
1565
+ **Issue:** ${finding.title}
1566
+ **File:** ${finding.evidence[0]?.file || "Unknown"}
1567
+ **Severity:** ${finding.severity.toUpperCase()}
1568
+ **Category:** ${finding.category}
1569
+
1570
+ **Description:**
1571
+ ${finding.description}
1572
+
1573
+ **Problematic Code:**
1574
+ \`\`\`
1575
+ ${finding.evidence[0]?.snippet || "See file above"}
1576
+ \`\`\`
1577
+
1578
+ **Recommended Fix:**
1579
+ ${finding.remediation?.recommendedFix || "See description above"}
1580
+
1581
+ Can you help me fix this security issue? Please:
1582
+ 1. Explain why this is a security risk
1583
+ 2. Show me the corrected code
1584
+ 3. Explain what the fix does`;
1585
+ const followUpQuestions = [
1586
+ `What other files might have similar ${finding.category} issues?`,
1587
+ "Are there any edge cases I should consider in this fix?",
1588
+ "How can I write a test to verify this is fixed?",
1589
+ "What would happen if an attacker tried to exploit this?"
1590
+ ];
1591
+ return {
1592
+ template,
1593
+ followUpQuestions
1594
+ };
1595
+ }
1596
+ function enhanceFinding(finding) {
1597
+ const enhancements = {
1598
+ plainEnglish: generatePlainEnglish(finding),
1599
+ severityContext: generateSeverityContext(finding),
1600
+ codeComparison: generateCodeComparison(finding),
1601
+ fixSteps: generateFixSteps(finding),
1602
+ aiPrompt: generateAIPrompt(finding)
1603
+ };
1604
+ return {
1605
+ ...finding,
1606
+ enhancements
1607
+ };
1608
+ }
1609
+ function enhanceFindings(findings) {
1610
+ return findings.map(enhanceFinding);
1611
+ }
1612
+
986
1613
  // src/utils/custom-rules-loader.ts
987
1614
  import { readFileSync as readFileSync2, statSync, readdirSync } from "fs";
988
1615
  import { join, extname } from "path";
@@ -9195,8 +9822,15 @@ async function executeScan(targetDir, options) {
9195
9822
  correlationSpinner.succeed("Phase 4 correlation: no patterns detected");
9196
9823
  }
9197
9824
  }
9825
+ let enhancedFindings = findings;
9826
+ if (options.enhance !== false) {
9827
+ const enhanceSpinner = new Spinner("Adding AI-native developer enhancements");
9828
+ enhanceSpinner.start();
9829
+ enhancedFindings = enhanceFindings(findings);
9830
+ enhanceSpinner.succeed(`Enhanced ${enhancedFindings.length} finding(s) with plain English explanations`);
9831
+ }
9198
9832
  const artifact = createArtifact(
9199
- findings,
9833
+ enhancedFindings,
9200
9834
  absoluteTarget,
9201
9835
  initialContext.fileIndex.allSourceFiles.length,
9202
9836
  options.repoName,
@@ -9361,6 +9995,9 @@ function registerScanCommand(program2) {
9361
9995
  ).option(
9362
9996
  "-r, --rules <path>",
9363
9997
  "Path to directory containing custom YAML rules or a single YAML rule file"
9998
+ ).option(
9999
+ "--no-enhance",
10000
+ "Disable AI-native developer enhancements (plain English, fix steps, AI prompts)"
9364
10001
  ).addHelpText(
9365
10002
  "after",
9366
10003
  `
@@ -9415,7 +10052,9 @@ Default excludes:
9415
10052
  includeTests: Boolean(cmdOptions.includeTests),
9416
10053
  applyFixes: Boolean(cmdOptions.applyFixes),
9417
10054
  force: Boolean(cmdOptions.force),
9418
- rules: cmdOptions.rules
10055
+ rules: cmdOptions.rules,
10056
+ enhance: cmdOptions.enhance !== false
10057
+ // default true, --no-enhance sets to false
9419
10058
  };
9420
10059
  const exitCode = await executeScan(targetDir, options);
9421
10060
  process.exit(exitCode);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantracode/vibecheck",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Security scanner for modern web applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",