@vibecheckai/cli 3.2.6 → 3.4.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.
Files changed (89) hide show
  1. package/bin/registry.js +306 -90
  2. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  3. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  4. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  5. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  6. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  7. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  8. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  11. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  12. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  14. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  15. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  16. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  17. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  18. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  19. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  20. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  21. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  22. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  23. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  24. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  25. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  26. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  27. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  28. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  29. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  30. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  31. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  32. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  35. package/bin/runners/lib/analyzers.js +136 -141
  36. package/bin/runners/lib/authority-badge.js +425 -0
  37. package/bin/runners/lib/cli-output.js +7 -1
  38. package/bin/runners/lib/entitlements-v2.js +96 -505
  39. package/bin/runners/lib/error-handler.js +16 -9
  40. package/bin/runners/lib/exit-codes.js +275 -0
  41. package/bin/runners/lib/global-flags.js +37 -0
  42. package/bin/runners/lib/help-formatter.js +413 -0
  43. package/bin/runners/lib/logger.js +38 -0
  44. package/bin/runners/lib/scan-output.js +18 -19
  45. package/bin/runners/lib/ship-output.js +18 -25
  46. package/bin/runners/lib/unified-cli-output.js +604 -0
  47. package/bin/runners/lib/upsell.js +105 -205
  48. package/bin/runners/runApprove.js +1200 -0
  49. package/bin/runners/runAuth.js +324 -95
  50. package/bin/runners/runCheckpoint.js +39 -21
  51. package/bin/runners/runClassify.js +859 -0
  52. package/bin/runners/runContext.js +136 -24
  53. package/bin/runners/runDoctor.js +108 -68
  54. package/bin/runners/runFix.js +6 -5
  55. package/bin/runners/runGuard.js +212 -118
  56. package/bin/runners/runInit.js +3 -2
  57. package/bin/runners/runMcp.js +130 -52
  58. package/bin/runners/runPolish.js +43 -20
  59. package/bin/runners/runProve.js +1 -2
  60. package/bin/runners/runReport.js +3 -2
  61. package/bin/runners/runScan.js +77 -45
  62. package/bin/runners/runShip.js +3 -4
  63. package/bin/runners/runValidate.js +19 -2
  64. package/bin/runners/runWatch.js +104 -53
  65. package/bin/vibecheck.js +103 -21
  66. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  67. package/mcp-server/agent-firewall-interceptor.js +367 -31
  68. package/mcp-server/authority-tools.js +569 -0
  69. package/mcp-server/conductor/conflict-resolver.js +588 -0
  70. package/mcp-server/conductor/execution-planner.js +544 -0
  71. package/mcp-server/conductor/index.js +377 -0
  72. package/mcp-server/conductor/lock-manager.js +615 -0
  73. package/mcp-server/conductor/request-queue.js +550 -0
  74. package/mcp-server/conductor/session-manager.js +500 -0
  75. package/mcp-server/conductor/tools.js +510 -0
  76. package/mcp-server/index.js +1152 -856
  77. package/mcp-server/lib/api-client.cjs +13 -0
  78. package/mcp-server/lib/logger.cjs +30 -0
  79. package/mcp-server/logger.js +173 -0
  80. package/mcp-server/package.json +2 -2
  81. package/mcp-server/premium-tools.js +2 -2
  82. package/mcp-server/tier-auth.js +194 -383
  83. package/mcp-server/tools-v3.js +495 -533
  84. package/mcp-server/truth-firewall-tools.js +145 -15
  85. package/mcp-server/vibecheck-tools.js +2 -2
  86. package/package.json +2 -3
  87. package/mcp-server/index.old.js +0 -4137
  88. package/mcp-server/lib/api-client.js +0 -269
  89. package/mcp-server/package-lock.json +0 -165
@@ -570,8 +570,47 @@ export function enforceClaimResult(result, policy = "strict") {
570
570
  return { allowed: true, confidence: derived };
571
571
  }
572
572
 
