@vibecheckai/cli 2.8.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/bin/_deprecations.js +35 -0
- package/bin/_router.js +46 -0
- package/bin/cli-hygiene.js +241 -0
- package/bin/guardrail.js +834 -0
- package/bin/runners/cli-utils.js +1070 -0
- package/bin/runners/context/ai-task-decomposer.js +337 -0
- package/bin/runners/context/analyzer.js +462 -0
- package/bin/runners/context/api-contracts.js +427 -0
- package/bin/runners/context/context-diff.js +342 -0
- package/bin/runners/context/context-pruner.js +291 -0
- package/bin/runners/context/dependency-graph.js +414 -0
- package/bin/runners/context/generators/claude.js +107 -0
- package/bin/runners/context/generators/codex.js +108 -0
- package/bin/runners/context/generators/copilot.js +119 -0
- package/bin/runners/context/generators/cursor.js +514 -0
- package/bin/runners/context/generators/mcp.js +151 -0
- package/bin/runners/context/generators/windsurf.js +180 -0
- package/bin/runners/context/git-context.js +302 -0
- package/bin/runners/context/index.js +1042 -0
- package/bin/runners/context/insights.js +173 -0
- package/bin/runners/context/mcp-server/generate-rules.js +337 -0
- package/bin/runners/context/mcp-server/index.js +1176 -0
- package/bin/runners/context/mcp-server/package.json +24 -0
- package/bin/runners/context/memory.js +200 -0
- package/bin/runners/context/monorepo.js +215 -0
- package/bin/runners/context/multi-repo-federation.js +404 -0
- package/bin/runners/context/patterns.js +253 -0
- package/bin/runners/context/proof-context.js +972 -0
- package/bin/runners/context/security-scanner.js +303 -0
- package/bin/runners/context/semantic-search.js +350 -0
- package/bin/runners/context/shared.js +264 -0
- package/bin/runners/context/team-conventions.js +310 -0
- package/bin/runners/lib/ai-bridge.js +416 -0
- package/bin/runners/lib/analysis-core.js +271 -0
- package/bin/runners/lib/analyzers.js +541 -0
- package/bin/runners/lib/audit-bridge.js +391 -0
- package/bin/runners/lib/auth-truth.js +193 -0
- package/bin/runners/lib/auth.js +215 -0
- package/bin/runners/lib/backup.js +62 -0
- package/bin/runners/lib/billing.js +107 -0
- package/bin/runners/lib/claims.js +118 -0
- package/bin/runners/lib/cli-ui.js +540 -0
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +165 -0
- package/bin/runners/lib/contracts/auth-contract.js +194 -0
- package/bin/runners/lib/contracts/env-contract.js +178 -0
- package/bin/runners/lib/contracts/external-contract.js +198 -0
- package/bin/runners/lib/contracts/guard.js +168 -0
- package/bin/runners/lib/contracts/index.js +89 -0
- package/bin/runners/lib/contracts/plan-validator.js +311 -0
- package/bin/runners/lib/contracts/route-contract.js +192 -0
- package/bin/runners/lib/detect.js +89 -0
- package/bin/runners/lib/doctor/autofix.js +254 -0
- package/bin/runners/lib/doctor/index.js +37 -0
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
- package/bin/runners/lib/doctor/modules/index.js +46 -0
- package/bin/runners/lib/doctor/modules/network.js +250 -0
- package/bin/runners/lib/doctor/modules/project.js +312 -0
- package/bin/runners/lib/doctor/modules/runtime.js +224 -0
- package/bin/runners/lib/doctor/modules/security.js +348 -0
- package/bin/runners/lib/doctor/modules/system.js +213 -0
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
- package/bin/runners/lib/doctor/reporter.js +262 -0
- package/bin/runners/lib/doctor/service.js +262 -0
- package/bin/runners/lib/doctor/types.js +113 -0
- package/bin/runners/lib/doctor/ui.js +263 -0
- package/bin/runners/lib/doctor-enhanced.js +233 -0
- package/bin/runners/lib/doctor-v2.js +608 -0
- package/bin/runners/lib/enforcement.js +72 -0
- package/bin/runners/lib/enterprise-detect.js +603 -0
- package/bin/runners/lib/enterprise-init.js +942 -0
- package/bin/runners/lib/entitlements-v2.js +381 -0
- package/bin/runners/lib/entitlements.generated.js +0 -0
- package/bin/runners/lib/entitlements.js +332 -0
- package/bin/runners/lib/env-template.js +66 -0
- package/bin/runners/lib/env.js +189 -0
- package/bin/runners/lib/error-handler.js +320 -0
- package/bin/runners/lib/firewall-prompt.js +50 -0
- package/bin/runners/lib/graph/graph-builder.js +265 -0
- package/bin/runners/lib/graph/html-renderer.js +413 -0
- package/bin/runners/lib/graph/index.js +32 -0
- package/bin/runners/lib/graph/runtime-collector.js +215 -0
- package/bin/runners/lib/graph/static-extractor.js +518 -0
- package/bin/runners/lib/init-wizard.js +308 -0
- package/bin/runners/lib/json-output.js +76 -0
- package/bin/runners/lib/llm.js +75 -0
- package/bin/runners/lib/meter.js +61 -0
- package/bin/runners/lib/missions/evidence.js +126 -0
- package/bin/runners/lib/missions/plan.js +69 -0
- package/bin/runners/lib/missions/templates.js +147 -0
- package/bin/runners/lib/patch.js +40 -0
- package/bin/runners/lib/permissions/auth-model.js +213 -0
- package/bin/runners/lib/permissions/idor-prover.js +205 -0
- package/bin/runners/lib/permissions/index.js +45 -0
- package/bin/runners/lib/permissions/matrix-builder.js +198 -0
- package/bin/runners/lib/pkgjson.js +28 -0
- package/bin/runners/lib/preflight.js +142 -0
- package/bin/runners/lib/reality-findings.js +84 -0
- package/bin/runners/lib/redact.js +29 -0
- package/bin/runners/lib/replay/capsule-manager.js +154 -0
- package/bin/runners/lib/replay/index.js +263 -0
- package/bin/runners/lib/replay/player.js +348 -0
- package/bin/runners/lib/replay/recorder.js +331 -0
- package/bin/runners/lib/report-engine.js +447 -0
- package/bin/runners/lib/report-html.js +1117 -0
- package/bin/runners/lib/report-templates.js +964 -0
- package/bin/runners/lib/route-detection.js +1140 -0
- package/bin/runners/lib/route-truth.js +477 -0
- package/bin/runners/lib/sandbox/index.js +59 -0
- package/bin/runners/lib/sandbox/proof-chain.js +399 -0
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -0
- package/bin/runners/lib/sandbox/worktree.js +174 -0
- package/bin/runners/lib/scan-cache.js +330 -0
- package/bin/runners/lib/scan-output-schema.js +344 -0
- package/bin/runners/lib/score-history.js +282 -0
- package/bin/runners/lib/security-bridge.js +249 -0
- package/bin/runners/lib/server-usage.js +513 -0
- package/bin/runners/lib/share-pack.js +239 -0
- package/bin/runners/lib/snippets.js +67 -0
- package/bin/runners/lib/truth.js +667 -0
- package/bin/runners/lib/unified-output.js +189 -0
- package/bin/runners/lib/validate-patch.js +156 -0
- package/bin/runners/lib/verification.js +345 -0
- package/bin/runners/reality/engine.js +917 -0
- package/bin/runners/reality/flows.js +122 -0
- package/bin/runners/reality/report.js +378 -0
- package/bin/runners/reality/session.js +193 -0
- package/bin/runners/runAIAgent.js +2 -0
- package/bin/runners/runAudit.js +2 -0
- package/bin/runners/runAuth.js +106 -0
- package/bin/runners/runAutopilot.js +2 -0
- package/bin/runners/runBadge.js +2 -0
- package/bin/runners/runCertify.js +2 -0
- package/bin/runners/runClaimVerifier.js +483 -0
- package/bin/runners/runContext.js +56 -0
- package/bin/runners/runContextCompiler.js +385 -0
- package/bin/runners/runCtx.js +187 -0
- package/bin/runners/runCtxGuard.js +176 -0
- package/bin/runners/runCtxSync.js +116 -0
- package/bin/runners/runDashboard.js +10 -0
- package/bin/runners/runDoctor.js +245 -0
- package/bin/runners/runEnhancedShip.js +2 -0
- package/bin/runners/runFix.js +735 -0
- package/bin/runners/runFixPacks.js +2 -0
- package/bin/runners/runGate.js +17 -0
- package/bin/runners/runGraph.js +283 -0
- package/bin/runners/runInit.js +260 -0
- package/bin/runners/runInitGha.js +101 -0
- package/bin/runners/runInstall.js +76 -0
- package/bin/runners/runInteractive.js +388 -0
- package/bin/runners/runLaunch.js +2 -0
- package/bin/runners/runMcp.js +19 -0
- package/bin/runners/runMdc.js +2 -0
- package/bin/runners/runMissionGenerator.js +282 -0
- package/bin/runners/runNaturalLanguage.js +3 -0
- package/bin/runners/runPR.js +96 -0
- package/bin/runners/runPermissions.js +290 -0
- package/bin/runners/runPromptFirewall.js +211 -0
- package/bin/runners/runProof.js +2 -0
- package/bin/runners/runProve.js +392 -0
- package/bin/runners/runReality.js +489 -0
- package/bin/runners/runRealitySniff.js +2 -0
- package/bin/runners/runReplay.js +469 -0
- package/bin/runners/runReport.js +478 -0
- package/bin/runners/runScan.js +835 -0
- package/bin/runners/runShare.js +34 -0
- package/bin/runners/runShip.js +1062 -0
- package/bin/runners/runStatus.js +136 -0
- package/bin/runners/runTruthpack.js +634 -0
- package/bin/runners/runUpgrade.js +2 -0
- package/bin/runners/runValidate.js +2 -0
- package/bin/runners/runVerifyAgentOutput.js +2 -0
- package/bin/runners/runWatch.js +230 -0
- package/bin/runners/utils.js +360 -0
- package/bin/scan.js +612 -0
- package/bin/vibecheck.js +834 -0
- package/package.json +11 -11
- package/dist/autopatch/verified-autopatch.d.ts +0 -111
- package/dist/autopatch/verified-autopatch.d.ts.map +0 -1
- package/dist/autopatch/verified-autopatch.js +0 -503
- package/dist/autopatch/verified-autopatch.js.map +0 -1
- package/dist/bundles/index.js +0 -8
- package/dist/bundles/vibecheck-core.js +0 -25799
- package/dist/bundles/vibecheck-security.js +0 -208693
- package/dist/bundles/vibecheck-ship.js +0 -2318
- package/dist/commands/baseline.d.ts +0 -7
- package/dist/commands/baseline.d.ts.map +0 -1
- package/dist/commands/baseline.js +0 -79
- package/dist/commands/baseline.js.map +0 -1
- package/dist/commands/cache.d.ts +0 -13
- package/dist/commands/cache.d.ts.map +0 -1
- package/dist/commands/cache.js +0 -165
- package/dist/commands/cache.js.map +0 -1
- package/dist/commands/checkpoint.d.ts +0 -8
- package/dist/commands/checkpoint.d.ts.map +0 -1
- package/dist/commands/checkpoint.js +0 -35
- package/dist/commands/checkpoint.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -17
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -226
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/evidence.d.ts +0 -45
- package/dist/commands/evidence.d.ts.map +0 -1
- package/dist/commands/evidence.js +0 -197
- package/dist/commands/evidence.js.map +0 -1
- package/dist/commands/explain.d.ts +0 -8
- package/dist/commands/explain.d.ts.map +0 -1
- package/dist/commands/explain.js +0 -52
- package/dist/commands/explain.js.map +0 -1
- package/dist/commands/fix-consolidated.d.ts +0 -19
- package/dist/commands/fix-consolidated.d.ts.map +0 -1
- package/dist/commands/fix-consolidated.js +0 -165
- package/dist/commands/fix-consolidated.js.map +0 -1
- package/dist/commands/index.d.ts +0 -8
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -15
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -125
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/launcher.d.ts +0 -10
- package/dist/commands/launcher.d.ts.map +0 -1
- package/dist/commands/launcher.js +0 -174
- package/dist/commands/launcher.js.map +0 -1
- package/dist/commands/on.d.ts +0 -8
- package/dist/commands/on.d.ts.map +0 -1
- package/dist/commands/on.js +0 -123
- package/dist/commands/on.js.map +0 -1
- package/dist/commands/replay.d.ts +0 -8
- package/dist/commands/replay.d.ts.map +0 -1
- package/dist/commands/replay.js +0 -52
- package/dist/commands/replay.js.map +0 -1
- package/dist/commands/scan-consolidated.d.ts +0 -61
- package/dist/commands/scan-consolidated.d.ts.map +0 -1
- package/dist/commands/scan-consolidated.js +0 -243
- package/dist/commands/scan-consolidated.js.map +0 -1
- package/dist/commands/scan-secrets.d.ts +0 -47
- package/dist/commands/scan-secrets.d.ts.map +0 -1
- package/dist/commands/scan-secrets.js +0 -225
- package/dist/commands/scan-secrets.js.map +0 -1
- package/dist/commands/scan-vulnerabilities-enhanced.d.ts +0 -41
- package/dist/commands/scan-vulnerabilities-enhanced.d.ts.map +0 -1
- package/dist/commands/scan-vulnerabilities-enhanced.js +0 -368
- package/dist/commands/scan-vulnerabilities-enhanced.js.map +0 -1
- package/dist/commands/scan-vulnerabilities-osv.d.ts +0 -58
- package/dist/commands/scan-vulnerabilities-osv.d.ts.map +0 -1
- package/dist/commands/scan-vulnerabilities-osv.js +0 -722
- package/dist/commands/scan-vulnerabilities-osv.js.map +0 -1
- package/dist/commands/scan-vulnerabilities.d.ts +0 -32
- package/dist/commands/scan-vulnerabilities.d.ts.map +0 -1
- package/dist/commands/scan-vulnerabilities.js +0 -283
- package/dist/commands/scan-vulnerabilities.js.map +0 -1
- package/dist/commands/secrets-allowlist.d.ts +0 -7
- package/dist/commands/secrets-allowlist.d.ts.map +0 -1
- package/dist/commands/secrets-allowlist.js +0 -85
- package/dist/commands/secrets-allowlist.js.map +0 -1
- package/dist/commands/ship-consolidated.d.ts +0 -58
- package/dist/commands/ship-consolidated.d.ts.map +0 -1
- package/dist/commands/ship-consolidated.js +0 -515
- package/dist/commands/ship-consolidated.js.map +0 -1
- package/dist/commands/stats.d.ts +0 -8
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js +0 -134
- package/dist/commands/stats.js.map +0 -1
- package/dist/commands/upgrade.d.ts +0 -8
- package/dist/commands/upgrade.d.ts.map +0 -1
- package/dist/commands/upgrade.js +0 -30
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/fix/applicator.d.ts +0 -44
- package/dist/fix/applicator.d.ts.map +0 -1
- package/dist/fix/applicator.js +0 -144
- package/dist/fix/applicator.js.map +0 -1
- package/dist/fix/backup.d.ts +0 -38
- package/dist/fix/backup.d.ts.map +0 -1
- package/dist/fix/backup.js +0 -154
- package/dist/fix/backup.js.map +0 -1
- package/dist/fix/engine.d.ts +0 -55
- package/dist/fix/engine.d.ts.map +0 -1
- package/dist/fix/engine.js +0 -285
- package/dist/fix/engine.js.map +0 -1
- package/dist/fix/index.d.ts +0 -5
- package/dist/fix/index.d.ts.map +0 -1
- package/dist/fix/index.js +0 -12
- package/dist/fix/index.js.map +0 -1
- package/dist/fix/interactive.d.ts +0 -22
- package/dist/fix/interactive.d.ts.map +0 -1
- package/dist/fix/interactive.js +0 -172
- package/dist/fix/interactive.js.map +0 -1
- package/dist/formatters/index.d.ts +0 -6
- package/dist/formatters/index.d.ts.map +0 -1
- package/dist/formatters/index.js +0 -11
- package/dist/formatters/index.js.map +0 -1
- package/dist/formatters/sarif-enhanced.d.ts +0 -78
- package/dist/formatters/sarif-enhanced.d.ts.map +0 -1
- package/dist/formatters/sarif-enhanced.js +0 -144
- package/dist/formatters/sarif-enhanced.js.map +0 -1
- package/dist/formatters/sarif-v2.d.ts +0 -121
- package/dist/formatters/sarif-v2.d.ts.map +0 -1
- package/dist/formatters/sarif-v2.js +0 -356
- package/dist/formatters/sarif-v2.js.map +0 -1
- package/dist/formatters/sarif.d.ts +0 -72
- package/dist/formatters/sarif.d.ts.map +0 -1
- package/dist/formatters/sarif.js +0 -146
- package/dist/formatters/sarif.js.map +0 -1
- package/dist/index.d.ts +0 -61
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4388
- package/dist/index.js.map +0 -1
- package/dist/init/ci-generator.d.ts +0 -18
- package/dist/init/ci-generator.d.ts.map +0 -1
- package/dist/init/ci-generator.js +0 -317
- package/dist/init/ci-generator.js.map +0 -1
- package/dist/init/detect-framework.d.ts +0 -15
- package/dist/init/detect-framework.d.ts.map +0 -1
- package/dist/init/detect-framework.js +0 -301
- package/dist/init/detect-framework.js.map +0 -1
- package/dist/init/hooks-installer.d.ts +0 -22
- package/dist/init/hooks-installer.d.ts.map +0 -1
- package/dist/init/hooks-installer.js +0 -310
- package/dist/init/hooks-installer.js.map +0 -1
- package/dist/init/index.d.ts +0 -8
- package/dist/init/index.d.ts.map +0 -1
- package/dist/init/index.js +0 -22
- package/dist/init/index.js.map +0 -1
- package/dist/init/templates.d.ts +0 -402
- package/dist/init/templates.d.ts.map +0 -1
- package/dist/init/templates.js +0 -240
- package/dist/init/templates.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -12
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -42
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/telemetry.d.ts +0 -40
- package/dist/mcp/telemetry.d.ts.map +0 -1
- package/dist/mcp/telemetry.js +0 -98
- package/dist/mcp/telemetry.js.map +0 -1
- package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts +0 -32
- package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts.map +0 -1
- package/dist/reality/no-dead-buttons/button-sweep-generator.js +0 -236
- package/dist/reality/no-dead-buttons/button-sweep-generator.js.map +0 -1
- package/dist/reality/no-dead-buttons/index.d.ts +0 -11
- package/dist/reality/no-dead-buttons/index.d.ts.map +0 -1
- package/dist/reality/no-dead-buttons/index.js +0 -18
- package/dist/reality/no-dead-buttons/index.js.map +0 -1
- package/dist/reality/no-dead-buttons/static-scanner.d.ts +0 -34
- package/dist/reality/no-dead-buttons/static-scanner.d.ts.map +0 -1
- package/dist/reality/no-dead-buttons/static-scanner.js +0 -230
- package/dist/reality/no-dead-buttons/static-scanner.js.map +0 -1
- package/dist/reality/reality-graph.d.ts +0 -192
- package/dist/reality/reality-graph.d.ts.map +0 -1
- package/dist/reality/reality-graph.js +0 -600
- package/dist/reality/reality-graph.js.map +0 -1
- package/dist/reality/reality-runner.d.ts +0 -89
- package/dist/reality/reality-runner.d.ts.map +0 -1
- package/dist/reality/reality-runner.js +0 -540
- package/dist/reality/reality-runner.js.map +0 -1
- package/dist/reality/receipt-generator.d.ts +0 -152
- package/dist/reality/receipt-generator.d.ts.map +0 -1
- package/dist/reality/receipt-generator.js +0 -495
- package/dist/reality/receipt-generator.js.map +0 -1
- package/dist/reality/runtime-tracer.d.ts +0 -75
- package/dist/reality/runtime-tracer.d.ts.map +0 -1
- package/dist/reality/runtime-tracer.js +0 -109
- package/dist/reality/runtime-tracer.js.map +0 -1
- package/dist/runtime/auth-utils.d.ts +0 -43
- package/dist/runtime/auth-utils.d.ts.map +0 -1
- package/dist/runtime/auth-utils.js +0 -130
- package/dist/runtime/auth-utils.js.map +0 -1
- package/dist/runtime/client.d.ts +0 -74
- package/dist/runtime/client.d.ts.map +0 -1
- package/dist/runtime/client.js +0 -222
- package/dist/runtime/client.js.map +0 -1
- package/dist/runtime/creds.d.ts +0 -48
- package/dist/runtime/creds.d.ts.map +0 -1
- package/dist/runtime/creds.js +0 -245
- package/dist/runtime/creds.js.map +0 -1
- package/dist/runtime/exit-codes.d.ts +0 -49
- package/dist/runtime/exit-codes.d.ts.map +0 -1
- package/dist/runtime/exit-codes.js +0 -93
- package/dist/runtime/exit-codes.js.map +0 -1
- package/dist/runtime/index.d.ts +0 -9
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js +0 -25
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/json-output.d.ts +0 -42
- package/dist/runtime/json-output.d.ts.map +0 -1
- package/dist/runtime/json-output.js +0 -59
- package/dist/runtime/json-output.js.map +0 -1
- package/dist/runtime/semver.d.ts +0 -37
- package/dist/runtime/semver.d.ts.map +0 -1
- package/dist/runtime/semver.js +0 -110
- package/dist/runtime/semver.js.map +0 -1
- package/dist/scan/dead-ui-detector.d.ts +0 -48
- package/dist/scan/dead-ui-detector.d.ts.map +0 -1
- package/dist/scan/dead-ui-detector.js +0 -170
- package/dist/scan/dead-ui-detector.js.map +0 -1
- package/dist/scan/playwright-sweep.d.ts +0 -40
- package/dist/scan/playwright-sweep.d.ts.map +0 -1
- package/dist/scan/playwright-sweep.js +0 -216
- package/dist/scan/playwright-sweep.js.map +0 -1
- package/dist/scan/proof-bundle.d.ts +0 -25
- package/dist/scan/proof-bundle.d.ts.map +0 -1
- package/dist/scan/proof-bundle.js +0 -203
- package/dist/scan/proof-bundle.js.map +0 -1
- package/dist/scan/proof-graph.d.ts +0 -59
- package/dist/scan/proof-graph.d.ts.map +0 -1
- package/dist/scan/proof-graph.js +0 -64
- package/dist/scan/proof-graph.js.map +0 -1
- package/dist/scan/reality-sniff.d.ts +0 -56
- package/dist/scan/reality-sniff.d.ts.map +0 -1
- package/dist/scan/reality-sniff.js +0 -200
- package/dist/scan/reality-sniff.js.map +0 -1
- package/dist/scan/structural-verifier.d.ts +0 -20
- package/dist/scan/structural-verifier.d.ts.map +0 -1
- package/dist/scan/structural-verifier.js +0 -112
- package/dist/scan/structural-verifier.js.map +0 -1
- package/dist/scan/verification-engine.d.ts +0 -47
- package/dist/scan/verification-engine.d.ts.map +0 -1
- package/dist/scan/verification-engine.js +0 -141
- package/dist/scan/verification-engine.js.map +0 -1
- package/dist/scanner/baseline.d.ts +0 -52
- package/dist/scanner/baseline.d.ts.map +0 -1
- package/dist/scanner/baseline.js +0 -85
- package/dist/scanner/baseline.js.map +0 -1
- package/dist/scanner/incremental.d.ts +0 -30
- package/dist/scanner/incremental.d.ts.map +0 -1
- package/dist/scanner/incremental.js +0 -82
- package/dist/scanner/incremental.js.map +0 -1
- package/dist/scanner/parallel.d.ts +0 -43
- package/dist/scanner/parallel.d.ts.map +0 -1
- package/dist/scanner/parallel.js +0 -99
- package/dist/scanner/parallel.js.map +0 -1
- package/dist/standalone.d.ts +0 -1
- package/dist/standalone.d.ts.map +0 -1
- package/dist/standalone.js +0 -1
- package/dist/standalone.js.map +0 -1
- package/dist/truth-pack/index.d.ts +0 -102
- package/dist/truth-pack/index.d.ts.map +0 -1
- package/dist/truth-pack/index.js +0 -694
- package/dist/truth-pack/index.js.map +0 -1
- package/dist/ui/frame.d.ts +0 -68
- package/dist/ui/frame.d.ts.map +0 -1
- package/dist/ui/frame.js +0 -165
- package/dist/ui/frame.js.map +0 -1
- package/dist/ui/index.d.ts +0 -5
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/ui/index.js +0 -16
- package/dist/ui/index.js.map +0 -1
- package/dist/ui.d.ts +0 -36
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js +0 -45
- package/dist/ui.js.map +0 -1
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck Scan - Route Integrity & Code Analysis
|
|
3
|
+
*
|
|
4
|
+
* The ultimate scanner combining:
|
|
5
|
+
* - Route integrity (dead links, orphan routes, coverage)
|
|
6
|
+
* - Security analysis (secrets, auth, vulnerabilities)
|
|
7
|
+
* - Code quality (mocks, placeholders, hygiene)
|
|
8
|
+
*
|
|
9
|
+
* Modes:
|
|
10
|
+
* - vibecheck scan: Layer 1 (AST) - Fast static analysis
|
|
11
|
+
* - vibecheck scan --truth: Layer 1+2 (+ build manifests) - CI/ship
|
|
12
|
+
* - vibecheck scan --reality --url <url>: Layer 1+2+3 (+ Playwright) - Full proof
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const { withErrorHandling, createUserError } = require("./lib/error-handler");
|
|
18
|
+
const { enforceLimit, trackUsage } = require("./lib/entitlements");
|
|
19
|
+
const { emitScanStart, emitScanComplete } = require("./lib/audit-bridge");
|
|
20
|
+
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
22
|
+
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
24
|
+
|
|
25
|
+
const c = {
|
|
26
|
+
reset: '\x1b[0m',
|
|
27
|
+
bold: '\x1b[1m',
|
|
28
|
+
dim: '\x1b[2m',
|
|
29
|
+
italic: '\x1b[3m',
|
|
30
|
+
underline: '\x1b[4m',
|
|
31
|
+
blink: '\x1b[5m',
|
|
32
|
+
inverse: '\x1b[7m',
|
|
33
|
+
hidden: '\x1b[8m',
|
|
34
|
+
strike: '\x1b[9m',
|
|
35
|
+
// Colors
|
|
36
|
+
black: '\x1b[30m',
|
|
37
|
+
red: '\x1b[31m',
|
|
38
|
+
green: '\x1b[32m',
|
|
39
|
+
yellow: '\x1b[33m',
|
|
40
|
+
blue: '\x1b[34m',
|
|
41
|
+
magenta: '\x1b[35m',
|
|
42
|
+
cyan: '\x1b[36m',
|
|
43
|
+
white: '\x1b[37m',
|
|
44
|
+
// Bright colors
|
|
45
|
+
gray: '\x1b[90m',
|
|
46
|
+
brightRed: '\x1b[91m',
|
|
47
|
+
brightGreen: '\x1b[92m',
|
|
48
|
+
brightYellow: '\x1b[93m',
|
|
49
|
+
brightBlue: '\x1b[94m',
|
|
50
|
+
brightMagenta: '\x1b[95m',
|
|
51
|
+
brightCyan: '\x1b[96m',
|
|
52
|
+
brightWhite: '\x1b[97m',
|
|
53
|
+
// Background
|
|
54
|
+
bgBlack: '\x1b[40m',
|
|
55
|
+
bgRed: '\x1b[41m',
|
|
56
|
+
bgGreen: '\x1b[42m',
|
|
57
|
+
bgYellow: '\x1b[43m',
|
|
58
|
+
bgBlue: '\x1b[44m',
|
|
59
|
+
bgMagenta: '\x1b[45m',
|
|
60
|
+
bgCyan: '\x1b[46m',
|
|
61
|
+
bgWhite: '\x1b[47m',
|
|
62
|
+
bgBrightBlack: '\x1b[100m',
|
|
63
|
+
bgBrightRed: '\x1b[101m',
|
|
64
|
+
bgBrightGreen: '\x1b[102m',
|
|
65
|
+
bgBrightYellow: '\x1b[103m',
|
|
66
|
+
// Cursor
|
|
67
|
+
cursorUp: (n = 1) => `\x1b[${n}A`,
|
|
68
|
+
cursorDown: (n = 1) => `\x1b[${n}B`,
|
|
69
|
+
cursorRight: (n = 1) => `\x1b[${n}C`,
|
|
70
|
+
cursorLeft: (n = 1) => `\x1b[${n}D`,
|
|
71
|
+
clearLine: '\x1b[2K',
|
|
72
|
+
clearScreen: '\x1b[2J',
|
|
73
|
+
saveCursor: '\x1b[s',
|
|
74
|
+
restoreCursor: '\x1b[u',
|
|
75
|
+
hideCursor: '\x1b[?25l',
|
|
76
|
+
showCursor: '\x1b[?25h',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// 256-color support
|
|
80
|
+
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
81
|
+
const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
|
|
82
|
+
|
|
83
|
+
// Gradient colors for the banner
|
|
84
|
+
const gradientCyan = rgb(0, 255, 255);
|
|
85
|
+
const gradientBlue = rgb(100, 149, 237);
|
|
86
|
+
const gradientPurple = rgb(138, 43, 226);
|
|
87
|
+
const gradientPink = rgb(255, 105, 180);
|
|
88
|
+
const gradientOrange = rgb(255, 165, 0);
|
|
89
|
+
|
|
90
|
+
const BANNER = `
|
|
91
|
+
${rgb(0, 200, 255)} ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗ ██████╗ █████╗ ██╗██╗ ${c.reset}
|
|
92
|
+
${rgb(30, 180, 255)} ██╔════╝ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██║██║ ${c.reset}
|
|
93
|
+
${rgb(60, 160, 255)} ██║ ███╗██║ ██║███████║██████╔╝██║ ██║██████╔╝███████║██║██║ ${c.reset}
|
|
94
|
+
${rgb(90, 140, 255)} ██║ ██║██║ ██║██╔══██║██╔══██╗██║ ██║██╔══██╗██╔══██║██║██║ ${c.reset}
|
|
95
|
+
${rgb(120, 120, 255)} ╚██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║██║███████╗${c.reset}
|
|
96
|
+
${rgb(150, 100, 255)} ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝${c.reset}
|
|
97
|
+
|
|
98
|
+
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
99
|
+
${c.dim} │${c.reset} ${rgb(255, 255, 255)}${c.bold}Route Integrity${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Security${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Quality${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Ship with Confidence${c.reset} ${c.dim}│${c.reset}
|
|
100
|
+
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
104
|
+
// TERMINAL UTILITIES
|
|
105
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
106
|
+
|
|
107
|
+
const BOX_CHARS = {
|
|
108
|
+
topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
|
|
109
|
+
horizontal: '─', vertical: '│',
|
|
110
|
+
teeRight: '├', teeLeft: '┤', teeDown: '┬', teeUp: '┴',
|
|
111
|
+
cross: '┼',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
115
|
+
let spinnerIndex = 0;
|
|
116
|
+
let spinnerInterval = null;
|
|
117
|
+
|
|
118
|
+
function formatNumber(num) {
|
|
119
|
+
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function truncate(str, len) {
|
|
123
|
+
if (str.length <= len) return str;
|
|
124
|
+
return str.slice(0, len - 3) + '...';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function progressBar(percent, width = 30) {
|
|
128
|
+
const filled = Math.round((percent / 100) * width);
|
|
129
|
+
const empty = width - filled;
|
|
130
|
+
const filledColor = percent >= 80 ? rgb(0, 255, 100) : percent >= 50 ? rgb(255, 200, 0) : rgb(255, 80, 80);
|
|
131
|
+
return `${filledColor}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function startSpinner(message) {
|
|
135
|
+
process.stdout.write(c.hideCursor);
|
|
136
|
+
spinnerInterval = setInterval(() => {
|
|
137
|
+
process.stdout.write(`\r ${c.cyan}${SPINNER_FRAMES[spinnerIndex]}${c.reset} ${message} `);
|
|
138
|
+
spinnerIndex = (spinnerIndex + 1) % SPINNER_FRAMES.length;
|
|
139
|
+
}, 80);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function stopSpinner(message, success = true) {
|
|
143
|
+
if (spinnerInterval) {
|
|
144
|
+
clearInterval(spinnerInterval);
|
|
145
|
+
spinnerInterval = null;
|
|
146
|
+
}
|
|
147
|
+
const icon = success ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
|
|
148
|
+
process.stdout.write(`\r${c.clearLine} ${icon} ${message}\n`);
|
|
149
|
+
process.stdout.write(c.showCursor);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function printBanner() {
|
|
153
|
+
console.log(BANNER);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function printDivider(char = '─', color = c.dim) {
|
|
157
|
+
console.log(`${color} ${char.repeat(69)}${c.reset}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function printSection(title, icon = '◆') {
|
|
161
|
+
console.log();
|
|
162
|
+
console.log(` ${rgb(100, 200, 255)}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
|
|
163
|
+
printDivider();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
167
|
+
// SCORE DISPLAY
|
|
168
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
169
|
+
|
|
170
|
+
function getScoreColor(score) {
|
|
171
|
+
if (score >= 90) return rgb(0, 255, 100);
|
|
172
|
+
if (score >= 80) return rgb(100, 255, 100);
|
|
173
|
+
if (score >= 70) return rgb(200, 255, 0);
|
|
174
|
+
if (score >= 60) return rgb(255, 200, 0);
|
|
175
|
+
if (score >= 50) return rgb(255, 150, 0);
|
|
176
|
+
return rgb(255, 80, 80);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getGradeColor(grade) {
|
|
180
|
+
const colors = {
|
|
181
|
+
'A': rgb(0, 255, 100),
|
|
182
|
+
'B': rgb(100, 255, 100),
|
|
183
|
+
'C': rgb(255, 200, 0),
|
|
184
|
+
'D': rgb(255, 150, 0),
|
|
185
|
+
'F': rgb(255, 80, 80),
|
|
186
|
+
};
|
|
187
|
+
return colors[grade] || c.white;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function printScoreCard(score, grade, canShip) {
|
|
191
|
+
const scoreColor = getScoreColor(score);
|
|
192
|
+
const gradeColor = getGradeColor(grade);
|
|
193
|
+
|
|
194
|
+
console.log();
|
|
195
|
+
console.log(` ${c.dim}╭────────────────────────────────────────────────────────────────╮${c.reset}`);
|
|
196
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
197
|
+
|
|
198
|
+
const scoreStr = `${score}`;
|
|
199
|
+
const scorePadding = ' '.repeat(Math.max(0, 3 - scoreStr.length));
|
|
200
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}INTEGRITY SCORE${c.reset} ${scoreColor}${c.bold}${scorePadding}${scoreStr}${c.reset}${c.dim}/100${c.reset} ${c.dim}GRADE${c.reset} ${gradeColor}${c.bold}${grade}${c.reset} ${c.dim}│${c.reset}`);
|
|
201
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
202
|
+
console.log(` ${c.dim}│${c.reset} ${progressBar(score, 40)} ${c.dim}│${c.reset}`);
|
|
203
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
204
|
+
|
|
205
|
+
if (canShip) {
|
|
206
|
+
console.log(` ${c.dim}│${c.reset} ${bgRgb(0, 150, 80)}${c.bold} ✓ CLEAR TO SHIP ${c.reset} ${c.dim}│${c.reset}`);
|
|
207
|
+
} else {
|
|
208
|
+
console.log(` ${c.dim}│${c.reset} ${bgRgb(200, 50, 50)}${c.bold} ✗ NOT SHIP READY ${c.reset} ${c.dim}│${c.reset}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
212
|
+
console.log(` ${c.dim}╰────────────────────────────────────────────────────────────────╯${c.reset}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
216
|
+
// COVERAGE MAP VISUALIZATION
|
|
217
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
218
|
+
|
|
219
|
+
function printCoverageMap(coverageMap) {
|
|
220
|
+
printSection('NAVIGATION COVERAGE', '🗺️');
|
|
221
|
+
|
|
222
|
+
const pct = coverageMap.coveragePercent;
|
|
223
|
+
const color = pct >= 80 ? rgb(0, 255, 100) : pct >= 60 ? rgb(255, 200, 0) : rgb(255, 80, 80);
|
|
224
|
+
|
|
225
|
+
console.log();
|
|
226
|
+
console.log(` ${color}${c.bold}${pct}%${c.reset} ${c.dim}of shipped routes reachable from${c.reset} ${c.cyan}/${c.reset}`);
|
|
227
|
+
console.log(` ${progressBar(pct, 50)}`);
|
|
228
|
+
console.log();
|
|
229
|
+
console.log(` ${c.dim}Routes:${c.reset} ${coverageMap.reachableFromRoot}${c.dim}/${c.reset}${coverageMap.totalShippedRoutes} ${c.dim}reachable${c.reset}`);
|
|
230
|
+
|
|
231
|
+
if (coverageMap.isolatedClusters && coverageMap.isolatedClusters.length > 0) {
|
|
232
|
+
console.log();
|
|
233
|
+
console.log(` ${c.yellow}⚠${c.reset} ${c.dim}Isolated clusters:${c.reset}`);
|
|
234
|
+
for (const cluster of coverageMap.isolatedClusters.slice(0, 3)) {
|
|
235
|
+
const auth = cluster.requiresAuth ? ` ${c.dim}(auth)${c.reset}` : '';
|
|
236
|
+
console.log(` ${c.dim}├─${c.reset} ${c.bold}${cluster.name}${c.reset}${auth} ${c.dim}(${cluster.nodeIds.length} routes)${c.reset}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (coverageMap.unreachableRoutes && coverageMap.unreachableRoutes.length > 0) {
|
|
241
|
+
console.log();
|
|
242
|
+
console.log(` ${c.red}✗${c.reset} ${c.dim}Unreachable routes:${c.reset}`);
|
|
243
|
+
for (const route of coverageMap.unreachableRoutes.slice(0, 5)) {
|
|
244
|
+
console.log(` ${c.dim}├─${c.reset} ${c.red}${route}${c.reset}`);
|
|
245
|
+
}
|
|
246
|
+
if (coverageMap.unreachableRoutes.length > 5) {
|
|
247
|
+
console.log(` ${c.dim}└─ ... and ${coverageMap.unreachableRoutes.length - 5} more${c.reset}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
253
|
+
// BREAKDOWN DISPLAY
|
|
254
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
255
|
+
|
|
256
|
+
function printBreakdown(breakdown) {
|
|
257
|
+
printSection('BREAKDOWN', '📊');
|
|
258
|
+
console.log();
|
|
259
|
+
|
|
260
|
+
const items = [
|
|
261
|
+
{ key: 'deadLinks', label: 'Dead Links', icon: '🔗', color: rgb(255, 100, 100) },
|
|
262
|
+
{ key: 'orphanRoutes', label: 'Orphan Routes', icon: '👻', color: rgb(200, 150, 255) },
|
|
263
|
+
{ key: 'runtimeFailures', label: 'Runtime 404s', icon: '💥', color: rgb(255, 80, 80) },
|
|
264
|
+
{ key: 'unresolvedDynamic', label: 'Unresolved Dynamic', icon: '❓', color: rgb(255, 200, 100) },
|
|
265
|
+
{ key: 'placeholders', label: 'Placeholders', icon: '📝', color: rgb(255, 180, 100) },
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
for (const item of items) {
|
|
269
|
+
const data = breakdown[item.key] || { count: 0, penalty: 0 };
|
|
270
|
+
const status = data.count === 0 ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
|
|
271
|
+
const countColor = data.count === 0 ? c.green : item.color;
|
|
272
|
+
const countStr = String(data.count).padStart(3);
|
|
273
|
+
const penaltyStr = data.penalty > 0 ? `${c.dim}-${data.penalty} pts${c.reset}` : `${c.dim} ---${c.reset}`;
|
|
274
|
+
|
|
275
|
+
console.log(` ${status} ${item.icon} ${item.label.padEnd(22)} ${countColor}${c.bold}${countStr}${c.reset} ${penaltyStr}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
280
|
+
// BLOCKERS DISPLAY
|
|
281
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
282
|
+
|
|
283
|
+
function printBlockers(blockers) {
|
|
284
|
+
if (!blockers || blockers.length === 0) {
|
|
285
|
+
printSection('SHIP BLOCKERS', '🚀');
|
|
286
|
+
console.log();
|
|
287
|
+
console.log(` ${c.green}${c.bold}✓ No blockers! You're clear to ship.${c.reset}`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
printSection(`SHIP BLOCKERS (${blockers.length})`, '🚨');
|
|
292
|
+
console.log();
|
|
293
|
+
|
|
294
|
+
for (const blocker of blockers.slice(0, 8)) {
|
|
295
|
+
const sevColor = blocker.severity === 'critical' ? bgRgb(180, 40, 40) : bgRgb(180, 120, 0);
|
|
296
|
+
const sevLabel = blocker.severity === 'critical' ? 'CRITICAL' : ' HIGH ';
|
|
297
|
+
|
|
298
|
+
console.log(` ${sevColor}${c.bold} ${sevLabel} ${c.reset} ${c.bold}${truncate(blocker.title, 45)}${c.reset}`);
|
|
299
|
+
console.log(` ${c.dim} ${truncate(blocker.description, 55)}${c.reset}`);
|
|
300
|
+
if (blocker.file) {
|
|
301
|
+
const fileDisplay = path.basename(blocker.file) + (blocker.line ? `:${blocker.line}` : '');
|
|
302
|
+
console.log(` ${c.dim} ${c.reset}${c.cyan}${fileDisplay}${c.reset}`);
|
|
303
|
+
}
|
|
304
|
+
if (blocker.fixSuggestion) {
|
|
305
|
+
console.log(` ${c.dim} ${c.green}→ ${blocker.fixSuggestion}${c.reset}`);
|
|
306
|
+
}
|
|
307
|
+
console.log();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (blockers.length > 8) {
|
|
311
|
+
console.log(` ${c.dim}... and ${blockers.length - 8} more blockers (see full report)${c.reset}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
316
|
+
// LAYERS DISPLAY
|
|
317
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
318
|
+
|
|
319
|
+
function printLayers(layers) {
|
|
320
|
+
printSection('ANALYSIS LAYERS', '⚡');
|
|
321
|
+
console.log();
|
|
322
|
+
|
|
323
|
+
const layerInfo = {
|
|
324
|
+
ast: { name: 'AST Analysis', icon: '🔍', desc: 'Static code analysis' },
|
|
325
|
+
truth: { name: 'Build Truth', icon: '📦', desc: 'Manifest verification' },
|
|
326
|
+
reality: { name: 'Reality Proof', icon: '🎭', desc: 'Playwright crawl' },
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
for (const layer of layers) {
|
|
330
|
+
const info = layerInfo[layer.layer] || { name: layer.layer, icon: '○', desc: '' };
|
|
331
|
+
const status = layer.executed ? `${c.green}✓${c.reset}` : `${c.dim}○${c.reset}`;
|
|
332
|
+
const duration = layer.executed ? `${c.dim}${layer.duration}ms${c.reset}` : `${c.dim}skipped${c.reset}`;
|
|
333
|
+
const findings = layer.executed ? `${c.cyan}${layer.findings}${c.reset} ${c.dim}findings${c.reset}` : '';
|
|
334
|
+
|
|
335
|
+
console.log(` ${status} ${info.icon} ${c.bold}${info.name.padEnd(15)}${c.reset} ${duration.padEnd(20)} ${findings}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
340
|
+
// ARGS PARSER
|
|
341
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
342
|
+
|
|
343
|
+
function parseArgs(args) {
|
|
344
|
+
const opts = {
|
|
345
|
+
path: process.cwd(),
|
|
346
|
+
truth: false,
|
|
347
|
+
reality: false,
|
|
348
|
+
realitySniff: false,
|
|
349
|
+
baseUrl: null,
|
|
350
|
+
json: false,
|
|
351
|
+
sarif: false,
|
|
352
|
+
verbose: false,
|
|
353
|
+
help: false,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < args.length; i++) {
|
|
357
|
+
const arg = args[i];
|
|
358
|
+
|
|
359
|
+
if (arg === '--truth' || arg === '-t') opts.truth = true;
|
|
360
|
+
else if (arg === '--reality' || arg === '-r') { opts.reality = true; opts.truth = true; }
|
|
361
|
+
else if (arg === '--reality-sniff' || arg === '--sniff') opts.realitySniff = true;
|
|
362
|
+
else if (arg === '--url' || arg === '-u') { opts.baseUrl = args[++i]; opts.reality = true; opts.truth = true; }
|
|
363
|
+
else if (arg === '--json') opts.json = true;
|
|
364
|
+
else if (arg === '--sarif') opts.sarif = true;
|
|
365
|
+
else if (arg === '--verbose' || arg === '-v') opts.verbose = true;
|
|
366
|
+
else if (arg === '--help' || arg === '-h') opts.help = true;
|
|
367
|
+
else if (arg === '--path' || arg === '-p') opts.path = args[++i];
|
|
368
|
+
else if (arg.startsWith('--path=')) opts.path = arg.split('=')[1];
|
|
369
|
+
else if (!arg.startsWith('-')) opts.path = path.resolve(arg);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return opts;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function printHelp() {
|
|
376
|
+
console.log(BANNER);
|
|
377
|
+
console.log(`
|
|
378
|
+
${c.bold}Usage:${c.reset} vibecheck scan [path] [options]
|
|
379
|
+
|
|
380
|
+
${c.bold}Scan Modes:${c.reset}
|
|
381
|
+
${c.cyan}(default)${c.reset} Layer 1: AST static analysis ${c.dim}(fast)${c.reset}
|
|
382
|
+
${c.cyan}--truth, -t${c.reset} Layer 1+2: Include build manifest verification ${c.dim}(CI/ship)${c.reset}
|
|
383
|
+
${c.cyan}--reality, -r${c.reset} Layer 1+2+3: Include Playwright runtime proof ${c.dim}(full)${c.reset}
|
|
384
|
+
${c.cyan}--reality-sniff${c.reset} Include Reality Sniff AI artifact detection ${c.dim}(recommended)${c.reset}
|
|
385
|
+
|
|
386
|
+
${c.bold}Options:${c.reset}
|
|
387
|
+
${c.cyan}--url, -u${c.reset} Base URL for reality testing (e.g., http://localhost:3000)
|
|
388
|
+
${c.cyan}--verbose, -v${c.reset} Show detailed progress
|
|
389
|
+
${c.cyan}--json${c.reset} Output results as JSON
|
|
390
|
+
${c.cyan}--sarif${c.reset} Output in SARIF format (GitHub code scanning)
|
|
391
|
+
${c.cyan}--help, -h${c.reset} Show this help
|
|
392
|
+
|
|
393
|
+
${c.bold}Examples:${c.reset}
|
|
394
|
+
${c.dim}# Quick scan (AST only)${c.reset}
|
|
395
|
+
vibecheck scan
|
|
396
|
+
|
|
397
|
+
${c.dim}# CI/CD scan with manifest verification${c.reset}
|
|
398
|
+
vibecheck scan --truth
|
|
399
|
+
|
|
400
|
+
${c.dim}# Full proof with Playwright${c.reset}
|
|
401
|
+
vibecheck scan --reality --url http://localhost:3000
|
|
402
|
+
`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
406
|
+
// MAIN SCAN FUNCTION - ROUTE INTEGRITY SYSTEM
|
|
407
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
408
|
+
|
|
409
|
+
async function runScan(args) {
|
|
410
|
+
const opts = parseArgs(args);
|
|
411
|
+
|
|
412
|
+
// Show help if requested
|
|
413
|
+
if (opts.help) {
|
|
414
|
+
printHelp();
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Entitlement check (graceful offline handling)
|
|
419
|
+
try {
|
|
420
|
+
await enforceLimit('scans');
|
|
421
|
+
await trackUsage('scans');
|
|
422
|
+
} catch (err) {
|
|
423
|
+
if (err.code === 'LIMIT_EXCEEDED') {
|
|
424
|
+
console.error(err.upgradePrompt || err.message);
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
// Network error - fall back to free tier only (SECURITY: never grant paid features offline)
|
|
428
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT' || err.code === 'ENOTFOUND' || err.name === 'NetworkError') {
|
|
429
|
+
console.warn(` ${c.yellow}⚠${c.reset} API unavailable, running in ${c.green}FREE${c.reset} tier mode`);
|
|
430
|
+
console.warn(` ${c.dim}Paid features require API connection. Continuing with free features only.${c.reset}\n`);
|
|
431
|
+
// Continue with free tier features only - scan command is free tier
|
|
432
|
+
} else {
|
|
433
|
+
throw err; // Re-throw unexpected errors
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Print banner
|
|
438
|
+
printBanner();
|
|
439
|
+
|
|
440
|
+
const projectPath = path.resolve(opts.path);
|
|
441
|
+
const startTime = Date.now();
|
|
442
|
+
|
|
443
|
+
// Emit audit event for scan start
|
|
444
|
+
emitScanStart(projectPath, args);
|
|
445
|
+
const projectName = path.basename(projectPath);
|
|
446
|
+
|
|
447
|
+
// Validate project path
|
|
448
|
+
if (!fs.existsSync(projectPath)) {
|
|
449
|
+
throw createUserError(`Project path does not exist: ${projectPath}`, "ValidationError");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Determine layers
|
|
453
|
+
const layers = {
|
|
454
|
+
ast: true,
|
|
455
|
+
truth: opts.truth,
|
|
456
|
+
reality: opts.reality,
|
|
457
|
+
realitySniff: opts.realitySniff,
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Print scan info
|
|
461
|
+
const layerNames = [];
|
|
462
|
+
if (layers.ast) layerNames.push('AST');
|
|
463
|
+
if (layers.truth) layerNames.push('Truth');
|
|
464
|
+
if (layers.reality) layerNames.push('Reality');
|
|
465
|
+
if (layers.realitySniff) layerNames.push('Reality Sniff');
|
|
466
|
+
|
|
467
|
+
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
468
|
+
console.log(` ${c.dim}Path:${c.reset} ${projectPath}`);
|
|
469
|
+
console.log(` ${c.dim}Layers:${c.reset} ${c.cyan}${layerNames.join(' → ')}${c.reset}`);
|
|
470
|
+
console.log();
|
|
471
|
+
|
|
472
|
+
// Reality layer requires URL
|
|
473
|
+
if (opts.reality && !opts.baseUrl) {
|
|
474
|
+
console.log(` ${c.yellow}⚠${c.reset} ${c.bold}Reality layer requires --url${c.reset}`);
|
|
475
|
+
console.log(` ${c.dim}Example: vibecheck scan --reality --url http://localhost:3000${c.reset}`);
|
|
476
|
+
console.log();
|
|
477
|
+
return 1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
// Import systems
|
|
482
|
+
const { scanRouteIntegrity } = require('../../dist/lib/route-integrity');
|
|
483
|
+
|
|
484
|
+
// Try to import new unified output system (may not be compiled yet)
|
|
485
|
+
let buildVerdictOutput, normalizeFinding, formatStandardOutput, formatScanOutput, getExitCode, CacheManager;
|
|
486
|
+
let useUnifiedOutput = false;
|
|
487
|
+
|
|
488
|
+
try {
|
|
489
|
+
const outputContract = require('../../dist/lib/cli/output-contract');
|
|
490
|
+
buildVerdictOutput = outputContract.buildVerdictOutput;
|
|
491
|
+
normalizeFinding = outputContract.normalizeFinding;
|
|
492
|
+
formatStandardOutput = outputContract.formatStandardOutput;
|
|
493
|
+
|
|
494
|
+
const unifiedOutput = require('./lib/unified-output');
|
|
495
|
+
formatScanOutput = unifiedOutput.formatScanOutput;
|
|
496
|
+
getExitCode = unifiedOutput.getExitCode;
|
|
497
|
+
|
|
498
|
+
const cacheModule = require('../../dist/lib/cli/cache-manager');
|
|
499
|
+
CacheManager = cacheModule.CacheManager;
|
|
500
|
+
useUnifiedOutput = true;
|
|
501
|
+
} catch (error) {
|
|
502
|
+
// Fallback to old system if new one not available
|
|
503
|
+
if (opts.verbose) {
|
|
504
|
+
console.warn('Unified output system not available, using legacy format');
|
|
505
|
+
}
|
|
506
|
+
useUnifiedOutput = false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Initialize cache if available
|
|
510
|
+
let cache = null;
|
|
511
|
+
let cached = false;
|
|
512
|
+
let cachedResult = null;
|
|
513
|
+
|
|
514
|
+
if (CacheManager) {
|
|
515
|
+
cache = new CacheManager(projectPath);
|
|
516
|
+
const cacheKey = 'scan';
|
|
517
|
+
|
|
518
|
+
// Compute project hash for caching
|
|
519
|
+
const sourceFiles = await findSourceFiles(projectPath);
|
|
520
|
+
const projectHash = await cache.computeProjectHash(sourceFiles, { layers, baseUrl: opts.baseUrl });
|
|
521
|
+
|
|
522
|
+
// Check cache
|
|
523
|
+
if (!opts.verbose) {
|
|
524
|
+
cachedResult = await cache.get(cacheKey, projectHash);
|
|
525
|
+
if (cachedResult && buildVerdictOutput) {
|
|
526
|
+
cached = true;
|
|
527
|
+
// Use cached result
|
|
528
|
+
const verdict = buildVerdictOutput(cachedResult.findings, cachedResult.timings, true);
|
|
529
|
+
const output = formatStandardOutput(verdict, cachedResult.findings, cachedResult.scanId, projectPath, {
|
|
530
|
+
version: require('../../package.json').version || '1.0.0',
|
|
531
|
+
nodeVersion: process.version,
|
|
532
|
+
platform: process.platform,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
if (opts.json) {
|
|
536
|
+
console.log(JSON.stringify(output, null, 2));
|
|
537
|
+
return getExitCode(verdict);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
console.log(formatScanOutput({ verdict, findings: cachedResult.findings }, { verbose: opts.verbose, json: opts.json }));
|
|
541
|
+
return getExitCode(verdict);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Start scanning with spinner
|
|
547
|
+
const timings = { discovery: 0, analysis: 0, verification: 0, detection: 0, total: 0 };
|
|
548
|
+
timings.discovery = Date.now();
|
|
549
|
+
|
|
550
|
+
startSpinner('Analyzing codebase...');
|
|
551
|
+
|
|
552
|
+
const result = await scanRouteIntegrity({
|
|
553
|
+
projectPath,
|
|
554
|
+
layers,
|
|
555
|
+
baseUrl: opts.baseUrl,
|
|
556
|
+
verbose: opts.verbose,
|
|
557
|
+
onProgress: opts.verbose ? (phase, progress) => {
|
|
558
|
+
stopSpinner(`${phase}: ${Math.round(progress)}%`, true);
|
|
559
|
+
if (progress < 100) startSpinner(`Running ${phase}...`);
|
|
560
|
+
} : undefined,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
timings.analysis = Date.now() - timings.discovery;
|
|
564
|
+
|
|
565
|
+
// Run new detection engines (Dead UI, Billing Bypass, Fake Success)
|
|
566
|
+
let detectionFindings = [];
|
|
567
|
+
try {
|
|
568
|
+
startSpinner('Running detection engines...');
|
|
569
|
+
const detectionStart = Date.now();
|
|
570
|
+
|
|
571
|
+
// Dynamic import for TypeScript detection engines
|
|
572
|
+
const { DeadUIDetector } = require('../../dist/engines/detection/dead-ui-detector');
|
|
573
|
+
const { BillingBypassDetector } = require('../../dist/engines/detection/billing-bypass-detector');
|
|
574
|
+
const { FakeSuccessDetector } = require('../../dist/engines/detection/fake-success-detector');
|
|
575
|
+
|
|
576
|
+
const exclude = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '_archive'];
|
|
577
|
+
|
|
578
|
+
// Run detectors in parallel
|
|
579
|
+
const [deadUIResult, billingResult, fakeSuccessResult] = await Promise.all([
|
|
580
|
+
new DeadUIDetector(projectPath).scan({ exclude }),
|
|
581
|
+
new BillingBypassDetector(projectPath).scan({ exclude }),
|
|
582
|
+
new FakeSuccessDetector(projectPath).scan({ exclude }),
|
|
583
|
+
]);
|
|
584
|
+
|
|
585
|
+
// Convert to normalized findings format
|
|
586
|
+
for (const finding of deadUIResult.findings) {
|
|
587
|
+
detectionFindings.push({
|
|
588
|
+
id: finding.id,
|
|
589
|
+
ruleId: finding.type,
|
|
590
|
+
category: 'DEAD_UI',
|
|
591
|
+
severity: finding.severity,
|
|
592
|
+
title: finding.message,
|
|
593
|
+
description: finding.suggestion,
|
|
594
|
+
file: finding.file,
|
|
595
|
+
line: finding.line,
|
|
596
|
+
evidence: finding.evidence,
|
|
597
|
+
autofixAvailable: false,
|
|
598
|
+
verdict: 'FAIL',
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
for (const finding of billingResult.findings) {
|
|
603
|
+
detectionFindings.push({
|
|
604
|
+
id: finding.id,
|
|
605
|
+
ruleId: finding.type,
|
|
606
|
+
category: 'BILLING',
|
|
607
|
+
severity: finding.severity,
|
|
608
|
+
title: finding.message,
|
|
609
|
+
description: finding.suggestion,
|
|
610
|
+
file: finding.file,
|
|
611
|
+
line: finding.line,
|
|
612
|
+
evidence: finding.evidence,
|
|
613
|
+
autofixAvailable: false,
|
|
614
|
+
verdict: 'FAIL',
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
for (const finding of fakeSuccessResult.findings) {
|
|
619
|
+
detectionFindings.push({
|
|
620
|
+
id: finding.id,
|
|
621
|
+
ruleId: finding.type,
|
|
622
|
+
category: 'FAKE_SUCCESS',
|
|
623
|
+
severity: finding.severity,
|
|
624
|
+
title: finding.message,
|
|
625
|
+
description: finding.suggestion,
|
|
626
|
+
file: finding.file,
|
|
627
|
+
line: finding.line,
|
|
628
|
+
evidence: finding.evidence,
|
|
629
|
+
autofixAvailable: false,
|
|
630
|
+
verdict: 'FAIL',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
timings.detection = Date.now() - detectionStart;
|
|
635
|
+
stopSpinner(`Detection complete (${detectionFindings.length} findings)`, true);
|
|
636
|
+
} catch (detectionError) {
|
|
637
|
+
// Detection engines not compiled yet - continue without them
|
|
638
|
+
if (opts.verbose) {
|
|
639
|
+
console.log(` ${c.dim}Detection engines not available: ${detectionError.message}${c.reset}`);
|
|
640
|
+
}
|
|
641
|
+
stopSpinner('Detection skipped (not compiled)', true);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
timings.verification = Date.now() - timings.analysis - timings.discovery;
|
|
645
|
+
timings.total = Date.now() - startTime;
|
|
646
|
+
|
|
647
|
+
stopSpinner('Analysis complete', true);
|
|
648
|
+
|
|
649
|
+
const { report, outputPaths } = result;
|
|
650
|
+
|
|
651
|
+
// Normalize findings with stable IDs
|
|
652
|
+
const existingIDs = new Set();
|
|
653
|
+
const normalizedFindings = [];
|
|
654
|
+
|
|
655
|
+
// Normalize route integrity findings
|
|
656
|
+
if (report.shipBlockers) {
|
|
657
|
+
for (let i = 0; i < report.shipBlockers.length; i++) {
|
|
658
|
+
const blocker = report.shipBlockers[i];
|
|
659
|
+
const category = blocker.category || 'ROUTE';
|
|
660
|
+
const normalized = normalizeFinding(blocker, category, i, existingIDs);
|
|
661
|
+
normalizedFindings.push(normalized);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Normalize Reality Sniff findings if present
|
|
666
|
+
if (report.realitySniffFindings) {
|
|
667
|
+
for (let i = 0; i < report.realitySniffFindings.length; i++) {
|
|
668
|
+
const finding = report.realitySniffFindings[i];
|
|
669
|
+
const category = finding.ruleId?.startsWith('auth') ? 'AUTH' : 'REALITY';
|
|
670
|
+
const normalized = normalizeFinding(finding, category, normalizedFindings.length, existingIDs);
|
|
671
|
+
normalizedFindings.push(normalized);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Add detection engine findings (Dead UI, Billing, Fake Success)
|
|
676
|
+
for (const finding of detectionFindings) {
|
|
677
|
+
normalizedFindings.push(finding);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Use new unified output if available, otherwise fallback to old format
|
|
681
|
+
if (useUnifiedOutput && buildVerdictOutput && normalizeFinding) {
|
|
682
|
+
// Normalize findings with stable IDs
|
|
683
|
+
const existingIDs = new Set();
|
|
684
|
+
const normalizedFindings = [];
|
|
685
|
+
|
|
686
|
+
// Normalize route integrity findings
|
|
687
|
+
if (report.shipBlockers) {
|
|
688
|
+
for (let i = 0; i < report.shipBlockers.length; i++) {
|
|
689
|
+
const blocker = report.shipBlockers[i];
|
|
690
|
+
const category = blocker.category || 'ROUTE';
|
|
691
|
+
const normalized = normalizeFinding(blocker, category, i, existingIDs);
|
|
692
|
+
normalizedFindings.push(normalized);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Normalize Reality Sniff findings if present
|
|
697
|
+
if (report.realitySniffFindings) {
|
|
698
|
+
for (let i = 0; i < report.realitySniffFindings.length; i++) {
|
|
699
|
+
const finding = report.realitySniffFindings[i];
|
|
700
|
+
const category = finding.ruleId?.startsWith('auth') ? 'AUTH' : 'REALITY';
|
|
701
|
+
const normalized = normalizeFinding(finding, category, normalizedFindings.length, existingIDs);
|
|
702
|
+
normalizedFindings.push(normalized);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Add detection engine findings (Dead UI, Billing, Fake Success)
|
|
707
|
+
for (const finding of detectionFindings) {
|
|
708
|
+
normalizedFindings.push(finding);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Build verdict
|
|
712
|
+
const verdict = buildVerdictOutput(normalizedFindings, timings, false);
|
|
713
|
+
const scanId = `scan_${Date.now()}`;
|
|
714
|
+
|
|
715
|
+
// Cache result
|
|
716
|
+
if (cache) {
|
|
717
|
+
const sourceFiles = await findSourceFiles(projectPath);
|
|
718
|
+
const projectHash = await cache.computeProjectHash(sourceFiles, { layers, baseUrl: opts.baseUrl });
|
|
719
|
+
await cache.set('scan', projectHash, {
|
|
720
|
+
findings: normalizedFindings,
|
|
721
|
+
timings,
|
|
722
|
+
scanId,
|
|
723
|
+
}, {
|
|
724
|
+
filesScanned: sourceFiles.length,
|
|
725
|
+
findings: normalizedFindings.length,
|
|
726
|
+
duration: timings.total,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Build standard output
|
|
731
|
+
const standardOutput = formatStandardOutput(verdict, normalizedFindings, scanId, projectPath, {
|
|
732
|
+
version: require('../../package.json').version || '1.0.0',
|
|
733
|
+
nodeVersion: process.version,
|
|
734
|
+
platform: process.platform,
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// JSON output mode
|
|
738
|
+
if (opts.json) {
|
|
739
|
+
console.log(JSON.stringify(standardOutput, null, 2));
|
|
740
|
+
return getExitCode(verdict);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// SARIF output mode
|
|
744
|
+
if (opts.sarif) {
|
|
745
|
+
const sarifContent = fs.readFileSync(outputPaths.sarif, 'utf8');
|
|
746
|
+
console.log(sarifContent);
|
|
747
|
+
return report.score.overall >= 70 ? 0 : 1;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
751
|
+
// UNIFIED OUTPUT
|
|
752
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
753
|
+
|
|
754
|
+
// Use unified output formatter
|
|
755
|
+
console.log(formatScanOutput({ verdict, findings: normalizedFindings }, { verbose: opts.verbose, json: false }));
|
|
756
|
+
|
|
757
|
+
// Additional details if verbose
|
|
758
|
+
if (opts.verbose) {
|
|
759
|
+
printBreakdown(report.score.breakdown);
|
|
760
|
+
printCoverageMap(report.coverageMap);
|
|
761
|
+
printLayers(report.layers);
|
|
762
|
+
|
|
763
|
+
printSection('REPORTS', '📄');
|
|
764
|
+
console.log();
|
|
765
|
+
console.log(` ${c.cyan}${outputPaths.md}${c.reset}`);
|
|
766
|
+
console.log(` ${c.dim}${outputPaths.json}${c.reset}`);
|
|
767
|
+
if (outputPaths.sarif) {
|
|
768
|
+
console.log(` ${c.dim}${outputPaths.sarif}${c.reset}`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Emit audit event for scan complete
|
|
773
|
+
emitScanComplete(projectPath, verdict.verdict === 'PASS' ? 'success' : 'failure', {
|
|
774
|
+
score: report.score?.overall || (verdict.verdict === 'PASS' ? 100 : 50),
|
|
775
|
+
grade: report.score?.grade || (verdict.verdict === 'PASS' ? 'A' : 'F'),
|
|
776
|
+
issueCount: verdict.summary.blockers,
|
|
777
|
+
durationMs: timings.total,
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
return getExitCode(verdict);
|
|
781
|
+
} // End of if (useUnifiedOutput)
|
|
782
|
+
|
|
783
|
+
} catch (error) {
|
|
784
|
+
stopSpinner(`Scan failed: ${error.message}`, false);
|
|
785
|
+
|
|
786
|
+
// Use unified error handling
|
|
787
|
+
const { printError, EXIT_CODES } = require('./lib/unified-output');
|
|
788
|
+
const exitCode = printError(error, 'Scan');
|
|
789
|
+
|
|
790
|
+
// Emit audit event for scan error
|
|
791
|
+
emitScanComplete(projectPath, 'error', {
|
|
792
|
+
errorCode: error.code || 'SCAN_ERROR',
|
|
793
|
+
errorMessage: error.message,
|
|
794
|
+
durationMs: Date.now() - startTime,
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
return exitCode;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Helper function to find source files for cache hash
|
|
802
|
+
async function findSourceFiles(projectPath) {
|
|
803
|
+
const files = [];
|
|
804
|
+
const fs = require('fs');
|
|
805
|
+
const path = require('path');
|
|
806
|
+
|
|
807
|
+
async function walk(dir) {
|
|
808
|
+
try {
|
|
809
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
810
|
+
for (const entry of entries) {
|
|
811
|
+
const fullPath = path.join(dir, entry.name);
|
|
812
|
+
if (entry.isDirectory()) {
|
|
813
|
+
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
814
|
+
await walk(fullPath);
|
|
815
|
+
}
|
|
816
|
+
} else if (entry.isFile()) {
|
|
817
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
818
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
819
|
+
files.push(fullPath);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
} catch {
|
|
824
|
+
// Skip inaccessible directories
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
await walk(projectPath);
|
|
829
|
+
return files;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Export with error handling wrapper
|
|
833
|
+
module.exports = {
|
|
834
|
+
runScan: withErrorHandling(runScan, "Scan failed"),
|
|
835
|
+
};
|