vibecheck-ai 2.0.2 → 5.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/bin/.generated +25 -0
- package/bin/_deprecations.js +463 -0
- package/bin/_router.js +46 -0
- package/bin/cli-hygiene.js +241 -0
- package/bin/dev/run-v2-torture.js +30 -0
- package/bin/registry.js +656 -0
- package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -0
- package/bin/runners/REPORT_AUDIT.md +64 -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 +513 -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-enhanced.js +2525 -0
- package/bin/runners/context/generators/cursor.js +514 -0
- package/bin/runners/context/generators/mcp.js +169 -0
- package/bin/runners/context/generators/windsurf.js +180 -0
- package/bin/runners/context/git-context.js +304 -0
- package/bin/runners/context/index.js +1110 -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 +1264 -0
- package/bin/runners/context/security-scanner.js +541 -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 +336 -0
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
- package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
- package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
- package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
- package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
- package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
- package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
- package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
- package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
- package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
- package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
- package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
- package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
- package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
- package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
- package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
- package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
- package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/interceptor/base.js +308 -0
- package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
- package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
- package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
- package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
- package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
- package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
- package/bin/runners/lib/agent-firewall/logger.js +141 -0
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
- package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
- package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +79 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +227 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +191 -0
- package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
- package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
- package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
- package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
- package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
- package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
- package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
- package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
- package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
- package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
- package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
- package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
- package/bin/runners/lib/agent-firewall/risk/thresholds.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
- package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
- package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
- package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
- package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
- package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
- package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
- package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
- package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
- package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
- package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/ai-bridge.js +416 -0
- package/bin/runners/lib/analysis-core.js +309 -0
- package/bin/runners/lib/analyzers.js +2500 -0
- package/bin/runners/lib/api-client.js +269 -0
- package/bin/runners/lib/approve-output.js +235 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
- package/bin/runners/lib/audit-bridge.js +391 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/auth-truth.js +193 -0
- package/bin/runners/lib/auth.js +215 -0
- package/bin/runners/lib/authority-badge.js +425 -0
- package/bin/runners/lib/backup.js +62 -0
- package/bin/runners/lib/billing.js +107 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/claims.js +118 -0
- package/bin/runners/lib/classify-output.js +204 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/cli-output.js +400 -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 +202 -0
- package/bin/runners/lib/contracts/env-contract.js +181 -0
- package/bin/runners/lib/contracts/external-contract.js +206 -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 +199 -0
- package/bin/runners/lib/contracts.js +804 -0
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/detect.js +89 -0
- package/bin/runners/lib/detectors-v2.js +622 -0
- package/bin/runners/lib/doctor/autofix.js +254 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/index.js +37 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
- package/bin/runners/lib/doctor/modules/index.js +105 -0
- package/bin/runners/lib/doctor/modules/network.js +250 -0
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/project.js +312 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/modules/runtime.js +224 -0
- package/bin/runners/lib/doctor/modules/security.js +350 -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/safe-repair.js +384 -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-output.js +226 -0
- package/bin/runners/lib/doctor-v2.js +608 -0
- package/bin/runners/lib/drift.js +425 -0
- package/bin/runners/lib/enforcement.js +72 -0
- 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 +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -0
- package/bin/runners/lib/enterprise-init.js +942 -0
- package/bin/runners/lib/entitlements-v2.js +265 -0
- package/bin/runners/lib/entitlements.generated.js +0 -0
- package/bin/runners/lib/entitlements.js +340 -0
- package/bin/runners/lib/env-resolver.js +417 -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 +368 -0
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +684 -0
- package/bin/runners/lib/exit-codes.js +275 -0
- package/bin/runners/lib/extractors/client-calls.js +990 -0
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
- package/bin/runners/lib/extractors/fastify-routes.js +426 -0
- package/bin/runners/lib/extractors/index.js +363 -0
- package/bin/runners/lib/extractors/next-routes.js +524 -0
- package/bin/runners/lib/extractors/proof-graph.js +431 -0
- package/bin/runners/lib/extractors/route-matcher.js +451 -0
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
- package/bin/runners/lib/extractors/ui-bindings.js +547 -0
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/findings-schema.js +281 -0
- package/bin/runners/lib/fingerprint.js +377 -0
- package/bin/runners/lib/firewall-prompt.js +50 -0
- package/bin/runners/lib/fix-output.js +228 -0
- package/bin/runners/lib/global-flags.js +250 -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/help-formatter.js +413 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/html-report.js +650 -0
- package/bin/runners/lib/init-wizard.js +601 -0
- package/bin/runners/lib/interactive-menu.js +1496 -0
- package/bin/runners/lib/json-output.js +76 -0
- package/bin/runners/lib/llm.js +75 -0
- package/bin/runners/lib/logger.js +38 -0
- package/bin/runners/lib/meter.js +61 -0
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/evidence.js +126 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +648 -0
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/missions/templates.js +317 -0
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -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/policy.js +295 -0
- package/bin/runners/lib/polish/accessibility.js +62 -0
- package/bin/runners/lib/polish/analyzer.js +93 -0
- package/bin/runners/lib/polish/backend.js +87 -0
- package/bin/runners/lib/polish/configuration.js +83 -0
- package/bin/runners/lib/polish/documentation.js +83 -0
- package/bin/runners/lib/polish/frontend.js +817 -0
- package/bin/runners/lib/polish/index.js +27 -0
- package/bin/runners/lib/polish/infrastructure.js +80 -0
- package/bin/runners/lib/polish/internationalization.js +85 -0
- package/bin/runners/lib/polish/libraries.js +180 -0
- package/bin/runners/lib/polish/observability.js +75 -0
- package/bin/runners/lib/polish/performance.js +64 -0
- package/bin/runners/lib/polish/privacy.js +110 -0
- package/bin/runners/lib/polish/resilience.js +92 -0
- package/bin/runners/lib/polish/security.js +78 -0
- package/bin/runners/lib/polish/seo.js +71 -0
- package/bin/runners/lib/polish/styles.js +62 -0
- package/bin/runners/lib/polish/utils.js +104 -0
- package/bin/runners/lib/preflight.js +142 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/prove-output.js +220 -0
- package/bin/runners/lib/reality/correlation-detectors.js +359 -0
- package/bin/runners/lib/reality/index.js +318 -0
- package/bin/runners/lib/reality/request-hashing.js +416 -0
- package/bin/runners/lib/reality/request-mapper.js +453 -0
- package/bin/runners/lib/reality/safety-rails.js +463 -0
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
- package/bin/runners/lib/reality/toast-detector.js +393 -0
- package/bin/runners/lib/reality-findings.js +84 -0
- package/bin/runners/lib/reality-output.js +231 -0
- package/bin/runners/lib/receipts.js +179 -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 +626 -0
- package/bin/runners/lib/report-html.js +1233 -0
- package/bin/runners/lib/report-output.js +366 -0
- package/bin/runners/lib/report-templates.js +967 -0
- package/bin/runners/lib/report.js +135 -0
- package/bin/runners/lib/route-detection.js +1209 -0
- package/bin/runners/lib/route-truth.js +1322 -0
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -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/scan-output.js +631 -0
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schema-validator.js +350 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/contracts.schema.json +160 -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/finding.schema.json +100 -0
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -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/share-pack.schema.json +180 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
- package/bin/runners/lib/schemas/validator.js +465 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -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/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output-enterprise.js +239 -0
- package/bin/runners/lib/ship-output.js +1128 -0
- package/bin/runners/lib/snippets.js +67 -0
- package/bin/runners/lib/status-output.js +340 -0
- package/bin/runners/lib/terminal-ui.js +356 -0
- package/bin/runners/lib/truth.js +1691 -0
- package/bin/runners/lib/ui.js +562 -0
- package/bin/runners/lib/unified-cli-output.js +947 -0
- package/bin/runners/lib/unified-output.js +197 -0
- package/bin/runners/lib/upsell.js +410 -0
- package/bin/runners/lib/usage.js +153 -0
- package/bin/runners/lib/validate-patch.js +156 -0
- package/bin/runners/lib/verdict-engine.js +628 -0
- package/bin/runners/lib/verification.js +345 -0
- package/bin/runners/lib/why-tree.js +650 -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 +229 -0
- package/bin/runners/runAgent.d.ts +5 -0
- package/bin/runners/runAgent.js +161 -0
- package/bin/runners/runAllowlist.js +418 -0
- package/bin/runners/runApprove.js +320 -0
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +731 -0
- package/bin/runners/runCI.js +353 -0
- package/bin/runners/runCheckpoint.js +530 -0
- package/bin/runners/runClassify.js +928 -0
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +175 -0
- package/bin/runners/runDoctor.js +877 -0
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.d.ts +5 -0
- package/bin/runners/runFirewall.js +134 -0
- package/bin/runners/runFirewallHook.d.ts +5 -0
- package/bin/runners/runFirewallHook.js +56 -0
- package/bin/runners/runFix.js +1355 -0
- package/bin/runners/runForge.js +451 -0
- package/bin/runners/runGuard.js +262 -0
- package/bin/runners/runInit.js +1927 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1875 -0
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +390 -0
- package/bin/runners/runPromptFirewall.js +211 -0
- package/bin/runners/runProve.js +1411 -0
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +2260 -0
- package/bin/runners/runReport.js +726 -0
- package/bin/runners/runRuntime.js +110 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +688 -0
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +1660 -0
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/runners/runTruth.js +101 -0
- package/bin/runners/runValidate.js +179 -0
- package/bin/runners/runWatch.js +478 -0
- package/bin/runners/utils.js +360 -0
- package/bin/scan.js +617 -0
- package/bin/vibecheck.js +1617 -0
- package/dist/guardrail/index.d.ts +2405 -0
- package/dist/guardrail/index.js +9747 -0
- package/dist/guardrail/index.js.map +1 -0
- package/dist/scanner/index.d.ts +282 -0
- package/dist/scanner/index.js +3395 -0
- package/dist/scanner/index.js.map +1 -0
- package/package.json +123 -104
- package/README.md +0 -491
- package/dist/index.js +0 -99711
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runScan.js — Ultimate Scanner v5.0 Runner
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* Inline scanner with 3 core engines (credentials, fake-features, security).
|
|
6
|
+
* When dist/scanner/ is compiled, upgrades to full 8-engine mode automatically.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* Files → Classify → [Engines in parallel] → Deduplicate → Score → Report
|
|
10
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const crypto = require("crypto");
|
|
18
|
+
const { withErrorHandling } = require("./lib/error-handler");
|
|
19
|
+
const { parseGlobalFlags } = require("./lib/global-flags");
|
|
20
|
+
const { EXIT } = require("./lib/exit-codes");
|
|
21
|
+
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
23
|
+
// LAZY-LOAD: Try compiled 8-engine scanner first
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
25
|
+
|
|
26
|
+
let _scanner = null;
|
|
27
|
+
function getScanner() {
|
|
28
|
+
if (_scanner) return _scanner;
|
|
29
|
+
try {
|
|
30
|
+
_scanner = require("../../dist/scanner/index.js");
|
|
31
|
+
return _scanner;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
38
|
+
// TERMINAL STYLING (zero deps)
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
|
+
|
|
41
|
+
const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
42
|
+
const c = SUPPORTS_COLOR
|
|
43
|
+
? {
|
|
44
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
45
|
+
red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m",
|
|
46
|
+
blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m",
|
|
47
|
+
white: "\x1b[37m", gray: "\x1b[90m",
|
|
48
|
+
}
|
|
49
|
+
: Object.fromEntries(
|
|
50
|
+
["reset","bold","dim","red","green","yellow","blue","magenta","cyan","white","gray"].map(k => [k, ""])
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const SEV_ICON = { critical: `${c.red}●${c.reset}`, high: `${c.yellow}●${c.reset}`, medium: `${c.cyan}●${c.reset}`, low: `${c.dim}●${c.reset}` };
|
|
54
|
+
const SEV_LABEL = { critical: `${c.red}CRITICAL${c.reset}`, high: `${c.yellow}HIGH${c.reset}`, medium: `${c.cyan}MEDIUM${c.reset}`, low: `${c.dim}LOW${c.reset}` };
|
|
55
|
+
const SEV_ORDER = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
56
|
+
|
|
57
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
58
|
+
// ARGS PARSER
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
60
|
+
|
|
61
|
+
function parseArgs(args) {
|
|
62
|
+
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
63
|
+
const opts = {
|
|
64
|
+
path: globalFlags.path || process.cwd(),
|
|
65
|
+
format: "pretty", severity: "low", engines: null, includeTests: false,
|
|
66
|
+
parallel: true, fix: false, dryRun: false, category: null,
|
|
67
|
+
help: globalFlags.help || false, json: globalFlags.json || false,
|
|
68
|
+
quiet: globalFlags.quiet || false, ci: globalFlags.ci || false,
|
|
69
|
+
verbose: globalFlags.verbose || false, sarif: false, failOn: null,
|
|
70
|
+
output: globalFlags.output || null,
|
|
71
|
+
};
|
|
72
|
+
for (let i = 0; i < cleanArgs.length; i++) {
|
|
73
|
+
const a = cleanArgs[i], n = cleanArgs[i + 1];
|
|
74
|
+
switch (a) {
|
|
75
|
+
case "--format": opts.format = n || "pretty"; i++; break;
|
|
76
|
+
case "--severity": opts.severity = n || "low"; i++; break;
|
|
77
|
+
case "--engines": opts.engines = n ? n.split(",") : null; i++; break;
|
|
78
|
+
case "--include-tests": opts.includeTests = true; break;
|
|
79
|
+
case "--parallel": opts.parallel = true; break;
|
|
80
|
+
case "--no-parallel": opts.parallel = false; break;
|
|
81
|
+
case "--fix": case "--auto-fix": opts.fix = true; break;
|
|
82
|
+
case "--dry-run": opts.dryRun = true; break;
|
|
83
|
+
case "--category": opts.category = n; i++; break;
|
|
84
|
+
case "--sarif": opts.sarif = true; opts.format = "sarif"; break;
|
|
85
|
+
case "--fail-on": opts.failOn = n; i++; break;
|
|
86
|
+
case "--output": opts.output = n; i++; break;
|
|
87
|
+
case "--json": opts.json = true; opts.format = "json"; break;
|
|
88
|
+
case "--quiet": case "-q": opts.quiet = true; break;
|
|
89
|
+
case "--verbose": opts.verbose = true; break;
|
|
90
|
+
case "--help": case "-h": opts.help = true; break;
|
|
91
|
+
default: if (!a.startsWith("-") && !opts.category) opts.category = a; break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return opts;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
98
|
+
// HELP
|
|
99
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
100
|
+
|
|
101
|
+
function printHelp() {
|
|
102
|
+
console.log(`
|
|
103
|
+
${c.bold}${c.cyan}vibecheck scan${c.reset} — The most accurate AI code scanner
|
|
104
|
+
|
|
105
|
+
${c.bold}Usage:${c.reset}
|
|
106
|
+
vibecheck scan [subcommand] [options]
|
|
107
|
+
|
|
108
|
+
${c.bold}Subcommands:${c.reset}
|
|
109
|
+
${c.cyan}secrets${c.reset} Scan for leaked credentials & API keys
|
|
110
|
+
${c.cyan}vulns${c.reset} Scan for dependency vulnerabilities
|
|
111
|
+
${c.cyan}routes${c.reset} Scan for dead/orphan routes
|
|
112
|
+
${c.cyan}env${c.reset} Scan for ghost environment variables
|
|
113
|
+
${c.cyan}auth${c.reset} Scan for auth drift & unprotected endpoints
|
|
114
|
+
|
|
115
|
+
${c.bold}Options:${c.reset}
|
|
116
|
+
--path <dir> Project path (default: .)
|
|
117
|
+
--format <fmt> Output: pretty, json, sarif (default: pretty)
|
|
118
|
+
--severity <level> Min severity: low, medium, high, critical
|
|
119
|
+
--engines <list> Comma-separated engine names
|
|
120
|
+
--include-tests Include test files
|
|
121
|
+
--fix Auto-fix fixable findings
|
|
122
|
+
--dry-run Preview fixes without applying
|
|
123
|
+
--sarif SARIF output for GitHub
|
|
124
|
+
--fail-on <level> Exit non-zero if findings >= level (CI mode)
|
|
125
|
+
--output <dir> Report output directory
|
|
126
|
+
--quiet Minimal output
|
|
127
|
+
--help Show this help
|
|
128
|
+
|
|
129
|
+
${c.bold}Engines:${c.reset}
|
|
130
|
+
credentials, security, fake-features, hallucinations,
|
|
131
|
+
dead-ui, code-quality, import-graph, runtime-verify
|
|
132
|
+
|
|
133
|
+
${c.bold}Examples:${c.reset}
|
|
134
|
+
vibecheck scan
|
|
135
|
+
vibecheck scan secrets
|
|
136
|
+
vibecheck scan --sarif --fail-on high
|
|
137
|
+
vibecheck scan --fix --dry-run
|
|
138
|
+
`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
142
|
+
// PATH CLASSIFIER
|
|
143
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
144
|
+
|
|
145
|
+
const CODE_EXTS = new Set([
|
|
146
|
+
".ts",".tsx",".js",".jsx",".mjs",".cjs",".vue",".svelte",
|
|
147
|
+
".py",".rb",".go",".rs",".java",".kt",".cs",".php",".swift",
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
function isCodeFile(fp) { return CODE_EXTS.has(path.extname(fp).toLowerCase()); }
|
|
151
|
+
|
|
152
|
+
function classifyPath(rel) {
|
|
153
|
+
const r = rel.replace(/\\/g, "/");
|
|
154
|
+
if (/node_modules\/|vendor\/|\.yarn\/|\.pnpm\//.test(r)) return { category: "third_party", excludeByDefault: true, isCriticalPath: false };
|
|
155
|
+
if (/\/dist\/|\/build\/|\/.next\/|\/.nuxt\/|\.min\.(js|css)$/.test(r)) return { category: "build_output", excludeByDefault: true, isCriticalPath: false };
|
|
156
|
+
if (/\.d\.ts$|\/generated\/|\.gen\.(ts|js)$|\.lock$/.test(r)) return { category: "generated", excludeByDefault: true, isCriticalPath: false };
|
|
157
|
+
if (/\.(test|spec)\.(ts|tsx|js|jsx)$|__tests__|__mocks__|\.stories\./.test(r)) return { category: "test", excludeByDefault: false, isCriticalPath: false };
|
|
158
|
+
if (/\.(md|mdx|rst|txt)$|\/docs\//.test(r)) return { category: "documentation", excludeByDefault: true, isCriticalPath: false };
|
|
159
|
+
const isCrit = /\/api\/|\/auth\/|\/payment\/|\/billing\/|\/admin\/|middleware/i.test(r);
|
|
160
|
+
return { category: "user_code", excludeByDefault: false, isCriticalPath: isCrit };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function escalateSev(sev, isCrit) {
|
|
164
|
+
if (!isCrit) return sev;
|
|
165
|
+
if (sev === "low") return "medium";
|
|
166
|
+
if (sev === "medium") return "high";
|
|
167
|
+
return sev;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
171
|
+
// FILE LOADER
|
|
172
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
173
|
+
|
|
174
|
+
const DEFAULT_EXCLUDE = ["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".venv",".output"];
|
|
175
|
+
|
|
176
|
+
function loadFiles(root, opts) {
|
|
177
|
+
const files = new Map();
|
|
178
|
+
const exclude = DEFAULT_EXCLUDE;
|
|
179
|
+
const maxSize = 512 * 1024;
|
|
180
|
+
|
|
181
|
+
function walk(dir) {
|
|
182
|
+
let entries;
|
|
183
|
+
try { entries = fs.readdirSync(dir); } catch { return; }
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
const full = path.join(dir, entry);
|
|
186
|
+
if (exclude.includes(entry) || entry.startsWith(".")) {
|
|
187
|
+
try { if (fs.statSync(full).isDirectory()) continue; } catch { continue; }
|
|
188
|
+
}
|
|
189
|
+
let stat;
|
|
190
|
+
try { stat = fs.statSync(full); } catch { continue; }
|
|
191
|
+
if (stat.isDirectory()) { walk(full); continue; }
|
|
192
|
+
if (!isCodeFile(full)) continue;
|
|
193
|
+
if (stat.size > maxSize) continue;
|
|
194
|
+
const rel = path.relative(root, full).replace(/\\/g, "/");
|
|
195
|
+
const cl = classifyPath(rel);
|
|
196
|
+
if (cl.excludeByDefault && cl.category !== "test") continue;
|
|
197
|
+
if (cl.category === "test" && !opts.includeTests) continue;
|
|
198
|
+
try {
|
|
199
|
+
const content = fs.readFileSync(full, "utf-8");
|
|
200
|
+
files.set(rel, {
|
|
201
|
+
path: rel, absolutePath: full, content, lines: content.split("\n"),
|
|
202
|
+
ext: path.extname(full).toLowerCase(),
|
|
203
|
+
hash: crypto.createHash("md5").update(content).digest("hex"),
|
|
204
|
+
classification: cl,
|
|
205
|
+
});
|
|
206
|
+
} catch { /* skip unreadable */ }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
walk(root);
|
|
210
|
+
return files;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
214
|
+
// ENGINE: CREDENTIALS (20 patterns)
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
216
|
+
|
|
217
|
+
const CRED_PATTERNS = [
|
|
218
|
+
{ n: "stripe-live-secret", id: "CRED001", re: /['"`](sk_live_[a-zA-Z0-9]{20,})['"`]/, sev: "critical", msg: "Stripe live secret key hardcoded", fix: "Use process.env.STRIPE_SECRET_KEY", conf: 99, test: true, cwe: "CWE-798" },
|
|
219
|
+
{ n: "stripe-live-pub", id: "CRED001", re: /['"`](pk_live_[a-zA-Z0-9]{20,})['"`]/, sev: "high", msg: "Stripe live publishable key hardcoded", fix: "Use process.env.NEXT_PUBLIC_STRIPE_KEY", conf: 95, test: false },
|
|
220
|
+
{ n: "aws-access", id: "CRED001", re: /['"`](AKIA[0-9A-Z]{16})['"`]/, sev: "critical", msg: "AWS Access Key ID hardcoded", fix: "Use AWS SDK credential chain", conf: 99, test: true, cwe: "CWE-798" },
|
|
221
|
+
{ n: "aws-secret", id: "CRED001", re: /aws_secret_access_key\s*[:=]\s*['"`]([^'"`]{20,})['"`]/i, sev: "critical", msg: "AWS Secret Access Key hardcoded", fix: "Use AWS_SECRET_ACCESS_KEY env var", conf: 98, test: true, cwe: "CWE-798" },
|
|
222
|
+
{ n: "openai", id: "CRED001", re: /['"`](sk-[a-zA-Z0-9]{32,})['"`]/, sev: "critical", msg: "OpenAI API key hardcoded", fix: "Use process.env.OPENAI_API_KEY", conf: 92, test: true },
|
|
223
|
+
{ n: "anthropic", id: "CRED001", re: /['"`](sk-ant-[a-zA-Z0-9-]{20,})['"`]/, sev: "critical", msg: "Anthropic API key hardcoded", fix: "Use process.env.ANTHROPIC_API_KEY", conf: 98, test: true },
|
|
224
|
+
{ n: "github-pat", id: "CRED001", re: /['"`](ghp_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "GitHub personal access token hardcoded", fix: "Use process.env.GITHUB_TOKEN", conf: 99, test: true },
|
|
225
|
+
{ n: "github-oauth", id: "CRED001", re: /['"`](gho_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "GitHub OAuth token hardcoded", fix: "Use process.env.GITHUB_OAUTH_TOKEN", conf: 99, test: true },
|
|
226
|
+
{ n: "google-api", id: "CRED001", re: /['"`](AIza[0-9A-Za-z_-]{35})['"`]/, sev: "high", msg: "Google API key hardcoded", fix: "Use process.env.GOOGLE_API_KEY", conf: 95, test: false },
|
|
227
|
+
{ n: "jwt-secret", id: "CRED003", re: /(?:jwt[_-]?secret|JWT_SECRET)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i, sev: "critical", msg: "JWT signing secret hardcoded", fix: "Use process.env.JWT_SECRET", conf: 90, test: false, cwe: "CWE-798" },
|
|
228
|
+
{ n: "password", id: "CRED002", re: /(?:password|passwd|pwd)\s*[:=]\s*['"`]([^'"`]{4,})['"`]/i, sev: "high", msg: "Hardcoded password detected", fix: "Use env variable or secrets manager", conf: 75, test: false, cwe: "CWE-798" },
|
|
229
|
+
{ n: "generic-secret", id: "CRED002", re: /(?:secret|api[_-]?key|auth[_-]?token)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i, sev: "high", msg: "Hardcoded secret/token detected", fix: "Use environment variable", conf: 70, test: false },
|
|
230
|
+
{ n: "private-key", id: "CRED004", re: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/, sev: "critical", msg: "Private key embedded in source code", fix: "Store key in file, reference via env var", conf: 99, test: true, cwe: "CWE-321" },
|
|
231
|
+
{ n: "conn-string", id: "CRED005", re: /['"`](?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\/\/[^'"`\s]{10,}['"`]/i, sev: "critical", msg: "Database connection string with credentials", fix: "Use DATABASE_URL env var", conf: 92, test: false, cwe: "CWE-798" },
|
|
232
|
+
{ n: "slack", id: "CRED001", re: /['"`](xox[bpoas]-[0-9a-zA-Z-]{10,})['"`]/, sev: "critical", msg: "Slack token hardcoded", fix: "Use process.env.SLACK_TOKEN", conf: 97, test: true },
|
|
233
|
+
{ n: "sendgrid", id: "CRED001", re: /['"`](SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43})['"`]/, sev: "critical", msg: "SendGrid API key hardcoded", fix: "Use process.env.SENDGRID_API_KEY", conf: 99, test: true },
|
|
234
|
+
{ n: "npm-token", id: "CRED001", re: /['"`](npm_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "npm auth token hardcoded", fix: "Use .npmrc with env var interpolation", conf: 98, test: true },
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
function scanCredentials(files) {
|
|
238
|
+
const findings = [];
|
|
239
|
+
for (const [, file] of files) {
|
|
240
|
+
const isTest = file.classification.category === "test";
|
|
241
|
+
const isExample = /\.(example|sample|template)\b/.test(file.path);
|
|
242
|
+
const isCrit = file.classification.isCriticalPath;
|
|
243
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
244
|
+
const line = file.lines[i];
|
|
245
|
+
const trimmed = line.trim();
|
|
246
|
+
if (trimmed.startsWith("//") && !trimmed.includes("=")) continue;
|
|
247
|
+
if (trimmed.startsWith("*")) continue;
|
|
248
|
+
for (const p of CRED_PATTERNS) {
|
|
249
|
+
if (isTest && !p.test) continue;
|
|
250
|
+
if (isExample) continue;
|
|
251
|
+
const m = line.match(p.re);
|
|
252
|
+
if (!m) continue;
|
|
253
|
+
if (p.n === "password") {
|
|
254
|
+
if (/type\s|interface\s|placeholder|example/i.test(line)) continue;
|
|
255
|
+
if (!m[1] || m[1].length < 6) continue;
|
|
256
|
+
}
|
|
257
|
+
if (p.n === "generic-secret") {
|
|
258
|
+
if (!m[1] || /^[a-z_]+$/i.test(m[1])) continue;
|
|
259
|
+
if (/placeholder|example|your[_-]/i.test(m[1])) continue;
|
|
260
|
+
}
|
|
261
|
+
findings.push({
|
|
262
|
+
id: `${p.id}-${file.path}-${i+1}`, ruleId: p.id, engine: "credentials",
|
|
263
|
+
category: "credentials", severity: escalateSev(p.sev, isCrit),
|
|
264
|
+
confidence: p.conf >= 90 ? "certain" : p.conf >= 70 ? "likely" : "possible",
|
|
265
|
+
confidenceScore: p.conf, file: file.path, line: i+1, column: m.index,
|
|
266
|
+
code: trimmed, message: p.msg, why: "Hardcoded credentials can be extracted from source, builds, or version history.", fix: p.fix,
|
|
267
|
+
autoFixable: false, tags: ["secrets","credentials"], cwe: p.cwe, verified: false,
|
|
268
|
+
});
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return findings;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
277
|
+
// ENGINE: FAKE FEATURES (6 pattern groups)
|
|
278
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
279
|
+
|
|
280
|
+
const PLACEHOLDER_RE = [
|
|
281
|
+
/[=:]\s*['"]?placeholder['"]?(?:\s*[,;}\]]|$)/i, /\bstub(?:bed|bing)?\s*(?:function|method|implementation)/i,
|
|
282
|
+
/\bdummy[-_]?(?:data|value|user|id|token|response)\b/i, /\bfake[-_]?(?:data|response|user|api|endpoint)\b/i,
|
|
283
|
+
/\bTODO\b.*(?:implement|fix|complete|replace|remove)/i, /\bWIP\b(?:\s*[-:]\s|\s+)/i,
|
|
284
|
+
/\bcoming\s+soon\b/i, /\bCHANGE[-_]?ME\b/i, /\bREPLACE[-_]?ME\b/i,
|
|
285
|
+
];
|
|
286
|
+
const FAKE_SUCCESS_RE = [
|
|
287
|
+
/^\s*return\s+true\s*;?\s*$/i, /return\s*{\s*success:\s*true\s*}/i,
|
|
288
|
+
/return\s*{\s*status:\s*["']ok["']\s*}/i, /\/\/\s*(?:fake|mock|stub)\s*success/i,
|
|
289
|
+
];
|
|
290
|
+
const SILENT_FAIL_RE = [
|
|
291
|
+
/catch\s*\(\s*(?:_|e|err|error)?\s*\)\s*{\s*}/, /catch\s*\([^)]*\)\s*{\s*return(?:\s+(?:undefined|null))?\s*;?\s*}/,
|
|
292
|
+
/catch\s*\([^)]*\)\s*{\s*\/\/[^\n]*\s*}/, /catch\s*\([^)]*\)\s*{\s*console\.log\([^)]+\)\s*;?\s*}/,
|
|
293
|
+
];
|
|
294
|
+
const AUTH_BYPASS_RE = [
|
|
295
|
+
/(?:if|&&|\|\|)\s*\(\s*(?:true|1)\s*\)\s*.*(?:admin|auth)/i, /\b(?:skip|disable|bypass)Auth(?:entication)?\s*[=:]\s*true\b/i,
|
|
296
|
+
/\b(?:const|let|var)\s+isAdmin\s*=\s*true\b/i, /\/\/\s*(?:bypass|skip|disable)\s*auth/i,
|
|
297
|
+
/permissions?\s*[=:]\s*\[\s*['"]?\*['"]?\s*\]/i, /(?:isAuth|isAdmin|hasPermission|canAccess)\s*[=:]\s*\(\)\s*=>\s*true/i,
|
|
298
|
+
];
|
|
299
|
+
const DANGER_DEFAULT_RE = [
|
|
300
|
+
/process\.env\.(?:\w*(?:SECRET|KEY|TOKEN|PASSWORD|CREDENTIAL)\w*)\s*\|\|\s*["'][^'"]{0,50}["']/i,
|
|
301
|
+
/(?:api[_-]?key|secret|token|password)\s*[=:]\s*["'](?:CHANGEME|REPLACE_ME|YOUR_[A-Z_]+|xxx+|TODO)["']/i,
|
|
302
|
+
];
|
|
303
|
+
const EMPTY_FN_RE = [
|
|
304
|
+
/async\s+(?:function\s+\w+|(?:const|let)\s+\w+\s*=\s*async)\s*\([^)]*\)\s*(?::\s*\w+[<>\[\]]*\s*)?\s*{\s*}/,
|
|
305
|
+
/function\s+\w+\s*\([^)]*\)\s*{\s*return\s*;?\s*}/, /=>\s*{\s*}\s*[;,)]/,
|
|
306
|
+
];
|
|
307
|
+
|
|
308
|
+
const FAKE_RULES = {
|
|
309
|
+
FAKE001: { desc: "Empty/stub function — no implementation", why: "Functions with no body silently do nothing. Users think the feature works.", fix: "Implement the function body or throw NotImplementedError.", tags: ["stub","empty"], auto: false },
|
|
310
|
+
FAKE002: { desc: "Fake success return — always returns true/ok", why: "Functions that always return success bypass real logic. Login that always succeeds = no auth.", fix: "Implement actual validation before returning success.", tags: ["fake-success"], auto: false },
|
|
311
|
+
FAKE003: { desc: "Silent error swallowing — empty catch block", why: "Empty catch blocks hide failures. Users see no error but nothing actually works.", fix: "Log the error, rethrow, or handle it meaningfully.", tags: ["silent-failure"], auto: true },
|
|
312
|
+
FAKE004: { desc: "Placeholder/TODO marker in production code", why: "Placeholder values indicate unfinished work that may be deployed.", fix: "Complete the implementation or remove the placeholder.", tags: ["placeholder","todo"], auto: false },
|
|
313
|
+
FAKE005: { desc: "Auth bypass — hardcoded admin or skipped auth", why: "Authentication bypasses let anyone access protected resources.", fix: "Remove hardcoded bypasses. Implement proper auth checks.", tags: ["auth-bypass","security"], auto: false },
|
|
314
|
+
FAKE006: { desc: "Dangerous default — secret env var with insecure fallback", why: "If the env var is missing, the app runs with an insecure default value.", fix: "Throw on missing required secrets. Never provide insecure defaults.", tags: ["dangerous-default"], auto: false },
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
function scanFakeFeatures(files, opts) {
|
|
318
|
+
const findings = [];
|
|
319
|
+
for (const [, file] of files) {
|
|
320
|
+
if (file.classification.category === "test" && !opts.includeTests) continue;
|
|
321
|
+
if (file.classification.category === "documentation") continue;
|
|
322
|
+
const isCrit = file.classification.isCriticalPath;
|
|
323
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
324
|
+
const line = file.lines[i], trimmed = line.trim();
|
|
325
|
+
const isComment = /^\s*(?:\/\/|\/\*|\*|#)/.test(trimmed);
|
|
326
|
+
|
|
327
|
+
// Placeholders (including TODO comments)
|
|
328
|
+
if (!isComment || /\b(?:TODO|FIXME|WIP)\b/i.test(trimmed)) {
|
|
329
|
+
if (!/\.(md|mdx|rst|txt)$/.test(file.path) && !file.path.includes("/docs/")) {
|
|
330
|
+
for (const re of PLACEHOLDER_RE) {
|
|
331
|
+
if (re.test(line)) { findings.push(makeFake("FAKE004", file, i, trimmed, "medium", 65, isCrit)); break; }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (isComment) continue;
|
|
336
|
+
|
|
337
|
+
// Fake success
|
|
338
|
+
for (const re of FAKE_SUCCESS_RE) {
|
|
339
|
+
if (re.test(line) && !file.path.includes(".test.") && !file.path.includes(".spec.")) {
|
|
340
|
+
findings.push(makeFake("FAKE002", file, i, trimmed, "medium", 65, isCrit)); break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Silent failures
|
|
344
|
+
for (const re of SILENT_FAIL_RE) {
|
|
345
|
+
if (re.test(line) && !line.toLowerCase().includes("finally")) {
|
|
346
|
+
findings.push(makeFake("FAKE003", file, i, trimmed, "high", 85, isCrit)); break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Auth bypass
|
|
350
|
+
for (const re of AUTH_BYPASS_RE) {
|
|
351
|
+
if (re.test(line) && !file.path.includes(".config.") && !file.path.includes(".env")) {
|
|
352
|
+
findings.push(makeFake("FAKE005", file, i, trimmed, "critical", 88, isCrit)); break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Dangerous defaults
|
|
356
|
+
for (const re of DANGER_DEFAULT_RE) {
|
|
357
|
+
if (re.test(line) && !file.path.includes(".example") && !file.path.includes(".template")) {
|
|
358
|
+
findings.push(makeFake("FAKE006", file, i, trimmed, "high", 82, isCrit)); break;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Empty functions
|
|
362
|
+
for (const re of EMPTY_FN_RE) {
|
|
363
|
+
if (re.test(line) && !/noop|passthrough|abstract|interface|override/i.test(line)) {
|
|
364
|
+
findings.push(makeFake("FAKE001", file, i, trimmed, "high", 75, isCrit)); break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return findings;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function makeFake(ruleId, file, idx, code, sev, conf, isCrit) {
|
|
373
|
+
const r = FAKE_RULES[ruleId];
|
|
374
|
+
return {
|
|
375
|
+
id: `${ruleId}-${file.path}-${idx+1}`, ruleId, engine: "fake-features",
|
|
376
|
+
category: "fake-features", severity: escalateSev(sev, isCrit),
|
|
377
|
+
confidence: conf >= 85 ? "certain" : conf >= 65 ? "likely" : "possible",
|
|
378
|
+
confidenceScore: conf, file: file.path, line: idx+1, code,
|
|
379
|
+
message: r.desc, why: r.why, fix: r.fix, autoFixable: r.auto,
|
|
380
|
+
tags: [...r.tags], verified: false,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
385
|
+
// ENGINE: SECURITY (15 patterns)
|
|
386
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
387
|
+
|
|
388
|
+
const SEC_PATTERNS = [
|
|
389
|
+
{ n: "sql-inject-tpl", id: "SEC001", re: /(?:query|execute|raw)\s*\(\s*[`'"](?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\s[^`'"]*\$\{/i, sev: "critical", msg: "SQL injection: template literal in query", fix: "Use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId])", conf: 92, cwe: "CWE-89" },
|
|
390
|
+
{ n: "sql-inject-concat", id: "SEC001", re: /(?:query|execute|raw)\s*\(\s*['"](?:SELECT|INSERT|UPDATE|DELETE)\s[^'"]*['"]\s*\+/i, sev: "critical", msg: "SQL injection: string concatenation in query", fix: "Use parameterized queries. Never concatenate user input into SQL.", conf: 90, cwe: "CWE-89" },
|
|
391
|
+
{ n: "unfiltered-delete", id: "SEC011", re: /DELETE\s+FROM\s+\w+\s*;?\s*$/im, sev: "critical", msg: "Unfiltered DELETE (no WHERE clause)", fix: "Add WHERE clause. Add confirmation for destructive ops.", conf: 85, cwe: "CWE-89" },
|
|
392
|
+
{ n: "exec-tpl", id: "SEC002", re: /(?:exec|execSync|spawn|spawnSync)\s*\(\s*`[^`]*\$\{/, sev: "critical", msg: "Command injection: template literal in shell command", fix: "Use execFile() with args array", conf: 95, cwe: "CWE-78" },
|
|
393
|
+
{ n: "exec-concat", id: "SEC002", re: /(?:exec|execSync)\s*\(\s*['"][^'"]*['"]\s*\+/, sev: "critical", msg: "Command injection: concatenation in shell command", fix: "Use execFile() with args array", conf: 93, cwe: "CWE-78" },
|
|
394
|
+
{ n: "innerHTML", id: "SEC003", re: /\.innerHTML\s*=/, sev: "high", msg: "innerHTML assignment — potential XSS", fix: "Use textContent for text. Use DOMPurify.sanitize() for HTML.", conf: 75, cwe: "CWE-79" },
|
|
395
|
+
{ n: "dangerouslySet", id: "SEC003", re: /dangerouslySetInnerHTML/, sev: "high", msg: "dangerouslySetInnerHTML — ensure content is sanitized", fix: "Sanitize with DOMPurify before passing.", conf: 70, cwe: "CWE-79" },
|
|
396
|
+
{ n: "document-write", id: "SEC003", re: /document\.write\s*\(/, sev: "high", msg: "document.write() can overwrite page with unescaped content", fix: "Use DOM manipulation methods instead.", conf: 80, cwe: "CWE-79" },
|
|
397
|
+
{ n: "path-traversal", id: "SEC004", re: /(?:path\.join|path\.resolve)\s*\([^)]*(?:req\.|params\.|query\.|body\.)/, sev: "high", msg: "User input in file path — potential path traversal", fix: "Validate/sanitize file paths. Use path.normalize() and check for '..'.", conf: 82, cwe: "CWE-22" },
|
|
398
|
+
{ n: "eval", id: "SEC005", re: /\beval\s*\(/, sev: "critical", msg: "eval() executes arbitrary code", fix: "Replace with JSON.parse(), Function constructor, or safer alternatives.", conf: 88, cwe: "CWE-95" },
|
|
399
|
+
{ n: "cors-star", id: "SEC006", re: /(?:Access-Control-Allow-Origin|cors)\s*[=:]\s*['"`]\s*\*\s*['"`]/, sev: "high", msg: "CORS wildcard allows any origin", fix: "Restrict to specific trusted origins.", conf: 85, cwe: "CWE-942" },
|
|
400
|
+
{ n: "no-https", id: "SEC007", re: /http:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)[\w.-]+\.\w+/, sev: "medium", msg: "Non-HTTPS URL in production code", fix: "Use HTTPS for all external endpoints.", conf: 60 },
|
|
401
|
+
{ n: "disable-ssl", id: "SEC007", re: /rejectUnauthorized\s*:\s*false/, sev: "critical", msg: "TLS verification disabled — MITM attack risk", fix: "Enable TLS verification. Fix certificates instead of disabling.", conf: 95, cwe: "CWE-295" },
|
|
402
|
+
{ n: "debug-true", id: "SEC008", re: /(?:debug|DEBUG)\s*[:=]\s*true\b/, sev: "medium", msg: "Debug mode enabled — may leak sensitive info", fix: "Use env-based debug flag. Disable in production.", conf: 55 },
|
|
403
|
+
{ n: "no-rate-limit", id: "SEC010", re: /\/\/\s*(?:TODO|FIXME).*rate.?limit/i, sev: "high", msg: "Missing rate limiting (noted in TODO)", fix: "Implement rate limiting for public endpoints.", conf: 80, cwe: "CWE-770" },
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
function scanSecurity(files, opts) {
|
|
407
|
+
const findings = [];
|
|
408
|
+
for (const [, file] of files) {
|
|
409
|
+
if (file.classification.category === "test" && !opts.includeTests) continue;
|
|
410
|
+
if (file.classification.category === "documentation") continue;
|
|
411
|
+
const isCrit = file.classification.isCriticalPath;
|
|
412
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
413
|
+
const line = file.lines[i], trimmed = line.trim();
|
|
414
|
+
if (/^\s*(?:\/\/|\/\*|\*)/.test(trimmed) && !/TODO|FIXME/i.test(trimmed)) continue;
|
|
415
|
+
for (const p of SEC_PATTERNS) {
|
|
416
|
+
if (!p.re.test(line)) continue;
|
|
417
|
+
findings.push({
|
|
418
|
+
id: `${p.id}-${file.path}-${i+1}`, ruleId: p.id, engine: "security",
|
|
419
|
+
category: "security", severity: escalateSev(p.sev, isCrit),
|
|
420
|
+
confidence: p.conf >= 85 ? "certain" : p.conf >= 65 ? "likely" : "possible",
|
|
421
|
+
confidenceScore: p.conf, file: file.path, line: i+1, code: trimmed,
|
|
422
|
+
message: p.msg, why: "Security vulnerabilities can be exploited to compromise your application.", fix: p.fix,
|
|
423
|
+
autoFixable: false, tags: ["security"], cwe: p.cwe, verified: false,
|
|
424
|
+
});
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return findings;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
433
|
+
// DEDUPLICATION
|
|
434
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
435
|
+
|
|
436
|
+
function dedup(findings) {
|
|
437
|
+
const groups = new Map();
|
|
438
|
+
for (const f of findings) {
|
|
439
|
+
const key = `${f.file}:${f.line}`;
|
|
440
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
441
|
+
groups.get(key).push(f);
|
|
442
|
+
}
|
|
443
|
+
const out = [];
|
|
444
|
+
let suppressed = 0;
|
|
445
|
+
for (const [, group] of groups) {
|
|
446
|
+
if (group.length === 1) { out.push(group[0]); continue; }
|
|
447
|
+
const subs = new Map();
|
|
448
|
+
for (const f of group) {
|
|
449
|
+
if (!subs.has(f.category)) subs.set(f.category, []);
|
|
450
|
+
subs.get(f.category).push(f);
|
|
451
|
+
}
|
|
452
|
+
for (const [, sub] of subs) {
|
|
453
|
+
if (sub.length === 1) { out.push(sub[0]); continue; }
|
|
454
|
+
sub.sort((a, b) => (b.confidenceScore - a.confidenceScore) || ((SEV_ORDER[b.severity]||0) - (SEV_ORDER[a.severity]||0)));
|
|
455
|
+
const best = { ...sub[0] };
|
|
456
|
+
const engines = new Set(sub.map(f => f.engine));
|
|
457
|
+
if (engines.size >= 2) {
|
|
458
|
+
best.verified = true;
|
|
459
|
+
best.confidenceScore = Math.min(99, best.confidenceScore + 5);
|
|
460
|
+
}
|
|
461
|
+
out.push(best);
|
|
462
|
+
suppressed += sub.length - 1;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
out.sort((a, b) => ((SEV_ORDER[b.severity]||0) - (SEV_ORDER[a.severity]||0)) || (b.confidenceScore - a.confidenceScore));
|
|
466
|
+
return { findings: out, suppressed };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
470
|
+
// HEALTH SCORE
|
|
471
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
472
|
+
|
|
473
|
+
function healthScore(findings) {
|
|
474
|
+
let score = 100;
|
|
475
|
+
for (const f of findings) {
|
|
476
|
+
switch (f.severity) { case "critical": score -= 15; break; case "high": score -= 8; break; case "medium": score -= 3; break; case "low": score -= 1; break; }
|
|
477
|
+
if (f.category === "hallucinations") score -= 3;
|
|
478
|
+
if (f.ruleId === "FAKE005") score -= 5;
|
|
479
|
+
}
|
|
480
|
+
return Math.max(0, Math.min(100, score));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
484
|
+
// INLINE SCAN (lite mode — 3 engines, no build required)
|
|
485
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
486
|
+
|
|
487
|
+
async function inlineScan(projectPath, opts) {
|
|
488
|
+
const startTime = Date.now();
|
|
489
|
+
const scanId = `scan-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
490
|
+
|
|
491
|
+
const files = loadFiles(projectPath, opts);
|
|
492
|
+
const engineResults = [];
|
|
493
|
+
let allFindings = [];
|
|
494
|
+
|
|
495
|
+
// Map category to engine filter
|
|
496
|
+
const catMap = {
|
|
497
|
+
secrets: ["credentials"], credentials: ["credentials"],
|
|
498
|
+
security: ["security"], auth: ["fake-features", "security"],
|
|
499
|
+
"fake-features": ["fake-features"], env: ["fake-features"],
|
|
500
|
+
routes: ["fake-features"], vulns: [],
|
|
501
|
+
};
|
|
502
|
+
const allowedEngines = opts.category && catMap[opts.category] ? catMap[opts.category] : null;
|
|
503
|
+
|
|
504
|
+
const engines = [
|
|
505
|
+
{ name: "credentials", fn: () => scanCredentials(files) },
|
|
506
|
+
{ name: "fake-features", fn: () => scanFakeFeatures(files, opts) },
|
|
507
|
+
{ name: "security", fn: () => scanSecurity(files, opts) },
|
|
508
|
+
];
|
|
509
|
+
|
|
510
|
+
for (const eng of engines) {
|
|
511
|
+
if (allowedEngines && !allowedEngines.includes(eng.name)) continue;
|
|
512
|
+
if (opts.engines && !opts.engines.includes(eng.name)) continue;
|
|
513
|
+
const t = Date.now();
|
|
514
|
+
try {
|
|
515
|
+
const f = await eng.fn();
|
|
516
|
+
engineResults.push({ engine: eng.name, findings: f.length, durationMs: Date.now() - t, success: true });
|
|
517
|
+
allFindings.push(...f);
|
|
518
|
+
} catch (e) {
|
|
519
|
+
engineResults.push({ engine: eng.name, findings: 0, durationMs: Date.now() - t, success: false, error: e.message });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const { findings: deduplicated, suppressed } = dedup(allFindings);
|
|
524
|
+
|
|
525
|
+
// Severity filter
|
|
526
|
+
const minSev = SEV_ORDER[opts.severity] || 1;
|
|
527
|
+
const filtered = deduplicated.filter(f => (SEV_ORDER[f.severity] || 0) >= minSev);
|
|
528
|
+
|
|
529
|
+
const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
530
|
+
const byCategory = {}, byEngine = {};
|
|
531
|
+
for (const f of filtered) {
|
|
532
|
+
bySeverity[f.severity]++;
|
|
533
|
+
byCategory[f.category] = (byCategory[f.category] || 0) + 1;
|
|
534
|
+
byEngine[f.engine] = (byEngine[f.engine] || 0) + 1;
|
|
535
|
+
}
|
|
536
|
+
const dur = Date.now() - startTime;
|
|
537
|
+
const timings = {};
|
|
538
|
+
for (const r of engineResults) timings[r.engine] = r.durationMs;
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
scanId, timestamp: new Date().toISOString(), findings: filtered,
|
|
542
|
+
summary: { totalFiles: files.size, filesScanned: files.size, totalFindings: filtered.length, bySeverity, byCategory, byEngine, autoFixable: filtered.filter(f => f.autoFixable).length, suppressedDuplicates: suppressed },
|
|
543
|
+
healthScore: healthScore(filtered), engineResults,
|
|
544
|
+
metrics: { durationMs: dur, filesPerSecond: files.size > 0 ? Math.round((files.size / dur) * 1000) : 0, engineTimings: timings },
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
549
|
+
// PRETTY REPORT
|
|
550
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
551
|
+
|
|
552
|
+
function getHealthBar(score) {
|
|
553
|
+
const filled = Math.round(score / 5), empty = 20 - filled;
|
|
554
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
555
|
+
return score >= 80 ? `${c.green}${bar}${c.reset}` : score >= 50 ? `${c.yellow}${bar}${c.reset}` : `${c.red}${bar}${c.reset}`;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function printReport(report, mode) {
|
|
559
|
+
const { summary, findings, healthScore: hs, metrics, engineResults } = report;
|
|
560
|
+
console.log("");
|
|
561
|
+
console.log(`${c.magenta}${c.bold} VIBECHECK SCAN${c.reset} ${c.dim}${mode}${c.reset}`);
|
|
562
|
+
console.log(`${c.dim} ${engineResults.length} engines \u00b7 ${summary.totalFiles} files \u00b7 ${metrics.durationMs}ms${c.reset}`);
|
|
563
|
+
console.log("");
|
|
564
|
+
const sc = hs >= 80 ? c.green : hs >= 50 ? c.yellow : c.red;
|
|
565
|
+
console.log(` ${c.bold}Health Score:${c.reset} ${getHealthBar(hs)} ${sc}${hs}%${c.reset}`);
|
|
566
|
+
console.log("");
|
|
567
|
+
console.log(` ${c.bold}Files scanned:${c.reset} ${summary.filesScanned}`);
|
|
568
|
+
console.log(` ${c.bold}Total findings:${c.reset} ${summary.totalFindings}${summary.suppressedDuplicates > 0 ? `${c.dim} (${summary.suppressedDuplicates} dupes suppressed)${c.reset}` : ""}`);
|
|
569
|
+
console.log(` ${c.bold}Auto-fixable:${c.reset} ${summary.autoFixable}`);
|
|
570
|
+
console.log("");
|
|
571
|
+
if (summary.bySeverity.critical > 0) console.log(` ${SEV_ICON.critical} ${c.red}${summary.bySeverity.critical} critical${c.reset}`);
|
|
572
|
+
if (summary.bySeverity.high > 0) console.log(` ${SEV_ICON.high} ${c.yellow}${summary.bySeverity.high} high${c.reset}`);
|
|
573
|
+
if (summary.bySeverity.medium > 0) console.log(` ${SEV_ICON.medium} ${c.cyan}${summary.bySeverity.medium} medium${c.reset}`);
|
|
574
|
+
if (summary.bySeverity.low > 0) console.log(` ${SEV_ICON.low} ${c.dim}${summary.bySeverity.low} low${c.reset}`);
|
|
575
|
+
console.log("");
|
|
576
|
+
console.log(`${c.dim} \u2500\u2500\u2500 Engine Performance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
577
|
+
for (const er of engineResults) {
|
|
578
|
+
const st = er.success ? `${c.green}\u2713${c.reset}` : `${c.red}\u2717${c.reset}`;
|
|
579
|
+
const fc = er.findings > 0 ? `${c.yellow} ${er.findings} findings${c.reset}` : `${c.dim} 0 findings${c.reset}`;
|
|
580
|
+
console.log(` ${st} ${er.engine.padEnd(16)}${String(er.durationMs).padStart(5)}ms${fc}`);
|
|
581
|
+
}
|
|
582
|
+
console.log(` ${c.dim} Total: ${metrics.durationMs}ms (${metrics.filesPerSecond} files/sec)${c.reset}`);
|
|
583
|
+
console.log("");
|
|
584
|
+
if (findings.length > 0) {
|
|
585
|
+
console.log(`${c.bold} \u2500\u2500\u2500 Findings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
586
|
+
console.log("");
|
|
587
|
+
const byFile = new Map();
|
|
588
|
+
for (const f of findings) { if (!byFile.has(f.file)) byFile.set(f.file, []); byFile.get(f.file).push(f); }
|
|
589
|
+
for (const [file, ff] of byFile) {
|
|
590
|
+
console.log(` ${c.bold}${c.white}${file}${c.reset}`);
|
|
591
|
+
for (const f of ff) {
|
|
592
|
+
console.log(` ${SEV_ICON[f.severity]||"\u25cf"} ${SEV_LABEL[f.severity]||f.severity} ${c.dim}[${f.ruleId}]${c.reset} ${f.message} ${c.dim}:${f.line}${c.reset}`);
|
|
593
|
+
console.log(` ${c.dim}\u2192${c.reset} ${f.fix}`);
|
|
594
|
+
if (f.verified) console.log(` ${c.green}\u2713 Verified by multiple engines${c.reset}`);
|
|
595
|
+
}
|
|
596
|
+
console.log("");
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
console.log(`${c.green} \u2713 No issues found. Ship it!${c.reset}`);
|
|
600
|
+
console.log("");
|
|
601
|
+
}
|
|
602
|
+
const verdict = hs >= 80 ? `${c.green}${c.bold}SHIP \u2713${c.reset}` : hs >= 50 ? `${c.yellow}${c.bold}WARN \u26a0${c.reset}` : `${c.red}${c.bold}BLOCK \u2717${c.reset}`;
|
|
603
|
+
console.log(` ${c.bold}Verdict:${c.reset} ${verdict}`);
|
|
604
|
+
if (mode && mode.includes("lite")) {
|
|
605
|
+
console.log(`\n ${c.dim}Lite mode (3 engines). Run 'pnpm build' to unlock all 8 engines (160+ patterns).${c.reset}`);
|
|
606
|
+
}
|
|
607
|
+
console.log("");
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function toSarif(report) {
|
|
611
|
+
return {
|
|
612
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
613
|
+
version: "2.1.0",
|
|
614
|
+
runs: [{ tool: { driver: { name: "VibeCheck Ultimate Scanner", version: "5.0.0", informationUri: "https://vibecheckai.dev",
|
|
615
|
+
rules: report.findings.map(f => ({ id: f.ruleId, shortDescription: { text: f.message }, help: { text: f.fix } })) } },
|
|
616
|
+
results: report.findings.map(f => ({ ruleId: f.ruleId, level: (f.severity === "critical" || f.severity === "high") ? "error" : "warning",
|
|
617
|
+
message: { text: f.message }, locations: [{ physicalLocation: { artifactLocation: { uri: f.file }, region: { startLine: f.line } } }] })) }],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
622
|
+
// MAIN RUNNER
|
|
623
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
624
|
+
|
|
625
|
+
async function runScan(args, context = {}) {
|
|
626
|
+
const opts = parseArgs(args);
|
|
627
|
+
if (opts.help) { printHelp(); return 0; }
|
|
628
|
+
const projectPath = path.resolve(opts.path);
|
|
629
|
+
|
|
630
|
+
// Try compiled 8-engine scanner first
|
|
631
|
+
const scanner = getScanner();
|
|
632
|
+
let report;
|
|
633
|
+
let mode;
|
|
634
|
+
|
|
635
|
+
if (scanner) {
|
|
636
|
+
mode = "8 engines \u00b7 160+ patterns";
|
|
637
|
+
if (!opts.quiet && !opts.json && !opts.sarif) console.log(`${c.dim} Scanning ${projectPath}...${c.reset}`);
|
|
638
|
+
const scanOpts = {
|
|
639
|
+
projectRoot: projectPath, severityThreshold: opts.severity,
|
|
640
|
+
engines: opts.engines || undefined, includeTests: opts.includeTests,
|
|
641
|
+
parallel: opts.parallel,
|
|
642
|
+
onProgress: (opts.quiet || opts.json || opts.sarif) ? undefined : (p) => {
|
|
643
|
+
if (p.phase === "loading") process.stdout.write(`${c.dim}\r Loading files...${c.reset}`);
|
|
644
|
+
if (p.phase === "scanning") process.stdout.write(`${c.dim}\r Running engines...${c.reset}`);
|
|
645
|
+
if (p.phase === "complete") process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
report = await scanner.scan(scanOpts);
|
|
649
|
+
} else {
|
|
650
|
+
mode = "3 engines \u00b7 52 patterns (lite)";
|
|
651
|
+
if (!opts.quiet && !opts.json && !opts.sarif) console.log(`${c.dim} Scanning ${projectPath}...${c.reset}`);
|
|
652
|
+
report = await inlineScan(projectPath, opts);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Output
|
|
656
|
+
if (opts.format === "json" || opts.json) {
|
|
657
|
+
console.log(JSON.stringify(report, null, 2));
|
|
658
|
+
} else if (opts.format === "sarif" || opts.sarif) {
|
|
659
|
+
console.log(JSON.stringify(toSarif(report), null, 2));
|
|
660
|
+
} else {
|
|
661
|
+
printReport(report, mode);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Save to output dir
|
|
665
|
+
if (opts.output) {
|
|
666
|
+
const outDir = path.resolve(opts.output);
|
|
667
|
+
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
668
|
+
fs.writeFileSync(path.join(outDir, "scan-report.json"), JSON.stringify(report, null, 2));
|
|
669
|
+
if (!opts.quiet) console.log(`${c.dim} Report saved to ${outDir}/scan-report.json${c.reset}`);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Exit code
|
|
673
|
+
if (opts.failOn) {
|
|
674
|
+
const threshold = SEV_ORDER[opts.failOn] || 0;
|
|
675
|
+
for (const f of report.findings) { if ((SEV_ORDER[f.severity] || 0) >= threshold) return EXIT.FINDINGS || 1; }
|
|
676
|
+
}
|
|
677
|
+
if (report.summary.bySeverity.critical > 0) return 2;
|
|
678
|
+
if (report.summary.bySeverity.high > 0) return 1;
|
|
679
|
+
return 0;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
683
|
+
// EXPORTS
|
|
684
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
685
|
+
|
|
686
|
+
module.exports = {
|
|
687
|
+
runScan: withErrorHandling(runScan, "Scan failed"),
|
|
688
|
+
};
|