573
+ // =============================================================================
574
+ // CLAIM VALIDATION WITH RACE CONDITION PROTECTION
575
+ //
576
+ // SECURITY FIX: Previous implementation had a TOCTOU race condition:
577
+ // 1. Thread A: hasRecentClaimValidation() returns true
578
+ // 2. Thread B: invalidates the claim (file change, etc.)
579
+ // 3. Thread A: proceeds with stale claim → invalid state
580
+ //
581
+ // New implementation uses atomic check-and-consume pattern with per-project locks.
582
+ // =============================================================================
583
+
584
+ /**
585
+ * Per-project validation locks to prevent concurrent operations
586
+ * from using the same validation state.
587
+ */
588
+ const validationLocks = new Map(); // Map<projectPath, { locked: boolean, queue: Promise }>
589
+
590
+ /**
591
+ * Acquire a validation lock for a project (serializes validation checks).
592
+ */
593
+ function acquireValidationLock(projectPath) {
594
+ let lockState = validationLocks.get(projectPath);
595
+ if (!lockState) {
596
+ lockState = { locked: false, queue: Promise.resolve() };
597
+ validationLocks.set(projectPath, lockState);
598
+ }
599
+
600
+ const acquirePromise = lockState.queue.then(() => {
601
+ lockState.locked = true;
602
+ return () => {
603
+ lockState.locked = false;
604
+ };
605
+ });
606
+
607
+ lockState.queue = acquirePromise.catch(() => {});
608
+ return acquirePromise;
609
+ }
610
+
573
611
  /**
574
- * Claim validation freshness per policy TTL.
612
+ * Check claim validation freshness (basic check, no lock).
613
+ * Use checkAndConsumeClaimValidation for atomic operations.
575
614
  */
