@vibecheckai/cli 3.2.2 → 3.2.4
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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1182,6 +1182,7 @@ function findOwnerModeBypass(repoRoot) {
|
|
|
1182
1182
|
* ========================================================================== */
|
|
1183
1183
|
|
|
1184
1184
|
function findMockData(repoRoot) {
|
|
1185
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1185
1186
|
const findings = [];
|
|
1186
1187
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1187
1188
|
cwd: repoRoot,
|
|
@@ -1196,64 +1197,39 @@ function findMockData(repoRoot) {
|
|
|
1196
1197
|
"**/tests/**",
|
|
1197
1198
|
"**/test/**",
|
|
1198
1199
|
"**/__tests__/**",
|
|
1199
|
-
"**/mocks/**",
|
|
1200
|
-
"**/__mocks__/**",
|
|
1201
1200
|
],
|
|
1202
1201
|
});
|
|
1203
1202
|
|
|
1204
|
-
const mockPatterns = [
|
|
1205
|
-
{ rx: /\bmockData\b/i, label: "mockData variable" },
|
|
1206
|
-
{ rx: /\bfakeData\b/i, label: "fakeData variable" },
|
|
1207
|
-
{ rx: /\bdummyData\b/i, label: "dummyData variable" },
|
|
1208
|
-
{ rx: /\btestData\b/i, label: "testData variable (in production code)" },
|
|
1209
|
-
{ rx: /\bsampleData\b/i, label: "sampleData variable" },
|
|
1210
|
-
{ rx: /['"]fake[_-]?user['"]|['"]test[_-]?user['"]|['"]demo[_-]?user['"]/i, label: "Hardcoded test user" },
|
|
1211
|
-
{ rx: /['"]password123['"]|['"]test123['"]|['"]admin123['"]|['"]secret123['"]/i, label: "Hardcoded test password" },
|
|
1212
|
-
{ rx: /['"]test@(test|example|fake)\.com['"]/i, label: "Hardcoded test email" },
|
|
1213
|
-
{ rx: /\b(MOCK_API|FAKE_API|DUMMY_API)\b/i, label: "Mock API reference" },
|
|
1214
|
-
{ rx: /setTimeout\([^)]*[5-9]\d{3,}|setTimeout\([^)]*\d{5,}/i, label: "Long setTimeout (simulated delay?)" },
|
|
1215
|
-
{ rx: /Math\.random\(\)\s*[*<>]\s*\d+/i, label: "Random data generation" },
|
|
1216
|
-
{ rx: /\bplaceholder\b.*\bdata\b|\bdata\b.*\bplaceholder\b/i, label: "Placeholder data" },
|
|
1217
|
-
];
|
|
1218
|
-
|
|
1219
1203
|
for (const fileAbs of files) {
|
|
1220
1204
|
try {
|
|
1221
1205
|
const code = readFileCached(fileAbs);
|
|
1222
1206
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1223
1207
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
for (const { rx, label } of mockPatterns) {
|
|
1230
|
-
if (!rxTest(rx, code)) continue;
|
|
1231
|
-
|
|
1232
|
-
let lineNum = 1;
|
|
1233
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1234
|
-
if (rxTest(rx, lines[i])) {
|
|
1235
|
-
lineNum = i + 1;
|
|
1236
|
-
break;
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1208
|
+
// Use unified engines
|
|
1209
|
+
const engineFindings = engines.analyzeMockData(code, fileRel);
|
|
1210
|
+
|
|
1211
|
+
// Convert engine findings to analyzer format
|
|
1212
|
+
for (const finding of engineFindings) {
|
|
1240
1213
|
findings.push({
|
|
1241
|
-
id: stableId("F_MOCK_DATA", `${fileRel}:${
|
|
1242
|
-
severity:
|
|
1243
|
-
category:
|
|
1244
|
-
title:
|
|
1214
|
+
id: stableId("F_MOCK_DATA", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1215
|
+
severity: finding.severity,
|
|
1216
|
+
category: finding.category,
|
|
1217
|
+
title: finding.title,
|
|
1218
|
+
message: finding.message,
|
|
1219
|
+
file: finding.file,
|
|
1220
|
+
line: finding.line,
|
|
1245
1221
|
why: "Mock/fake data in production causes embarrassing bugs and makes your app look unfinished.",
|
|
1246
|
-
confidence:
|
|
1247
|
-
evidence: [{ file: fileRel,
|
|
1222
|
+
confidence: finding.confidence,
|
|
1223
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1248
1224
|
fixHints: [
|
|
1249
1225
|
"Replace mock data with real API calls or database queries.",
|
|
1250
1226
|
"If intentional sample data, move to a clearly marked demo mode.",
|
|
1251
1227
|
],
|
|
1252
1228
|
});
|
|
1253
|
-
break;
|
|
1254
1229
|
}
|
|
1255
|
-
} catch {
|
|
1256
|
-
//
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
// Skip files that can't be analyzed
|
|
1232
|
+
continue;
|
|
1257
1233
|
}
|
|
1258
1234
|
}
|
|
1259
1235
|
|
|
@@ -1265,89 +1241,73 @@ function findMockData(repoRoot) {
|
|
|
1265
1241
|
* ========================================================================== */
|
|
1266
1242
|
|
|
1267
1243
|
function findTodoFixme(repoRoot) {
|
|
1244
|
+
// TODO/FIXME engine not in unified engines yet - keep using existing
|
|
1245
|
+
const { analyzeTodoFixme } = require("./engines/todo-fixme-engine");
|
|
1246
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1268
1247
|
const findings = [];
|
|
1269
1248
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1270
1249
|
cwd: repoRoot,
|
|
1271
1250
|
absolute: true,
|
|
1272
|
-
ignore: [
|
|
1251
|
+
ignore: [
|
|
1252
|
+
"**/node_modules/**",
|
|
1253
|
+
"**/.next/**",
|
|
1254
|
+
"**/dist/**",
|
|
1255
|
+
"**/build/**",
|
|
1256
|
+
"**/*.d.ts",
|
|
1257
|
+
],
|
|
1273
1258
|
});
|
|
1274
1259
|
|
|
1275
|
-
const todoPatterns = [
|
|
1276
|
-
{ rx: /\/\/\s*TODO[\s:]/i, label: "TODO comment", severity: "WARN" },
|
|
1277
|
-
{ rx: /\/\/\s*FIXME[\s:]/i, label: "FIXME comment", severity: "WARN" },
|
|
1278
|
-
{ rx: /\/\/\s*HACK[\s:]/i, label: "HACK comment", severity: "WARN" },
|
|
1279
|
-
{ rx: /\/\/\s*XXX[\s:]/i, label: "XXX comment", severity: "WARN" },
|
|
1280
|
-
{ rx: /\/\/\s*BUG[\s:]/i, label: "BUG comment", severity: "BLOCK" },
|
|
1281
|
-
{ rx: /\/\/\s*BROKEN[\s:]/i, label: "BROKEN comment", severity: "BLOCK" },
|
|
1282
|
-
{ rx: /\/\/\s*URGENT[\s:]/i, label: "URGENT comment", severity: "BLOCK" },
|
|
1283
|
-
{ rx: /\/\/\s*SECURITY[\s:]/i, label: "SECURITY comment", severity: "BLOCK" },
|
|
1284
|
-
{ rx: /\/\/\s*DANGER[\s:]/i, label: "DANGER comment", severity: "BLOCK" },
|
|
1285
|
-
{ rx: /\/\*\s*TODO[\s:]/i, label: "TODO block comment", severity: "WARN" },
|
|
1286
|
-
{ rx: /\/\*\s*FIXME[\s:]/i, label: "FIXME block comment", severity: "WARN" },
|
|
1287
|
-
];
|
|
1288
|
-
|
|
1289
|
-
let todoCount = 0;
|
|
1290
|
-
let fixmeCount = 0;
|
|
1291
|
-
const MAX_INDIVIDUAL_FINDINGS = 20;
|
|
1292
|
-
|
|
1293
1260
|
for (const fileAbs of files) {
|
|
1294
1261
|
try {
|
|
1295
1262
|
const code = readFileCached(fileAbs);
|
|
1296
1263
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1264
|
+
|
|
1265
|
+
// Use AST-based engine (not in unified engines yet)
|
|
1266
|
+
const engineFindings = analyzeTodoFixme(code, fileRel);
|
|
1267
|
+
|
|
1268
|
+
// Convert engine findings to analyzer format
|
|
1269
|
+
for (const finding of engineFindings) {
|
|
1270
|
+
if (finding.type === "summary") {
|
|
1271
|
+
findings.push({
|
|
1272
|
+
id: "F_TODO_SUMMARY",
|
|
1273
|
+
severity: finding.severity,
|
|
1274
|
+
category: finding.category,
|
|
1275
|
+
title: finding.title,
|
|
1276
|
+
why: "Large numbers of TODO comments indicate significant unfinished work.",
|
|
1277
|
+
confidence: finding.confidence,
|
|
1278
|
+
evidence: [],
|
|
1279
|
+
fixHints: [
|
|
1280
|
+
"Review and address high-priority TODOs before shipping.",
|
|
1281
|
+
`Run: grep -rn "TODO\\|FIXME" --include="*.ts" --include="*.js" .`,
|
|
1282
|
+
],
|
|
1283
|
+
});
|
|
1284
|
+
} else {
|
|
1285
|
+
findings.push({
|
|
1286
|
+
id: stableId("F_TODO", `${fileRel}:${finding.line}:${finding.type}`),
|
|
1287
|
+
severity: finding.severity,
|
|
1288
|
+
category: finding.category,
|
|
1289
|
+
title: finding.title,
|
|
1290
|
+
message: finding.message,
|
|
1291
|
+
file: finding.file,
|
|
1292
|
+
line: finding.line,
|
|
1293
|
+
why: finding.severity === "BLOCK"
|
|
1294
|
+
? "This comment indicates a known critical issue that must be addressed before shipping."
|
|
1295
|
+
: "Unfinished work markers suggest the code isn't production-ready.",
|
|
1296
|
+
confidence: finding.confidence,
|
|
1297
|
+
evidence: [{ file: fileRel, lines: `${finding.line}`, reason: finding.title }],
|
|
1298
|
+
fixHints: [
|
|
1299
|
+
"Complete the TODO or remove it if already done.",
|
|
1300
|
+
"If deferring, create a tracked issue and reference it in the comment.",
|
|
1301
|
+
],
|
|
1302
|
+
});
|
|
1327
1303
|
}
|
|
1328
1304
|
}
|
|
1329
|
-
} catch {
|
|
1330
|
-
//
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
// Skip files that can't be analyzed
|
|
1307
|
+
continue;
|
|
1331
1308
|
}
|
|
1332
1309
|
}
|
|
1333
1310
|
|
|
1334
|
-
const totalTodos = todoCount + fixmeCount;
|
|
1335
|
-
if (totalTodos > MAX_INDIVIDUAL_FINDINGS) {
|
|
1336
|
-
findings.push({
|
|
1337
|
-
id: "F_TODO_SUMMARY",
|
|
1338
|
-
severity: "WARN",
|
|
1339
|
-
category: "TodoFixme",
|
|
1340
|
-
title: `${totalTodos} TODO/FIXME comments found (${totalTodos - MAX_INDIVIDUAL_FINDINGS} more not shown)`,
|
|
1341
|
-
why: "Large numbers of TODO comments indicate significant unfinished work.",
|
|
1342
|
-
confidence: "high",
|
|
1343
|
-
evidence: [],
|
|
1344
|
-
fixHints: [
|
|
1345
|
-
"Review and address high-priority TODOs before shipping.",
|
|
1346
|
-
`Run: grep -rn "TODO\\|FIXME" --include="*.ts" --include="*.js" .`,
|
|
1347
|
-
],
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
1311
|
return findings;
|
|
1352
1312
|
}
|
|
1353
1313
|
|
|
@@ -1356,6 +1316,7 @@ function findTodoFixme(repoRoot) {
|
|
|
1356
1316
|
* ========================================================================== */
|
|
1357
1317
|
|
|
1358
1318
|
function findConsoleLogs(repoRoot) {
|
|
1319
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1359
1320
|
const findings = [];
|
|
1360
1321
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1361
1322
|
cwd: repoRoot,
|
|
@@ -1365,67 +1326,40 @@ function findConsoleLogs(repoRoot) {
|
|
|
1365
1326
|
"**/.next/**",
|
|
1366
1327
|
"**/dist/**",
|
|
1367
1328
|
"**/build/**",
|
|
1368
|
-
"**/*.
|
|
1369
|
-
"**/*.spec.*",
|
|
1370
|
-
"**/tests/**",
|
|
1371
|
-
"**/__tests__/**",
|
|
1372
|
-
"**/scripts/**",
|
|
1373
|
-
"**/bin/**",
|
|
1329
|
+
"**/*.d.ts",
|
|
1374
1330
|
],
|
|
1375
1331
|
});
|
|
1376
1332
|
|
|
1377
|
-
let consoleCount = 0;
|
|
1378
|
-
const MAX_INDIVIDUAL_FINDINGS = 15;
|
|
1379
|
-
|
|
1380
1333
|
for (const fileAbs of files) {
|
|
1381
1334
|
try {
|
|
1382
1335
|
const code = readFileCached(fileAbs);
|
|
1383
1336
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1384
1337
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
for (
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
why: "Console statements leak debugging info and clutter logs/console.",
|
|
1404
|
-
confidence: "high",
|
|
1405
|
-
evidence: [{ file: fileRel, lines: `${i + 1}`, reason: snippet }],
|
|
1406
|
-
fixHints: ["Remove console.log or replace with a proper logger.", "Use a logger that can be silenced in production."],
|
|
1407
|
-
});
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1338
|
+
// Use unified engines
|
|
1339
|
+
const engineFindings = engines.analyzeConsoleLogs(code, fileRel);
|
|
1340
|
+
|
|
1341
|
+
// Convert engine findings to analyzer format
|
|
1342
|
+
for (const finding of engineFindings) {
|
|
1343
|
+
findings.push({
|
|
1344
|
+
id: stableId("F_CONSOLE_LOG", `${fileRel}:${finding.line}:${finding.type}`),
|
|
1345
|
+
severity: finding.severity,
|
|
1346
|
+
category: finding.category,
|
|
1347
|
+
title: finding.title,
|
|
1348
|
+
message: finding.message,
|
|
1349
|
+
file: finding.file,
|
|
1350
|
+
line: finding.line,
|
|
1351
|
+
why: "Console statements leak debugging info and clutter logs/console.",
|
|
1352
|
+
confidence: finding.confidence,
|
|
1353
|
+
evidence: [{ file: fileRel, lines: `${finding.line}`, reason: finding.codeSnippet || finding.title }],
|
|
1354
|
+
fixHints: ["Remove console.log or replace with a proper logger.", "Use a logger that can be silenced in production."],
|
|
1355
|
+
});
|
|
1410
1356
|
}
|
|
1411
|
-
} catch {
|
|
1412
|
-
//
|
|
1357
|
+
} catch (err) {
|
|
1358
|
+
// Skip files that can't be analyzed
|
|
1359
|
+
continue;
|
|
1413
1360
|
}
|
|
1414
1361
|
}
|
|
1415
1362
|
|
|
1416
|
-
if (consoleCount > MAX_INDIVIDUAL_FINDINGS) {
|
|
1417
|
-
findings.push({
|
|
1418
|
-
id: "F_CONSOLE_LOG_SUMMARY",
|
|
1419
|
-
severity: "WARN",
|
|
1420
|
-
category: "ConsoleLog",
|
|
1421
|
-
title: `${consoleCount} console.log statements found (${consoleCount - MAX_INDIVIDUAL_FINDINGS} more not shown)`,
|
|
1422
|
-
why: "Large numbers of console statements suggest debugging code left in production.",
|
|
1423
|
-
confidence: "high",
|
|
1424
|
-
evidence: [],
|
|
1425
|
-
fixHints: ["Use ESLint no-console to catch automatically.", "Replace with a proper logging library (pino, winston, etc.)."],
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
1363
|
return findings;
|
|
1430
1364
|
}
|
|
1431
1365
|
|
|
@@ -1434,104 +1368,61 @@ function findConsoleLogs(repoRoot) {
|
|
|
1434
1368
|
* ========================================================================== */
|
|
1435
1369
|
|
|
1436
1370
|
function findHardcodedSecrets(repoRoot) {
|
|
1371
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1437
1372
|
const findings = [];
|
|
1438
1373
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx,json}"], {
|
|
1439
1374
|
cwd: repoRoot,
|
|
1440
1375
|
absolute: true,
|
|
1441
|
-
ignore: [
|
|
1376
|
+
ignore: [
|
|
1377
|
+
"**/node_modules/**",
|
|
1378
|
+
"**/.next/**",
|
|
1379
|
+
"**/dist/**",
|
|
1380
|
+
"**/build/**",
|
|
1381
|
+
"**/package*.json",
|
|
1382
|
+
"**/*.test.*",
|
|
1383
|
+
"**/tests/**",
|
|
1384
|
+
"**/*.d.ts",
|
|
1385
|
+
],
|
|
1442
1386
|
});
|
|
1443
1387
|
|
|
1444
|
-
// V3: Split patterns into "specific" (prefix-based, high confidence) and "generic" (entropy-based)
|
|
1445
|
-
// Specific patterns have distinctive prefixes - no entropy check needed
|
|
1446
|
-
const specificPatterns = [
|
|
1447
|
-
{ rx: /['"]sk_live_[a-zA-Z0-9]{20,}['"]/g, label: "Stripe live secret key", severity: "BLOCK" },
|
|
1448
|
-
{ rx: /['"]sk_test_[a-zA-Z0-9]{20,}['"]/g, label: "Stripe test secret key", severity: "WARN" },
|
|
1449
|
-
{ rx: /['"]pk_live_[a-zA-Z0-9]{20,}['"]/g, label: "Stripe live publishable key", severity: "BLOCK" },
|
|
1450
|
-
{ rx: /['"]AKIA[0-9A-Z]{16}['"]/g, label: "AWS Access Key ID", severity: "BLOCK" },
|
|
1451
|
-
{ rx: /['"]ghp_[a-zA-Z0-9]{36}['"]/g, label: "GitHub Personal Access Token", severity: "BLOCK" },
|
|
1452
|
-
{ rx: /['"]gho_[a-zA-Z0-9]{36}['"]/g, label: "GitHub OAuth Token", severity: "BLOCK" },
|
|
1453
|
-
{ rx: /['"]xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}['"]/g, label: "Slack Token", severity: "BLOCK" },
|
|
1454
|
-
{ rx: /['"]eyJ[a-zA-Z0-9_-]{100,}\.[a-zA-Z0-9_-]{100,}\.[a-zA-Z0-9_-]{43,}['"]/g, label: "JWT Token (hardcoded)", severity: "WARN" },
|
|
1455
|
-
];
|
|
1456
|
-
|
|
1457
|
-
// V3: Generic patterns need Shannon entropy check to avoid false positives (Git SHAs, image IDs, etc.)
|
|
1458
|
-
const genericPatterns = [
|
|
1459
|
-
{ rx: /['"]([a-zA-Z0-9+/]{40})['"]/g, label: "Possible AWS Secret Key", minEntropy: 4.5 },
|
|
1460
|
-
{ rx: /password\s*[:=]\s*['"]([^'"]{8,})['"]/gi, label: "Hardcoded password", minEntropy: 3.0 },
|
|
1461
|
-
{ rx: /api[_-]?key\s*[:=]\s*['"]([a-zA-Z0-9]{20,})['"]/gi, label: "Hardcoded API key", minEntropy: 4.0 },
|
|
1462
|
-
{ rx: /secret\s*[:=]\s*['"]([a-zA-Z0-9]{16,})['"]/gi, label: "Hardcoded secret", minEntropy: 4.0 },
|
|
1463
|
-
];
|
|
1464
|
-
|
|
1465
1388
|
for (const fileAbs of files) {
|
|
1466
1389
|
try {
|
|
1467
1390
|
const code = readFileCached(fileAbs);
|
|
1468
1391
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1469
|
-
if (/\.env/.test(fileRel)) continue;
|
|
1470
1392
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
// 1. Check specific patterns (prefix-based, high confidence - no entropy needed)
|
|
1474
|
-
for (const { rx, label, severity } of specificPatterns) {
|
|
1475
|
-
rx.lastIndex = 0;
|
|
1476
|
-
const matches = code.match(rx);
|
|
1477
|
-
if (matches && matches.length > 0) {
|
|
1478
|
-
findings.push({
|
|
1479
|
-
id: stableId("F_SECRET", `${fileRel}:${label}`),
|
|
1480
|
-
severity,
|
|
1481
|
-
category: "HardcodedSecret",
|
|
1482
|
-
title: `${label} detected in: ${fileRel}`,
|
|
1483
|
-
why: "Hardcoded secrets get committed and leaked. This is critical.",
|
|
1484
|
-
confidence: "high",
|
|
1485
|
-
evidence: [{ file: fileRel, reason: label }],
|
|
1486
|
-
fixHints: [
|
|
1487
|
-
"Move the secret to environment variables.",
|
|
1488
|
-
"Rotate the compromised secret immediately.",
|
|
1489
|
-
"Add the file to .gitignore if it shouldn't be committed.",
|
|
1490
|
-
],
|
|
1491
|
-
});
|
|
1492
|
-
foundInFile = true;
|
|
1493
|
-
break;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1393
|
+
// Use unified engines
|
|
1394
|
+
const engineFindings = engines.analyzeHardcodedSecrets(code, fileRel);
|
|
1496
1395
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
why: "This string looks mathematically random, which usually indicates a hardcoded secret key.",
|
|
1520
|
-
confidence: "med",
|
|
1521
|
-
evidence: [{ file: fileRel, reason: `Entropy: ${entropy.toFixed(2)} >= ${minEntropy}` }],
|
|
1522
|
-
fixHints: [
|
|
1396
|
+
// Convert engine findings to analyzer format
|
|
1397
|
+
for (const finding of engineFindings) {
|
|
1398
|
+
findings.push({
|
|
1399
|
+
id: stableId("F_SECRET", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1400
|
+
severity: finding.severity,
|
|
1401
|
+
category: finding.category,
|
|
1402
|
+
title: finding.title,
|
|
1403
|
+
message: finding.message,
|
|
1404
|
+
file: finding.file,
|
|
1405
|
+
line: finding.line,
|
|
1406
|
+
why: finding.severity === "BLOCK"
|
|
1407
|
+
? "Hardcoded secrets get committed and leaked. This is critical."
|
|
1408
|
+
: "This string looks mathematically random, which usually indicates a hardcoded secret key.",
|
|
1409
|
+
confidence: finding.confidence,
|
|
1410
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1411
|
+
fixHints: finding.severity === "BLOCK"
|
|
1412
|
+
? [
|
|
1413
|
+
"Move the secret to environment variables.",
|
|
1414
|
+
"Rotate the compromised secret immediately.",
|
|
1415
|
+
"Add the file to .gitignore if it shouldn't be committed.",
|
|
1416
|
+
]
|
|
1417
|
+
: [
|
|
1523
1418
|
"Move the secret to environment variables.",
|
|
1524
1419
|
"If this is not a secret, consider using a more descriptive variable name.",
|
|
1525
1420
|
],
|
|
1526
|
-
|
|
1527
|
-
foundInFile = true;
|
|
1528
|
-
break;
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
if (foundInFile) break;
|
|
1421
|
+
});
|
|
1532
1422
|
}
|
|
1533
|
-
} catch {
|
|
1534
|
-
//
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
// Skip files that can't be analyzed
|
|
1425
|
+
continue;
|
|
1535
1426
|
}
|
|
1536
1427
|
}
|
|
1537
1428
|
|
|
@@ -1539,47 +1430,51 @@ function findHardcodedSecrets(repoRoot) {
|
|
|
1539
1430
|
}
|
|
1540
1431
|
|
|
1541
1432
|
/* ============================================================================
|
|
1542
|
-
* DEAD CODE / UNUSED EXPORTS DETECTOR (
|
|
1433
|
+
* DEAD CODE / UNUSED EXPORTS DETECTOR (AST-based engine)
|
|
1543
1434
|
* ========================================================================== */
|
|
1544
1435
|
|
|
1545
1436
|
function findDeadCode(repoRoot) {
|
|
1437
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1546
1438
|
const findings = [];
|
|
1547
1439
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1548
1440
|
cwd: repoRoot,
|
|
1549
1441
|
absolute: true,
|
|
1550
|
-
ignore: [
|
|
1442
|
+
ignore: [
|
|
1443
|
+
"**/node_modules/**",
|
|
1444
|
+
"**/.next/**",
|
|
1445
|
+
"**/dist/**",
|
|
1446
|
+
"**/build/**",
|
|
1447
|
+
"**/*.d.ts",
|
|
1448
|
+
],
|
|
1551
1449
|
});
|
|
1552
1450
|
|
|
1553
|
-
const deadCodePatterns = [
|
|
1554
|
-
{ rx: /^\s*\/\/\s*export\s+(const|function|class|interface|type)/m, label: "Commented out export" },
|
|
1555
|
-
{ rx: /^\s*\/\*[\s\S]*?export[\s\S]*?\*\//m, label: "Block-commented export" },
|
|
1556
|
-
{ rx: /if\s*\(\s*false\s*\)\s*\{/m, label: "if (false) block" },
|
|
1557
|
-
{ rx: /if\s*\(\s*0\s*\)\s*\{/m, label: "if (0) block" },
|
|
1558
|
-
{ rx: /return;\s*\n\s*[^}]/m, label: "Unreachable code after return" },
|
|
1559
|
-
{ rx: /throw\s+new\s+Error[^;]*;\s*\n\s*[^}]/m, label: "Unreachable code after throw" },
|
|
1560
|
-
];
|
|
1561
|
-
|
|
1562
1451
|
for (const fileAbs of files) {
|
|
1563
1452
|
try {
|
|
1564
1453
|
const code = readFileCached(fileAbs);
|
|
1565
1454
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1455
|
+
|
|
1456
|
+
// Use unified engines
|
|
1457
|
+
const engineFindings = engines.analyzeDeadCode(code, fileRel);
|
|
1458
|
+
|
|
1459
|
+
// Convert engine findings to analyzer format
|
|
1460
|
+
for (const finding of engineFindings) {
|
|
1569
1461
|
findings.push({
|
|
1570
|
-
id: stableId("F_DEAD_CODE", `${fileRel}:${
|
|
1571
|
-
severity:
|
|
1572
|
-
category:
|
|
1573
|
-
title:
|
|
1462
|
+
id: stableId("F_DEAD_CODE", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1463
|
+
severity: finding.severity,
|
|
1464
|
+
category: finding.category,
|
|
1465
|
+
title: finding.title,
|
|
1466
|
+
message: finding.message,
|
|
1467
|
+
file: finding.file,
|
|
1468
|
+
line: finding.line,
|
|
1574
1469
|
why: "Dead code adds confusion and maintenance burden and usually indicates incomplete refactoring.",
|
|
1575
|
-
confidence:
|
|
1576
|
-
evidence: [{ file: fileRel, reason:
|
|
1470
|
+
confidence: finding.confidence,
|
|
1471
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1577
1472
|
fixHints: ["Remove the dead code entirely.", "If needed for reference, use git history instead of commenting."],
|
|
1578
1473
|
});
|
|
1579
|
-
break;
|
|
1580
1474
|
}
|
|
1581
|
-
} catch {
|
|
1582
|
-
//
|
|
1475
|
+
} catch (err) {
|
|
1476
|
+
// Skip files that can't be analyzed
|
|
1477
|
+
continue;
|
|
1583
1478
|
}
|
|
1584
1479
|
}
|
|
1585
1480
|
|
|
@@ -1591,47 +1486,47 @@ function findDeadCode(repoRoot) {
|
|
|
1591
1486
|
* ========================================================================== */
|
|
1592
1487
|
|
|
1593
1488
|
function findDeprecatedApis(repoRoot) {
|
|
1489
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1594
1490
|
const findings = [];
|
|
1595
1491
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1596
1492
|
cwd: repoRoot,
|
|
1597
1493
|
absolute: true,
|
|
1598
|
-
ignore: [
|
|
1494
|
+
ignore: [
|
|
1495
|
+
"**/node_modules/**",
|
|
1496
|
+
"**/.next/**",
|
|
1497
|
+
"**/dist/**",
|
|
1498
|
+
"**/build/**",
|
|
1499
|
+
"**/*.d.ts",
|
|
1500
|
+
],
|
|
1599
1501
|
});
|
|
1600
1502
|
|
|
1601
|
-
const deprecatedPatterns = [
|
|
1602
|
-
{ rx: /\bcomponentWillMount\b/, label: "componentWillMount (deprecated React lifecycle)" },
|
|
1603
|
-
{ rx: /\bcomponentWillReceiveProps\b/, label: "componentWillReceiveProps (deprecated)" },
|
|
1604
|
-
{ rx: /\bcomponentWillUpdate\b/, label: "componentWillUpdate (deprecated)" },
|
|
1605
|
-
{ rx: /\bgetInitialProps\b/, label: "getInitialProps (legacy Next.js)" },
|
|
1606
|
-
{ rx: /\bsubstr\s*\(/, label: "String.substr() (deprecated, use slice)" },
|
|
1607
|
-
{ rx: /\bdocument\.write\b/, label: "document.write (deprecated)" },
|
|
1608
|
-
{ rx: /new\s+Buffer\s*\(/, label: "new Buffer() (deprecated, use Buffer.from)" },
|
|
1609
|
-
{ rx: /\brequire\(['"]fs['"]\)\.exists\b/, label: "fs.exists (deprecated)" },
|
|
1610
|
-
{ rx: /\.__proto__\b/, label: "__proto__ (deprecated)" },
|
|
1611
|
-
];
|
|
1612
|
-
|
|
1613
1503
|
for (const fileAbs of files) {
|
|
1614
1504
|
try {
|
|
1615
1505
|
const code = readFileCached(fileAbs);
|
|
1616
1506
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1507
|
+
|
|
1508
|
+
// Use unified engines
|
|
1509
|
+
const engineFindings = engines.analyzeDeprecatedApi(code, fileRel);
|
|
1510
|
+
|
|
1511
|
+
// Convert engine findings to analyzer format
|
|
1512
|
+
for (const finding of engineFindings) {
|
|
1621
1513
|
findings.push({
|
|
1622
|
-
id: stableId("F_DEPRECATED", `${fileRel}:${
|
|
1623
|
-
severity:
|
|
1624
|
-
category:
|
|
1625
|
-
title:
|
|
1514
|
+
id: stableId("F_DEPRECATED", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1515
|
+
severity: finding.severity,
|
|
1516
|
+
category: finding.category,
|
|
1517
|
+
title: finding.title,
|
|
1518
|
+
message: finding.message,
|
|
1519
|
+
file: finding.file,
|
|
1520
|
+
line: finding.line,
|
|
1626
1521
|
why: "Deprecated APIs may break in future versions and sometimes carry security issues.",
|
|
1627
|
-
confidence:
|
|
1628
|
-
evidence: [{ file: fileRel, reason:
|
|
1629
|
-
fixHints: ["Update to the modern API equivalent.", "Check migration guides for the specific deprecation."],
|
|
1522
|
+
confidence: finding.confidence,
|
|
1523
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1524
|
+
fixHints: finding.replacement ? [`Use ${finding.replacement} instead.`, "Check migration guides for the specific deprecation."] : ["Update to the modern API equivalent.", "Check migration guides for the specific deprecation."],
|
|
1630
1525
|
});
|
|
1631
|
-
break;
|
|
1632
1526
|
}
|
|
1633
|
-
} catch {
|
|
1634
|
-
//
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
// Skip files that can't be analyzed
|
|
1529
|
+
continue;
|
|
1635
1530
|
}
|
|
1636
1531
|
}
|
|
1637
1532
|
|
|
@@ -1639,39 +1534,51 @@ function findDeprecatedApis(repoRoot) {
|
|
|
1639
1534
|
}
|
|
1640
1535
|
|
|
1641
1536
|
/* ============================================================================
|
|
1642
|
-
* EMPTY CATCH BLOCKS DETECTOR (
|
|
1537
|
+
* EMPTY CATCH BLOCKS DETECTOR (AST-based engine)
|
|
1643
1538
|
* ========================================================================== */
|
|
1644
1539
|
|
|
1645
1540
|
function findEmptyCatch(repoRoot) {
|
|
1541
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1646
1542
|
const findings = [];
|
|
1647
1543
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1648
1544
|
cwd: repoRoot,
|
|
1649
1545
|
absolute: true,
|
|
1650
|
-
ignore: [
|
|
1546
|
+
ignore: [
|
|
1547
|
+
"**/node_modules/**",
|
|
1548
|
+
"**/.next/**",
|
|
1549
|
+
"**/dist/**",
|
|
1550
|
+
"**/build/**",
|
|
1551
|
+
"**/*.d.ts",
|
|
1552
|
+
],
|
|
1651
1553
|
});
|
|
1652
1554
|
|
|
1653
1555
|
for (const fileAbs of files) {
|
|
1654
1556
|
try {
|
|
1655
1557
|
const code = readFileCached(fileAbs);
|
|
1656
1558
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
|
|
1559
|
+
|
|
1560
|
+
// Use unified engines
|
|
1561
|
+
const engineFindings = engines.analyzeEmptyCatch(code, fileRel);
|
|
1562
|
+
|
|
1563
|
+
// Convert engine findings to analyzer format
|
|
1564
|
+
for (const finding of engineFindings) {
|
|
1662
1565
|
findings.push({
|
|
1663
|
-
id: stableId("F_EMPTY_CATCH", fileRel),
|
|
1664
|
-
severity:
|
|
1665
|
-
category:
|
|
1666
|
-
title:
|
|
1566
|
+
id: stableId("F_EMPTY_CATCH", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1567
|
+
severity: finding.severity,
|
|
1568
|
+
category: finding.category,
|
|
1569
|
+
title: finding.title,
|
|
1570
|
+
message: finding.message,
|
|
1571
|
+
file: finding.file,
|
|
1572
|
+
line: finding.line,
|
|
1667
1573
|
why: "Empty catch blocks swallow errors and make debugging impossible.",
|
|
1668
|
-
confidence:
|
|
1669
|
-
evidence: [{ file: fileRel, reason:
|
|
1574
|
+
confidence: finding.confidence,
|
|
1575
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1670
1576
|
fixHints: ["Log the error or handle it appropriately.", "If intentionally ignoring, add a comment explaining why."],
|
|
1671
1577
|
});
|
|
1672
1578
|
}
|
|
1673
|
-
} catch {
|
|
1674
|
-
//
|
|
1579
|
+
} catch (err) {
|
|
1580
|
+
// Skip files that can't be analyzed
|
|
1581
|
+
continue;
|
|
1675
1582
|
}
|
|
1676
1583
|
}
|
|
1677
1584
|
|
|
@@ -1683,40 +1590,405 @@ function findEmptyCatch(repoRoot) {
|
|
|
1683
1590
|
* ========================================================================== */
|
|
1684
1591
|
|
|
1685
1592
|
function findUnsafeRegex(repoRoot) {
|
|
1593
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1686
1594
|
const findings = [];
|
|
1687
1595
|
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1688
1596
|
cwd: repoRoot,
|
|
1689
1597
|
absolute: true,
|
|
1690
|
-
ignore: [
|
|
1598
|
+
ignore: [
|
|
1599
|
+
"**/node_modules/**",
|
|
1600
|
+
"**/.next/**",
|
|
1601
|
+
"**/dist/**",
|
|
1602
|
+
"**/build/**",
|
|
1603
|
+
"**/*.d.ts",
|
|
1604
|
+
],
|
|
1691
1605
|
});
|
|
1692
1606
|
|
|
1693
|
-
const unsafePatterns = [
|
|
1694
|
-
{ rx: /new\s+RegExp\s*\([^)]*\+[^)]*\)/, label: "Dynamic regex with concatenation" },
|
|
1695
|
-
{ rx: /\(\.\*\)\+|\(\.\+\)\+|\(\.\*\)\*|\(\.\+\)\*/, label: "Nested quantifiers (ReDoS risk)" },
|
|
1696
|
-
{ rx: /\([^)]+\|[^)]+\)\+/, label: "Alternation with quantifier (ReDoS risk)" },
|
|
1697
|
-
];
|
|
1698
|
-
|
|
1699
1607
|
for (const fileAbs of files) {
|
|
1700
1608
|
try {
|
|
1701
1609
|
const code = readFileCached(fileAbs);
|
|
1702
1610
|
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1611
|
+
|
|
1612
|
+
// Use unified engines
|
|
1613
|
+
const engineFindings = engines.analyzeUnsafeRegex(code, fileRel);
|
|
1614
|
+
|
|
1615
|
+
// Convert engine findings to analyzer format
|
|
1616
|
+
for (const finding of engineFindings) {
|
|
1706
1617
|
findings.push({
|
|
1707
|
-
id: stableId("F_UNSAFE_REGEX", `${fileRel}:${
|
|
1708
|
-
severity:
|
|
1709
|
-
category:
|
|
1710
|
-
title:
|
|
1618
|
+
id: stableId("F_UNSAFE_REGEX", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1619
|
+
severity: finding.severity,
|
|
1620
|
+
category: finding.category,
|
|
1621
|
+
title: finding.title,
|
|
1622
|
+
message: finding.message,
|
|
1623
|
+
file: finding.file,
|
|
1624
|
+
line: finding.line,
|
|
1711
1625
|
why: "Unsafe regex patterns can cause denial of service via catastrophic backtracking.",
|
|
1712
|
-
confidence:
|
|
1713
|
-
evidence: [{ file: fileRel, reason:
|
|
1626
|
+
confidence: finding.confidence,
|
|
1627
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1714
1628
|
fixHints: ["Validate input length before applying regex.", "Consider safer parsing or a regex linter.", "Avoid nested quantifiers."],
|
|
1715
1629
|
});
|
|
1716
|
-
break;
|
|
1717
1630
|
}
|
|
1718
|
-
} catch {
|
|
1719
|
-
//
|
|
1631
|
+
} catch (err) {
|
|
1632
|
+
// Skip files that can't be analyzed
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
return findings;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/* ============================================================================
|
|
1641
|
+
* NEW SECURITY & PERFORMANCE ANALYZERS
|
|
1642
|
+
* ========================================================================== */
|
|
1643
|
+
|
|
1644
|
+
function findSecurityVulnerabilities(repoRoot) {
|
|
1645
|
+
// Note: Security vulnerabilities engine not in unified engines yet
|
|
1646
|
+
// Keep using the existing engine for now
|
|
1647
|
+
const { analyzeSecurityVulnerabilities } = require("./engines/security-vulnerabilities-engine");
|
|
1648
|
+
const findings = [];
|
|
1649
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1650
|
+
cwd: repoRoot,
|
|
1651
|
+
absolute: true,
|
|
1652
|
+
ignore: [
|
|
1653
|
+
"**/node_modules/**",
|
|
1654
|
+
"**/.next/**",
|
|
1655
|
+
"**/dist/**",
|
|
1656
|
+
"**/build/**",
|
|
1657
|
+
"**/*.d.ts",
|
|
1658
|
+
],
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
for (const fileAbs of files) {
|
|
1662
|
+
try {
|
|
1663
|
+
const code = readFileCached(fileAbs);
|
|
1664
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1665
|
+
|
|
1666
|
+
const engineFindings = analyzeSecurityVulnerabilities(code, fileRel);
|
|
1667
|
+
|
|
1668
|
+
for (const finding of engineFindings) {
|
|
1669
|
+
findings.push({
|
|
1670
|
+
id: stableId("F_SECURITY", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1671
|
+
severity: finding.severity,
|
|
1672
|
+
category: finding.category,
|
|
1673
|
+
title: finding.title,
|
|
1674
|
+
message: finding.message,
|
|
1675
|
+
file: finding.file,
|
|
1676
|
+
line: finding.line,
|
|
1677
|
+
why: "Security vulnerabilities can lead to data breaches, unauthorized access, or system compromise.",
|
|
1678
|
+
confidence: finding.confidence,
|
|
1679
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1680
|
+
fixHints: [
|
|
1681
|
+
"Review security best practices for the detected vulnerability type.",
|
|
1682
|
+
"Use parameterized queries for SQL operations.",
|
|
1683
|
+
"Sanitize and validate all user inputs.",
|
|
1684
|
+
"Use Content Security Policy (CSP) headers for XSS protection.",
|
|
1685
|
+
],
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
} catch (err) {
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
return findings;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
function findPerformanceIssues(repoRoot) {
|
|
1697
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1698
|
+
const findings = [];
|
|
1699
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1700
|
+
cwd: repoRoot,
|
|
1701
|
+
absolute: true,
|
|
1702
|
+
ignore: [
|
|
1703
|
+
"**/node_modules/**",
|
|
1704
|
+
"**/.next/**",
|
|
1705
|
+
"**/dist/**",
|
|
1706
|
+
"**/build/**",
|
|
1707
|
+
"**/*.d.ts",
|
|
1708
|
+
],
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
for (const fileAbs of files) {
|
|
1712
|
+
try {
|
|
1713
|
+
const code = readFileCached(fileAbs);
|
|
1714
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1715
|
+
|
|
1716
|
+
// Use unified engines
|
|
1717
|
+
const engineFindings = engines.analyzePerformanceIssues(code, fileRel);
|
|
1718
|
+
|
|
1719
|
+
for (const finding of engineFindings) {
|
|
1720
|
+
findings.push({
|
|
1721
|
+
id: stableId("F_PERF", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1722
|
+
severity: finding.severity,
|
|
1723
|
+
category: finding.category,
|
|
1724
|
+
title: finding.title,
|
|
1725
|
+
message: finding.message,
|
|
1726
|
+
file: finding.file,
|
|
1727
|
+
line: finding.line,
|
|
1728
|
+
why: "Performance issues can degrade user experience and increase server costs.",
|
|
1729
|
+
confidence: finding.confidence,
|
|
1730
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1731
|
+
fixHints: [
|
|
1732
|
+
"Optimize algorithms and data structures.",
|
|
1733
|
+
"Use pagination for large datasets.",
|
|
1734
|
+
"Remove unnecessary re-renders.",
|
|
1735
|
+
"Use async/await for I/O operations.",
|
|
1736
|
+
],
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
} catch (err) {
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
return findings;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
function findCodeQualityIssues(repoRoot) {
|
|
1748
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1749
|
+
const findings = [];
|
|
1750
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1751
|
+
cwd: repoRoot,
|
|
1752
|
+
absolute: true,
|
|
1753
|
+
ignore: [
|
|
1754
|
+
"**/node_modules/**",
|
|
1755
|
+
"**/.next/**",
|
|
1756
|
+
"**/dist/**",
|
|
1757
|
+
"**/build/**",
|
|
1758
|
+
"**/*.d.ts",
|
|
1759
|
+
],
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
for (const fileAbs of files) {
|
|
1763
|
+
try {
|
|
1764
|
+
const code = readFileCached(fileAbs);
|
|
1765
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1766
|
+
|
|
1767
|
+
// Use unified engines
|
|
1768
|
+
const engineFindings = engines.analyzeCodeQuality(code, fileRel);
|
|
1769
|
+
|
|
1770
|
+
for (const finding of engineFindings) {
|
|
1771
|
+
findings.push({
|
|
1772
|
+
id: stableId("F_QUALITY", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1773
|
+
severity: finding.severity,
|
|
1774
|
+
category: finding.category,
|
|
1775
|
+
title: finding.title,
|
|
1776
|
+
message: finding.message,
|
|
1777
|
+
file: finding.file,
|
|
1778
|
+
line: finding.line,
|
|
1779
|
+
why: "Code quality issues make code harder to maintain, test, and extend.",
|
|
1780
|
+
confidence: finding.confidence,
|
|
1781
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1782
|
+
fixHints: [
|
|
1783
|
+
"Break down complex functions into smaller, focused functions.",
|
|
1784
|
+
"Extract magic numbers into named constants.",
|
|
1785
|
+
"Reduce nesting depth with early returns.",
|
|
1786
|
+
"Consider design patterns for common problems.",
|
|
1787
|
+
],
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
return findings;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
function findCrossFileIssues(repoRoot) {
|
|
1799
|
+
const { analyzeCrossFile } = require("./engines/cross-file-analysis-engine");
|
|
1800
|
+
const fg = require("fast-glob");
|
|
1801
|
+
|
|
1802
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1803
|
+
cwd: repoRoot,
|
|
1804
|
+
absolute: true,
|
|
1805
|
+
ignore: [
|
|
1806
|
+
"**/node_modules/**",
|
|
1807
|
+
"**/.next/**",
|
|
1808
|
+
"**/dist/**",
|
|
1809
|
+
"**/build/**",
|
|
1810
|
+
"**/*.d.ts",
|
|
1811
|
+
"**/*.test.*",
|
|
1812
|
+
"**/*.spec.*",
|
|
1813
|
+
"**/tests/**",
|
|
1814
|
+
],
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1817
|
+
const engineFindings = analyzeCrossFile(files, repoRoot);
|
|
1818
|
+
const findings = [];
|
|
1819
|
+
|
|
1820
|
+
for (const finding of engineFindings) {
|
|
1821
|
+
findings.push({
|
|
1822
|
+
id: stableId("F_CROSSFILE", `${finding.file}:${finding.type}`),
|
|
1823
|
+
severity: finding.severity,
|
|
1824
|
+
category: finding.category,
|
|
1825
|
+
title: finding.title,
|
|
1826
|
+
message: finding.message,
|
|
1827
|
+
file: finding.file,
|
|
1828
|
+
line: finding.line,
|
|
1829
|
+
why: "Cross-file issues indicate architectural problems that can cause maintenance difficulties.",
|
|
1830
|
+
confidence: finding.confidence,
|
|
1831
|
+
evidence: [{ file: finding.file, reason: finding.title }],
|
|
1832
|
+
fixHints: [
|
|
1833
|
+
"Remove unused exports or mark them as internal.",
|
|
1834
|
+
"Refactor to break circular dependencies.",
|
|
1835
|
+
"Standardize import paths across the codebase.",
|
|
1836
|
+
],
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
return findings;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
function findTypeSafetyIssues(repoRoot) {
|
|
1844
|
+
const engines = require("./engines/vibecheck-engines");
|
|
1845
|
+
const findings = [];
|
|
1846
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1847
|
+
cwd: repoRoot,
|
|
1848
|
+
absolute: true,
|
|
1849
|
+
ignore: [
|
|
1850
|
+
"**/node_modules/**",
|
|
1851
|
+
"**/.next/**",
|
|
1852
|
+
"**/dist/**",
|
|
1853
|
+
"**/build/**",
|
|
1854
|
+
"**/*.d.ts",
|
|
1855
|
+
],
|
|
1856
|
+
});
|
|
1857
|
+
|
|
1858
|
+
for (const fileAbs of files) {
|
|
1859
|
+
try {
|
|
1860
|
+
const code = readFileCached(fileAbs);
|
|
1861
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1862
|
+
|
|
1863
|
+
// Use unified engines
|
|
1864
|
+
const engineFindings = engines.analyzeTypeAware(code, fileRel);
|
|
1865
|
+
|
|
1866
|
+
for (const finding of engineFindings) {
|
|
1867
|
+
findings.push({
|
|
1868
|
+
id: stableId("F_TYPE", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1869
|
+
severity: finding.severity,
|
|
1870
|
+
category: finding.category,
|
|
1871
|
+
title: finding.title,
|
|
1872
|
+
message: finding.message,
|
|
1873
|
+
file: finding.file,
|
|
1874
|
+
line: finding.line,
|
|
1875
|
+
why: "Type safety issues can lead to runtime errors and make code harder to maintain.",
|
|
1876
|
+
confidence: finding.confidence,
|
|
1877
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1878
|
+
fixHints: [
|
|
1879
|
+
"Use proper TypeScript types instead of 'any'.",
|
|
1880
|
+
"Fix underlying type errors instead of suppressing them.",
|
|
1881
|
+
"Add explicit return type annotations.",
|
|
1882
|
+
],
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
} catch (err) {
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
return findings;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function findAccessibilityIssues(repoRoot) {
|
|
1894
|
+
const { analyzeAccessibility } = require("./engines/accessibility-engine");
|
|
1895
|
+
const findings = [];
|
|
1896
|
+
const files = fg.sync(["**/*.{tsx,jsx}"], {
|
|
1897
|
+
cwd: repoRoot,
|
|
1898
|
+
absolute: true,
|
|
1899
|
+
ignore: [
|
|
1900
|
+
"**/node_modules/**",
|
|
1901
|
+
"**/.next/**",
|
|
1902
|
+
"**/dist/**",
|
|
1903
|
+
"**/build/**",
|
|
1904
|
+
"**/*.d.ts",
|
|
1905
|
+
"**/*.test.*",
|
|
1906
|
+
"**/*.spec.*",
|
|
1907
|
+
"**/tests/**",
|
|
1908
|
+
],
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1911
|
+
for (const fileAbs of files) {
|
|
1912
|
+
try {
|
|
1913
|
+
const code = readFileCached(fileAbs);
|
|
1914
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1915
|
+
|
|
1916
|
+
const engineFindings = analyzeAccessibility(code, fileRel);
|
|
1917
|
+
|
|
1918
|
+
for (const finding of engineFindings) {
|
|
1919
|
+
findings.push({
|
|
1920
|
+
id: stableId("F_A11Y", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1921
|
+
severity: finding.severity,
|
|
1922
|
+
category: finding.category,
|
|
1923
|
+
title: finding.title,
|
|
1924
|
+
message: finding.message,
|
|
1925
|
+
file: finding.file,
|
|
1926
|
+
line: finding.line,
|
|
1927
|
+
why: "Accessibility issues prevent users with disabilities from using your application.",
|
|
1928
|
+
confidence: finding.confidence,
|
|
1929
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1930
|
+
fixHints: [
|
|
1931
|
+
"Add alt text to all images.",
|
|
1932
|
+
"Ensure all interactive elements have accessible labels.",
|
|
1933
|
+
"Add keyboard handlers for custom interactive elements.",
|
|
1934
|
+
"Test with screen readers.",
|
|
1935
|
+
],
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
continue;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
return findings;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
function findAPIConsistencyIssues(repoRoot) {
|
|
1947
|
+
const { analyzeAPIConsistency } = require("./engines/api-consistency-engine");
|
|
1948
|
+
const findings = [];
|
|
1949
|
+
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
1950
|
+
cwd: repoRoot,
|
|
1951
|
+
absolute: true,
|
|
1952
|
+
ignore: [
|
|
1953
|
+
"**/node_modules/**",
|
|
1954
|
+
"**/.next/**",
|
|
1955
|
+
"**/dist/**",
|
|
1956
|
+
"**/build/**",
|
|
1957
|
+
"**/*.d.ts",
|
|
1958
|
+
"**/*.test.*",
|
|
1959
|
+
"**/*.spec.*",
|
|
1960
|
+
"**/tests/**",
|
|
1961
|
+
],
|
|
1962
|
+
});
|
|
1963
|
+
|
|
1964
|
+
for (const fileAbs of files) {
|
|
1965
|
+
try {
|
|
1966
|
+
const code = readFileCached(fileAbs);
|
|
1967
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
1968
|
+
|
|
1969
|
+
const engineFindings = analyzeAPIConsistency(code, fileRel);
|
|
1970
|
+
|
|
1971
|
+
for (const finding of engineFindings) {
|
|
1972
|
+
findings.push({
|
|
1973
|
+
id: stableId("F_API", `${fileRel}:${finding.type}:${finding.line}`),
|
|
1974
|
+
severity: finding.severity,
|
|
1975
|
+
category: finding.category,
|
|
1976
|
+
title: finding.title,
|
|
1977
|
+
message: finding.message,
|
|
1978
|
+
file: finding.file,
|
|
1979
|
+
line: finding.line,
|
|
1980
|
+
why: "API consistency issues make APIs harder to use and maintain.",
|
|
1981
|
+
confidence: finding.confidence,
|
|
1982
|
+
evidence: [{ file: fileRel, reason: finding.title, line: finding.line }],
|
|
1983
|
+
fixHints: [
|
|
1984
|
+
"Standardize response formats across all API routes.",
|
|
1985
|
+
"Add consistent error handling.",
|
|
1986
|
+
"Always return explicit HTTP status codes.",
|
|
1987
|
+
],
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
} catch (err) {
|
|
1991
|
+
continue;
|
|
1720
1992
|
}
|
|
1721
1993
|
}
|
|
1722
1994
|
|
|
@@ -1746,4 +2018,13 @@ module.exports = {
|
|
|
1746
2018
|
findDeprecatedApis,
|
|
1747
2019
|
findEmptyCatch,
|
|
1748
2020
|
findUnsafeRegex,
|
|
2021
|
+
// Enhanced analyzers
|
|
2022
|
+
findSecurityVulnerabilities,
|
|
2023
|
+
findPerformanceIssues,
|
|
2024
|
+
findCodeQualityIssues,
|
|
2025
|
+
// Advanced analyzers
|
|
2026
|
+
findCrossFileIssues,
|
|
2027
|
+
findTypeSafetyIssues,
|
|
2028
|
+
findAccessibilityIssues,
|
|
2029
|
+
findAPIConsistencyIssues,
|
|
1749
2030
|
};
|