@vibecheckai/cli 3.5.0 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1018,108 +1018,177 @@ async function resolvePythonClientRefs(repoRoot) {
|
|
|
1018
1018
|
// MAIN: RESOLVE ALL ROUTES
|
|
1019
1019
|
// ============================================================================
|
|
1020
1020
|
|
|
1021
|
-
|
|
1021
|
+
// Smart orchestration: only run expensive scanners when signals say they matter.
|
|
1022
|
+
// You can force the old behavior by setting:
|
|
1023
|
+
// VIBECHECK_ROUTE_SCAN_MODE=deep
|
|
1024
|
+
// or calling resolveAllRoutes(repoRoot, { mode: "deep" }).
|
|
1025
|
+
async function resolveAllRoutes(repoRoot, opts = {}) {
|
|
1026
|
+
const mode = (opts.mode || process.env.VIBECHECK_ROUTE_SCAN_MODE || "smart").toLowerCase();
|
|
1027
|
+
const verbose = !!(opts.verbose || process.env.VIBECHECK_VERBOSE_ROUTES);
|
|
1028
|
+
const skipExpress = !!opts.skipExpress; // When true, skip Express (handled by optimized extractor)
|
|
1029
|
+
|
|
1022
1030
|
const frameworks = await detectFrameworks(repoRoot);
|
|
1023
|
-
|
|
1031
|
+
|
|
1024
1032
|
const allRoutes = [];
|
|
1025
1033
|
const allClientRefs = [];
|
|
1026
1034
|
const gaps = [];
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
//
|
|
1031
|
-
|
|
1035
|
+
|
|
1036
|
+
const fw = new Set(frameworks.map((x) => String(x).toLowerCase()));
|
|
1037
|
+
|
|
1038
|
+
// Lightweight language presence checks (avoid globbing millions of files)
|
|
1039
|
+
const has = {
|
|
1040
|
+
python: exists(path.join(repoRoot, "requirements.txt")) || exists(path.join(repoRoot, "pyproject.toml")),
|
|
1041
|
+
ruby: exists(path.join(repoRoot, "Gemfile")),
|
|
1042
|
+
go: exists(path.join(repoRoot, "go.mod")),
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// If we don't have explicit deps, do a quick filesystem sniff.
|
|
1046
|
+
// (We intentionally keep this cheap: just check for any matching file.)
|
|
1047
|
+
if (!has.python) {
|
|
1048
|
+
try {
|
|
1049
|
+
const py = await fg(["**/*.py"], { cwd: repoRoot, absolute: true, ignore: ["**/node_modules/**", "**/.venv/**", "**/venv/**", "**/.git/**", "**/dist/**", "**/build/**"] , deep: 4 });
|
|
1050
|
+
has.python = py.length > 0;
|
|
1051
|
+
} catch {}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const shouldDeepScan = mode === "deep";
|
|
1055
|
+
|
|
1056
|
+
if (verbose) {
|
|
1057
|
+
const list = frameworks.length ? frameworks.join(", ") : "none";
|
|
1058
|
+
console.log(` 📦 Detected frameworks: ${list} (mode=${mode})`);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// JS/TS frameworks
|
|
1062
|
+
const shouldExpress = shouldDeepScan || fw.has("express");
|
|
1063
|
+
const shouldHono = shouldDeepScan || fw.has("hono");
|
|
1064
|
+
const shouldKoa = shouldDeepScan || fw.has("koa");
|
|
1065
|
+
const shouldNest = shouldDeepScan || fw.has("nestjs");
|
|
1066
|
+
|
|
1067
|
+
// Python frameworks
|
|
1068
|
+
const shouldFlask = shouldDeepScan || fw.has("flask") || (has.python && fw.size === 0);
|
|
1069
|
+
const shouldFastAPI = shouldDeepScan || fw.has("fastapi") || (has.python && fw.size === 0);
|
|
1070
|
+
const shouldDjango = shouldDeepScan || fw.has("django") || (has.python && fw.size === 0);
|
|
1071
|
+
|
|
1072
|
+
// Go frameworks
|
|
1073
|
+
const shouldGo = shouldDeepScan || fw.has("go") || has.go;
|
|
1074
|
+
|
|
1075
|
+
// OpenAPI/GraphQL are cheap and high-signal, so include when present.
|
|
1076
|
+
const shouldOpenAPI = shouldDeepScan || fw.has("openapi");
|
|
1077
|
+
const shouldGraphQL = shouldDeepScan || fw.has("graphql");
|
|
1078
|
+
|
|
1079
|
+
// If nothing detected and not deep mode, keep JS scanning conservative.
|
|
1080
|
+
// (Truthpack already handles Next/Fastify separately; this is a safety net.)
|
|
1081
|
+
const unknownRepo = fw.size === 0 && !has.python && !has.go && !has.ruby;
|
|
1082
|
+
const safeJsScan = shouldDeepScan || !unknownRepo;
|
|
1083
|
+
|
|
1032
1084
|
try {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1085
|
+
if (safeJsScan && shouldExpress && !skipExpress) {
|
|
1086
|
+
const expressRoutes = await resolveExpressRoutes(repoRoot);
|
|
1087
|
+
if (expressRoutes.length > 0) {
|
|
1088
|
+
if (verbose) console.log(` ✓ Express: ${expressRoutes.length} routes`);
|
|
1089
|
+
allRoutes.push(...expressRoutes);
|
|
1090
|
+
}
|
|
1091
|
+
} else if (skipExpress && verbose) {
|
|
1092
|
+
console.log(` ⏭️ Express: skipped (using optimized extractor)`);
|
|
1037
1093
|
}
|
|
1038
1094
|
} catch (e) { gaps.push({ kind: "express_scan_error", error: e.message }); }
|
|
1039
|
-
|
|
1095
|
+
|
|
1040
1096
|
try {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1097
|
+
if (safeJsScan && shouldHono) {
|
|
1098
|
+
const honoRoutes = await resolveHonoRoutes(repoRoot);
|
|
1099
|
+
if (honoRoutes.length > 0) {
|
|
1100
|
+
if (verbose) console.log(` ✓ Hono: ${honoRoutes.length} routes`);
|
|
1101
|
+
allRoutes.push(...honoRoutes);
|
|
1102
|
+
}
|
|
1045
1103
|
}
|
|
1046
1104
|
} catch (e) { gaps.push({ kind: "hono_scan_error", error: e.message }); }
|
|
1047
|
-
|
|
1105
|
+
|
|
1048
1106
|
try {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1107
|
+
if (safeJsScan && shouldKoa) {
|
|
1108
|
+
const koaRoutes = await resolveKoaRoutes(repoRoot);
|
|
1109
|
+
if (koaRoutes.length > 0) {
|
|
1110
|
+
if (verbose) console.log(` ✓ Koa: ${koaRoutes.length} routes`);
|
|
1111
|
+
allRoutes.push(...koaRoutes);
|
|
1112
|
+
}
|
|
1053
1113
|
}
|
|
1054
1114
|
} catch (e) { gaps.push({ kind: "koa_scan_error", error: e.message }); }
|
|
1055
|
-
|
|
1056
|
-
//
|
|
1115
|
+
|
|
1116
|
+
// NestJS route extraction is expensive and flaky; only run it when clearly present.
|
|
1117
|
+
if (shouldNest) {
|
|
1118
|
+
// (No dedicated resolver today; keep placeholder for future.)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1057
1121
|
try {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1122
|
+
if (shouldFlask) {
|
|
1123
|
+
const flaskRoutes = await resolveFlaskRoutes(repoRoot);
|
|
1124
|
+
if (flaskRoutes.length > 0) {
|
|
1125
|
+
if (verbose) console.log(` ✓ Flask: ${flaskRoutes.length} routes`);
|
|
1126
|
+
allRoutes.push(...flaskRoutes);
|
|
1127
|
+
}
|
|
1062
1128
|
}
|
|
1063
1129
|
} catch (e) { gaps.push({ kind: "flask_scan_error", error: e.message }); }
|
|
1064
|
-
|
|
1130
|
+
|
|
1065
1131
|
try {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1132
|
+
if (shouldFastAPI) {
|
|
1133
|
+
const fastapiRoutes = await resolveFastAPIRoutes(repoRoot);
|
|
1134
|
+
if (fastapiRoutes.length > 0) {
|
|
1135
|
+
if (verbose) console.log(` ✓ FastAPI: ${fastapiRoutes.length} routes`);
|
|
1136
|
+
allRoutes.push(...fastapiRoutes);
|
|
1137
|
+
}
|
|
1070
1138
|
}
|
|
1071
1139
|
} catch (e) { gaps.push({ kind: "fastapi_scan_error", error: e.message }); }
|
|
1072
|
-
|
|
1140
|
+
|
|
1073
1141
|
try {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1142
|
+
if (shouldDjango) {
|
|
1143
|
+
const djangoRoutes = await resolveDjangoRoutes(repoRoot);
|
|
1144
|
+
if (djangoRoutes.length > 0) {
|
|
1145
|
+
if (verbose) console.log(` ✓ Django: ${djangoRoutes.length} routes`);
|
|
1146
|
+
allRoutes.push(...djangoRoutes);
|
|
1147
|
+
}
|
|
1078
1148
|
}
|
|
1079
1149
|
} catch (e) { gaps.push({ kind: "django_scan_error", error: e.message }); }
|
|
1080
|
-
|
|
1081
|
-
// Python client refs
|
|
1150
|
+
|
|
1082
1151
|
try {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1152
|
+
if (has.python && (shouldDeepScan || shouldFlask || shouldFastAPI || shouldDjango)) {
|
|
1153
|
+
const pythonRefs = await resolvePythonClientRefs(repoRoot);
|
|
1154
|
+
if (pythonRefs.length > 0) {
|
|
1155
|
+
if (verbose) console.log(` ✓ Python client refs: ${pythonRefs.length}`);
|
|
1156
|
+
allClientRefs.push(...pythonRefs);
|
|
1157
|
+
}
|
|
1087
1158
|
}
|
|
1088
1159
|
} catch (e) { gaps.push({ kind: "python_client_scan_error", error: e.message }); }
|
|
1089
|
-
|
|
1090
|
-
// OpenAPI/Swagger specs (high accuracy)
|
|
1160
|
+
|
|
1091
1161
|
try {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1162
|
+
if (shouldOpenAPI) {
|
|
1163
|
+
const openapiRoutes = await resolveOpenAPIRoutes(repoRoot);
|
|
1164
|
+
if (openapiRoutes.length > 0) {
|
|
1165
|
+
if (verbose) console.log(` ✓ OpenAPI spec: ${openapiRoutes.length} routes`);
|
|
1166
|
+
allRoutes.push(...openapiRoutes);
|
|
1167
|
+
}
|
|
1096
1168
|
}
|
|
1097
1169
|
} catch (e) { gaps.push({ kind: "openapi_scan_error", error: e.message }); }
|
|
1098
|
-
|
|
1099
|
-
// GraphQL endpoints
|
|
1170
|
+
|
|
1100
1171
|
try {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1172
|
+
if (shouldGraphQL) {
|
|
1173
|
+
const graphqlRoutes = await resolveGraphQLRoutes(repoRoot);
|
|
1174
|
+
if (graphqlRoutes.length > 0) {
|
|
1175
|
+
if (verbose) console.log(` ✓ GraphQL: ${graphqlRoutes.length} endpoints`);
|
|
1176
|
+
allRoutes.push(...graphqlRoutes);
|
|
1177
|
+
}
|
|
1105
1178
|
}
|
|
1106
1179
|
} catch (e) { gaps.push({ kind: "graphql_scan_error", error: e.message }); }
|
|
1107
|
-
|
|
1108
|
-
// Go frameworks (Gin, Echo, Fiber, Chi, Gorilla, stdlib)
|
|
1180
|
+
|
|
1109
1181
|
try {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1182
|
+
if (shouldGo) {
|
|
1183
|
+
const goRoutes = await resolveGoRoutes(repoRoot);
|
|
1184
|
+
if (goRoutes.length > 0) {
|
|
1185
|
+
if (verbose) console.log(` ✓ Go: ${goRoutes.length} routes`);
|
|
1186
|
+
allRoutes.push(...goRoutes);
|
|
1187
|
+
}
|
|
1114
1188
|
}
|
|
1115
1189
|
} catch (e) { gaps.push({ kind: "go_scan_error", error: e.message }); }
|
|
1116
|
-
|
|
1117
|
-
return {
|
|
1118
|
-
routes: allRoutes,
|
|
1119
|
-
clientRefs: allClientRefs,
|
|
1120
|
-
gaps,
|
|
1121
|
-
frameworks
|
|
1122
|
-
};
|
|
1190
|
+
|
|
1191
|
+
return { routes: allRoutes, clientRefs: allClientRefs, gaps, frameworks };
|
|
1123
1192
|
}
|
|
1124
1193
|
|
|
1125
1194
|
module.exports = {
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
* Features:
|
|
4
4
|
* - "SCAN" ASCII Art
|
|
5
5
|
* - Fixed alignment tables
|
|
6
|
-
* -
|
|
6
|
+
* - Auto-fix upsell section for Scan context
|
|
7
|
+
* - Deterministic output ordering
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
// Use ANSI codes directly (chalk v5 is ESM-only, this is CommonJS)
|
|
10
11
|
const ESC = '\x1b';
|
|
12
|
+
const { sortFindings } = require('./finding-sorter');
|
|
11
13
|
const chalk = {
|
|
12
14
|
reset: `${ESC}[0m`,
|
|
13
15
|
bold: `${ESC}[1m`,
|
|
@@ -99,6 +101,11 @@ function formatScanOutput(result, options = {}) {
|
|
|
99
101
|
let duration = 0;
|
|
100
102
|
let scannedFiles = 0;
|
|
101
103
|
|
|
104
|
+
// Sort findings deterministically for stable output
|
|
105
|
+
if (result.findings && Array.isArray(result.findings)) {
|
|
106
|
+
result.findings = sortFindings(result.findings);
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
// Helper function to calculate score from findings
|
|
103
110
|
function calculateScoreFromFindings(findings) {
|
|
104
111
|
if (!findings || findings.length === 0) return 100;
|
|
@@ -245,17 +252,33 @@ function formatScanOutput(result, options = {}) {
|
|
|
245
252
|
});
|
|
246
253
|
|
|
247
254
|
const categoryLabels = {
|
|
248
|
-
|
|
249
|
-
'MissingRoute': '
|
|
250
|
-
'
|
|
251
|
-
'
|
|
252
|
-
'
|
|
255
|
+
// AI Hallucination Categories (primary)
|
|
256
|
+
'MissingRoute': '🛤️ Routes',
|
|
257
|
+
'FakeSuccess': '👻 Fake',
|
|
258
|
+
'GhostAuth': '🔒 Auth',
|
|
259
|
+
'EnvContract': '🌍 Env',
|
|
260
|
+
'EnvGap': '🌍 Env',
|
|
261
|
+
'DeadUI': '💀 Dead UI',
|
|
262
|
+
'OwnerModeBypass': '🔐 Bypass',
|
|
263
|
+
'OptimisticNoRollback': '↩️ Rollback',
|
|
264
|
+
'SilentCatch': '🔇 Silent',
|
|
265
|
+
// Billing/Monetization
|
|
266
|
+
'Billing': '💰 Billing',
|
|
267
|
+
'StripeWebhook': '💰 Webhook',
|
|
268
|
+
'PaidSurface': '💰 Paid',
|
|
269
|
+
'Entitlements': '💳 Entitle',
|
|
270
|
+
// Security
|
|
271
|
+
'Security': '🛡️ Security',
|
|
253
272
|
'Secrets': '🔑 Secrets',
|
|
273
|
+
'SECRET': '🔑 Secrets',
|
|
274
|
+
// Quality
|
|
275
|
+
'MockData': '🎭 Mocks',
|
|
276
|
+
'MOCK': '🎭 Mocks',
|
|
254
277
|
'ConsoleLog': '📝 Logs',
|
|
255
278
|
'TodoFixme': '📋 TODOs',
|
|
256
279
|
'CodeQuality': '✨ Quality',
|
|
257
|
-
'Security': '⚡ Security',
|
|
258
280
|
'Performance': '⚡ Perf',
|
|
281
|
+
'ContractDrift': '📜 Drift',
|
|
259
282
|
};
|
|
260
283
|
|
|
261
284
|
const summaryItems = Object.entries(categoryCounts)
|
|
@@ -382,6 +405,25 @@ function formatScanOutput(result, options = {}) {
|
|
|
382
405
|
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
383
406
|
lines.push(`${chalk.gray}${BOX.bottomLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.bottomRight}${chalk.reset}`);
|
|
384
407
|
|
|
408
|
+
// DASHBOARD LINK (outside frame)
|
|
409
|
+
const runId = result.runId || result.verdict?.runId;
|
|
410
|
+
if (runId) {
|
|
411
|
+
lines.push('');
|
|
412
|
+
lines.push(` ${chalk.dim}🔗 Dashboard:${chalk.reset} https://app.vibecheckai.dev/runs/${runId}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// NEXT BEST ACTION (outside frame)
|
|
416
|
+
try {
|
|
417
|
+
const { formatNextActionOneLine, getNextActionForCommand } = require('./next-action');
|
|
418
|
+
const tier = options.tier || 'free';
|
|
419
|
+
const verdict = typeof result.verdict === 'object' ? result.verdict.verdict : result.verdict;
|
|
420
|
+
const nextAction = getNextActionForCommand('scan', { verdict, runId, findings }, tier);
|
|
421
|
+
lines.push('');
|
|
422
|
+
lines.push(` ${formatNextActionOneLine(nextAction)}`);
|
|
423
|
+
} catch (e) {
|
|
424
|
+
// Next action module might not be available, skip gracefully
|
|
425
|
+
}
|
|
426
|
+
|
|
385
427
|
return lines.join('\n');
|
|
386
428
|
}
|
|
387
429
|
|
|
@@ -461,36 +503,56 @@ function printError(error, context = '') {
|
|
|
461
503
|
* Format SARIF output (placeholder - full implementation in report-engine.js)
|
|
462
504
|
*/
|
|
463
505
|
function formatSARIF(findings, options = {}) {
|
|
464
|
-
|
|
465
|
-
|
|
506
|
+
const path = require('path');
|
|
507
|
+
// Sort findings deterministically for stable output
|
|
508
|
+
const sortedFindings = sortFindings(findings || []);
|
|
509
|
+
|
|
510
|
+
const projectPath = options.projectPath || process.cwd();
|
|
511
|
+
|
|
512
|
+
// Valid SARIF 2.1.0 structure
|
|
513
|
+
const sarif = {
|
|
466
514
|
version: "2.1.0",
|
|
467
|
-
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema.json",
|
|
515
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
468
516
|
runs: [{
|
|
469
517
|
tool: {
|
|
470
518
|
driver: {
|
|
471
519
|
name: "vibecheck",
|
|
472
|
-
version: options.version || "1.0.0"
|
|
520
|
+
version: options.version || "1.0.0",
|
|
521
|
+
informationUri: "https://vibecheckai.dev"
|
|
473
522
|
}
|
|
474
523
|
},
|
|
475
|
-
results:
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
524
|
+
results: sortedFindings.map((f, i) => {
|
|
525
|
+
const filePath = f.file || f.path;
|
|
526
|
+
const relativePath = filePath && projectPath
|
|
527
|
+
? path.relative(projectPath, filePath).replace(/\\/g, '/')
|
|
528
|
+
: filePath || 'unknown';
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
ruleId: f.ruleId || f.id || f.category || f.type || `rule-${i}`,
|
|
532
|
+
message: {
|
|
533
|
+
text: f.message || f.title || f.description || "Issue detected"
|
|
534
|
+
},
|
|
535
|
+
level: f.severity === 'critical' || f.severity === 'BLOCK' ? 'error' :
|
|
536
|
+
f.severity === 'warning' || f.severity === 'WARN' ? 'warning' : 'note',
|
|
537
|
+
locations: filePath ? [{
|
|
538
|
+
physicalLocation: {
|
|
539
|
+
artifactLocation: {
|
|
540
|
+
uri: relativePath
|
|
541
|
+
},
|
|
542
|
+
region: f.line ? {
|
|
543
|
+
startLine: f.line,
|
|
544
|
+
startColumn: f.column || 1,
|
|
545
|
+
endLine: f.endLine || f.line,
|
|
546
|
+
endColumn: f.endColumn || f.column || 1
|
|
547
|
+
} : undefined
|
|
548
|
+
}
|
|
549
|
+
}] : []
|
|
550
|
+
};
|
|
551
|
+
})
|
|
492
552
|
}]
|
|
493
|
-
}
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
return JSON.stringify(sarif, null, 2);
|
|
494
556
|
}
|
|
495
557
|
|
|
496
558
|
// Placeholder functions for compatibility
|
|
@@ -510,52 +572,8 @@ function renderLayers(layers) {
|
|
|
510
572
|
return ''; // Implement if needed
|
|
511
573
|
}
|
|
512
574
|
|
|
513
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
514
|
-
// ENHANCED OUTPUT (with charts and Pro upselling)
|
|
515
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
516
|
-
|
|
517
|
-
let enhancedOutput;
|
|
518
|
-
try {
|
|
519
|
-
enhancedOutput = require('./scan-output-enhanced');
|
|
520
|
-
} catch (e) {
|
|
521
|
-
enhancedOutput = null;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Format scan output with enhanced visuals (charts, upsell)
|
|
526
|
-
* Falls back to standard output if enhanced module not available
|
|
527
|
-
*/
|
|
528
|
-
function formatScanOutputEnhanced(result, options = {}) {
|
|
529
|
-
if (enhancedOutput && !options.legacy) {
|
|
530
|
-
enhancedOutput.renderEnhancedScanOutput(result, options);
|
|
531
|
-
return ''; // Output is printed directly
|
|
532
|
-
}
|
|
533
|
-
return formatScanOutput(result, options);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Get Pro upsell prompt for current context
|
|
538
|
-
*/
|
|
539
|
-
function getProUpsellPrompt(context, data = {}) {
|
|
540
|
-
if (enhancedOutput) {
|
|
541
|
-
return enhancedOutput.getProPrompt(context, data);
|
|
542
|
-
}
|
|
543
|
-
return null;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
/**
|
|
547
|
-
* Render inline Pro upsell
|
|
548
|
-
*/
|
|
549
|
-
function renderProUpsell(context, data = {}) {
|
|
550
|
-
if (enhancedOutput) {
|
|
551
|
-
return enhancedOutput.renderInlineProPrompt(context, data);
|
|
552
|
-
}
|
|
553
|
-
return '';
|
|
554
|
-
}
|
|
555
|
-
|
|
556
575
|
module.exports = {
|
|
557
576
|
formatScanOutput,
|
|
558
|
-
formatScanOutputEnhanced,
|
|
559
577
|
calculateScore,
|
|
560
578
|
getExitCode,
|
|
561
579
|
printError,
|
|
@@ -565,7 +583,4 @@ module.exports = {
|
|
|
565
583
|
renderBreakdown,
|
|
566
584
|
renderBlockers,
|
|
567
585
|
renderLayers,
|
|
568
|
-
// Pro upsell
|
|
569
|
-
getProUpsellPrompt,
|
|
570
|
-
renderProUpsell,
|
|
571
586
|
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan Runner - Timeout and Cancellation Support
|
|
3
|
+
*
|
|
4
|
+
* Provides timeout and cancellation capabilities for scan execution.
|
|
5
|
+
* Prevents runaway scans from hanging indefinitely.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} ScanOptions
|
|
12
|
+
* @property {number} [timeout] - Timeout in milliseconds (default: 5 minutes)
|
|
13
|
+
* @property {AbortSignal} [signal] - AbortSignal for cancellation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} ScanResult
|
|
18
|
+
* @property {'completed'|'cancelled'|'failed'} status
|
|
19
|
+
* @property {Array} findings
|
|
20
|
+
* @property {string} [error]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
class ScanRunner extends EventEmitter {
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
this.aborted = false;
|
|
27
|
+
this.timeoutId = null;
|
|
28
|
+
}
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.aborted = false;
|
|
32
|
+
this.timeoutId = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run a scan with timeout and cancellation support
|
|
37
|
+
* @param {string} projectPath - Path to project to scan
|
|
38
|
+
* @param {ScanOptions} options - Scan options
|
|
39
|
+
* @returns {Promise<ScanResult>}
|
|
40
|
+
*/
|
|
41
|
+
async run(projectPath, options = {}) {
|
|
42
|
+
const timeout = options.timeout || 5 * 60 * 1000; // Default 5 minutes
|
|
43
|
+
|
|
44
|
+
// Set up abort handling
|
|
45
|
+
if (options.signal) {
|
|
46
|
+
options.signal.addEventListener('abort', () => {
|
|
47
|
+
this.abort('Scan aborted via signal');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Set up timeout
|
|
52
|
+
this.timeoutId = setTimeout(() => {
|
|
53
|
+
this.abort('Scan timed out');
|
|
54
|
+
}, timeout);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
this.emit('start', { projectPath, timeout });
|
|
58
|
+
|
|
59
|
+
const result = await this.executeAnalyzers(projectPath);
|
|
60
|
+
|
|
61
|
+
if (this.aborted) {
|
|
62
|
+
return { status: 'cancelled', findings: [] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (this.aborted) {
|
|
68
|
+
return { status: 'cancelled', findings: [], error: error.message };
|
|
69
|
+
}
|
|
70
|
+
return { status: 'failed', findings: [], error: error.message };
|
|
71
|
+
} finally {
|
|
72
|
+
if (this.timeoutId) {
|
|
73
|
+
clearTimeout(this.timeoutId);
|
|
74
|
+
this.timeoutId = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Abort the current scan
|
|
81
|
+
* @param {string} reason - Reason for abortion
|
|
82
|
+
*/
|
|
83
|
+
abort(reason = 'Scan aborted') {
|
|
84
|
+
this.aborted = true;
|
|
85
|
+
this.emit('abort', { reason });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Execute analyzers with abort checks between each
|
|
90
|
+
* @private
|
|
91
|
+
* @param {string} projectPath - Path to project
|
|
92
|
+
* @returns {Promise<ScanResult>}
|
|
93
|
+
*/
|
|
94
|
+
async executeAnalyzers(projectPath) {
|
|
95
|
+
// Import analysis core dynamically to avoid circular dependencies
|
|
96
|
+
const { runAnalysis } = require('./analysis-core');
|
|
97
|
+
|
|
98
|
+
// Check abort before starting
|
|
99
|
+
if (this.aborted) {
|
|
100
|
+
return { status: 'cancelled', findings: [] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Run analysis with progress tracking
|
|
105
|
+
this.emit('progress', { phase: 'analysis', status: 'running' });
|
|
106
|
+
|
|
107
|
+
const result = await runAnalysis({
|
|
108
|
+
repoRoot: projectPath,
|
|
109
|
+
extended: true,
|
|
110
|
+
noWrite: false
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Check abort after analysis
|
|
114
|
+
if (this.aborted) {
|
|
115
|
+
return { status: 'cancelled', findings: result.findings || [] };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.emit('progress', { phase: 'analysis', status: 'complete', count: result.findings?.length || 0 });
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
status: 'completed',
|
|
122
|
+
findings: result.findings || [],
|
|
123
|
+
verdict: result.verdict,
|
|
124
|
+
report: result.report
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (this.aborted) {
|
|
128
|
+
return { status: 'cancelled', findings: [], error: error.message };
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { ScanRunner };
|