576
615
  export function hasRecentClaimValidation(projectPath, policy = "strict") {
577
616
  const last = state.lastValidationByProject.get(projectPath);
@@ -580,6 +619,56 @@ export function hasRecentClaimValidation(projectPath, policy = "strict") {
580
619
  return Date.now() - last <= ttl;
581
620
  }
582
621
 
622
+ /**
623
+ * Atomic check-and-consume claim validation.
624
+ *
625
+ * SECURITY: Use this for operations that depend on claim validation.
626
+ * It ensures no other operation can use the same validation state concurrently.
627
+ *
628
+ * @param {string} projectPath - Project path
629
+ * @param {string} policy - Policy name (strict/balanced/permissive)
630
+ * @param {string} operationId - Unique ID for this operation (for audit)
631
+ * @returns {Promise<{ valid: boolean, consumedAt?: number, reason?: string }>}
632
+ */
633
+ export async function checkAndConsumeClaimValidation(projectPath, policy = "strict", operationId = null) {
634
+ const release = await acquireValidationLock(projectPath);
635
+
636
+ try {
637
+ const last = state.lastValidationByProject.get(projectPath);
638
+ const now = Date.now();
639
+
640
+ if (typeof last !== "number") {
641
+ return {
642
+ valid: false,
643
+ reason: "No claim validation found for this project"
644
+ };
645
+ }
646
+
647
+ const ttl = getPolicyConfig(policy).validationTTL;
648
+ const age = now - last;
649
+
650
+ if (age > ttl) {
651
+ return {
652
+ valid: false,
653
+ reason: `Claim validation expired (age: ${Math.round(age / 1000)}s, TTL: ${Math.round(ttl / 1000)}s)`
654
+ };
655
+ }
656
+
657
+ // Mark this validation as consumed by updating the timestamp
658
+ // This prevents replay/reuse of the same validation
659
+ state.lastValidationByProject.set(projectPath, now);
660
+
661
+ return {
662
+ valid: true,
663
+ consumedAt: now,
664
+ operationId,
665
+ };
666
+
667
+ } finally {
668
+ release();
669
+ }
670
+ }
671
+
583
672
  // =============================================================================
584
673
  // FINGERPRINT + WRAPPER
585
674
  // =============================================================================
@@ -1069,22 +1158,63 @@ async function proposePatch(projectPath, args) {
1069
1158
  return patch;
1070
1159
  }
1071
1160
 
1161
+ /**
1162
+ * Validate command against strict allowlist.
1163
+ *
1164
+ * SECURITY FIX: Previous allowlist was too permissive, allowing arbitrary code execution:
1165
+ * - "node -e 'require(\"child_process\").exec(\"rm -rf /\")'" would pass
1166
+ * - "npm exec malicious-package" would pass
1167
+ * - "pnpm dlx evil-tool" would pass
1168
+ *
1169
+ * New allowlist only permits specific safe subcommands.
1170
+ */
1072
1171
  function commandAllowlisted(cmd) {
1073
- // Keep this tight. Expand only if you mean it.
1074
- const allow = [
1075
- /^vibecheck(\s|$)/,
1076
- /^pnpm(\s|$)/,
1077
- /^npm(\s|$)/,
1078
- /^yarn(\s|$)/,
1079
- /^node(\s|$)/,
1080
- /^bun(\s|$)/,
1081
- /^vitest(\s|$)/,
1082
- /^jest(\s|$)/,
1083
- /^tsc(\s|$)/,
1084
- /^eslint(\s|$)/,
1085
- /^playwright(\s|$)/,
1172
+ const trimmed = cmd.trim();
1173
+
1174
+ // Reject commands with shell metacharacters that could enable injection
1175
+ // These are dangerous even in "safe" commands: ; | & $ ` \ ( ) { } < > \n
1176
+ if (/[;|&$`\\(){}<>\n]/.test(trimmed)) {
1177
+ return false;
1178
+ }
1179
+
1180
+ // Reject commands that use flags commonly used for code execution
1181
+ if (/\s-[eEc]\s|\s--eval\s|\s--exec\s/i.test(trimmed)) {
1182
+ return false;
1183
+ }
1184
+
1185
+ // Strict allowlist: only specific commands with specific safe subcommands
1186
+ const strictAllow = [
1187
+ // Vibecheck CLI - only specific safe commands
1188
+ /^vibecheck\s+(ship|scan|ctx|lint|status)\b/,
1189
+ /^vibecheck\s+--help\b/,
1190
+ /^vibecheck\s+--version\b/,
1191
+
1192
+ // Package managers - only test/build/lint (no exec, dlx, or install scripts)
1193
+ /^pnpm\s+(test|build|lint|typecheck|check|run\s+(test|build|lint|typecheck))\b/,
1194
+ /^npm\s+(test|run\s+(test|build|lint|typecheck))\b/,
1195
+ /^yarn\s+(test|build|lint|typecheck|run\s+(test|build|lint|typecheck))\b/,
1196
+ /^bun\s+(test|run\s+(test|build|lint))\b/,
1197
+
1198
+ // TypeScript compiler - only type checking (no emit)
1199
+ /^tsc\s+(--noEmit|--build)\b/,
1200
+ /^tsc$/, // Default tsc with no args is safe
1201
+
1202
+ // Linters - safe read-only operations
1203
+ /^eslint\s+/, // ESLint with any args (read-only)
1204
+ /^eslint$/,
1205
+
1206
+ // Test runners - only run tests
1207
+ /^vitest\s*(run|--run)?\b/,
1208
+ /^vitest$/,
1209
+ /^jest\s*(--ci|--coverage|--passWithNoTests)?\b/,
1210
+ /^jest$/,
1211
+
1212
+ // Playwright - only test mode (no codegen which opens browsers)
1213
+ /^playwright\s+test\b/,
1214
+ /^npx\s+playwright\s+test\b/,
1086
1215
  ];
1087
- return allow.some((re) => re.test(cmd.trim()));
1216
+
1217
+ return strictAllow.some((re) => re.test(trimmed));
1088
1218
  }
1089
1219
 
1090
1220
  async function verifyPatch(projectPath, args) {
@@ -13,7 +13,7 @@
13
13
  import path from "path";
14
14
  import fs from "fs/promises";
15
15
  import { execSync } from "child_process";
16
- import { withTierCheck, checkFeatureAccess } from "./tier-auth.js";
16
+ import { withTierCheck, getFeatureAccessStatus } from "./tier-auth.js";
17
17
 
18
18
  // ============================================================================
19
19
  // TOOL DEFINITIONS
@@ -292,7 +292,7 @@ export async function handleVibecheckTool(toolName, args) {
292
292
 
293
293
  const requiredFeature = featureMap[toolName];
294
294
  if (requiredFeature) {
295
- const access = await checkFeatureAccess(requiredFeature, args?.apiKey);
295
+ const access = await getFeatureAccessStatus(requiredFeature, args?.apiKey);
296
296
  if (!access.hasAccess) {
297
297
  return {
298
298
  content: [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecheckai/cli",
3
- "version": "3.2.6",
3
+ "version": "3.4.0",
4
4
  "description": "Vibecheck CLI - Ship with confidence. One verdict: SHIP | WARN | BLOCK.",
5
5
  "main": "bin/vibecheck.js",
6
6
  "bin": {
@@ -37,7 +37,6 @@
37
37
  "js-yaml": "^4.1.0",
38
38
  "ora": "^8.0.0",
39
39
  "uuid": "^9.0.0",
40
- "yaml": "^2.3.0",
41
40
  "zod": "^3.23.0"
42
41
  },
43
42
  "optionalDependencies": {
@@ -77,7 +76,7 @@
77
76
  "url": "https://github.com/vibecheck-oss/vibecheck/issues"
78
77
  },
79
78
  "engines": {
80
- "node": ">=18.0.0"
79
+ "node": ">=20.11"
81
80
  },
82
81
  "publishConfig": {
83
82
  "access": "public"