@vibecheckai/cli 3.5.0 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Sandbox - Path Security for MCP Server
3
+ *
4
+ * Enforces that all tool requests resolve within an allowed workspace root.
5
+ * Prevents path traversal attacks, absolute path escapes, and symlink escapes.
6
+ *
7
+ * @module mcp-server/lib/sandbox
8
+ */
9
+
10
+ import * as path from 'path';
11
+ import * as fs from 'fs';
12
+
13
+ /**
14
+ * Default excluded directory patterns.
15
+ * These are enforced at the MCP level even if CLI forgets.
16
+ */
17
+ export const DEFAULT_EXCLUSIONS = [
18
+ 'node_modules/**',
19
+ 'venv/**',
20
+ '.venv/**',
21
+ 'dist/**',
22
+ 'build/**',
23
+ '.next/**',
24
+ 'out/**',
25
+ 'coverage/**',
26
+ '.git/**',
27
+ '.cache/**',
28
+ '.turbo/**',
29
+ ] as const;
30
+
31
+ /**
32
+ * Third-party directories (can be included via config)
33
+ */
34
+ export const THIRD_PARTY_DIRS = ['node_modules/**', 'venv/**', '.venv/**'] as const;
35
+
36
+ /**
37
+ * Generated/build directories (can be included via config)
38
+ */
39
+ export const GENERATED_DIRS = ['dist/**', 'build/**', '.next/**', 'out/**'] as const;
40
+
41
+ /**
42
+ * Configuration options for sandbox path resolution
43
+ */
44
+ export interface SandboxConfig {
45
+ /** Workspace root directory (absolute path) */
46
+ workspaceRoot: string;
47
+ /** Include third-party directories (node_modules, venv, etc.) */
48
+ includeThirdParty?: boolean;
49
+ /** Include generated/build directories (dist, build, .next, out) */
50
+ includeGenerated?: boolean;
51
+ /** Additional paths to exclude (glob patterns) */
52
+ additionalExclusions?: string[];
53
+ /** Paths to explicitly allow (overrides exclusions) */
54
+ allowlist?: string[];
55
+ }
56
+
57
+ /**
58
+ * Result of a sandbox path validation
59
+ */
60
+ export interface SandboxResult {
61
+ /** Whether the path is valid and safe */
62
+ valid: boolean;
63
+ /** The resolved absolute path (if valid) */
64
+ resolvedPath?: string;
65
+ /** Error message (if invalid) */
66
+ error?: string;
67
+ /** Error code for programmatic handling */
68
+ errorCode?:
69
+ | 'TRAVERSAL_DETECTED'
70
+ | 'ABSOLUTE_PATH_OUTSIDE_ROOT'
71
+ | 'SYMLINK_ESCAPE'
72
+ | 'EXCLUDED_PATH'
73
+ | 'INVALID_ROOT';
74
+ }
75
+
76
+ /**
77
+ * Checks if a path matches any of the given glob patterns.
78
+ * Simple glob matching supporting ** and * wildcards.
79
+ */
80
+ function matchesGlob(filePath: string, patterns: readonly string[]): boolean {
81
+ const normalizedPath = filePath.replace(/\\/g, '/');
82
+
83
+ for (const pattern of patterns) {
84
+ const normalizedPattern = pattern.replace(/\\/g, '/');
85
+
86
+ // Convert glob pattern to regex
87
+ const regexPattern = normalizedPattern
88
+ .replace(/\*\*/g, '<<<GLOBSTAR>>>')
89
+ .replace(/\*/g, '[^/]*')
90
+ .replace(/<<<GLOBSTAR>>>/g, '.*')
91
+ .replace(/\//g, '\\/');
92
+
93
+ const regex = new RegExp(`^${regexPattern}$|^${regexPattern}\\/|\\/${regexPattern}$|\\/${regexPattern}\\/`);
94
+
95
+ if (regex.test(normalizedPath)) {
96
+ return true;
97
+ }
98
+
99
+ // Also check if the path starts with the pattern's directory
100
+ const patternDir = normalizedPattern.replace(/\/?\*\*.*$/, '');
101
+ if (patternDir && normalizedPath.startsWith(patternDir + '/')) {
102
+ return true;
103
+ }
104
+ if (patternDir && normalizedPath === patternDir) {
105
+ return true;
106
+ }
107
+ }
108
+
109
+ return false;
110
+ }
111
+
112
+ /**
113
+ * Detects path traversal attempts (../ sequences)
114
+ */
115
+ function hasTraversalSequence(inputPath: string): boolean {
116
+ const normalized = inputPath.replace(/\\/g, '/');
117
+
118
+ // Check for .. sequences
119
+ if (normalized.includes('..')) {
120
+ return true;
121
+ }
122
+
123
+ // Check for URL-encoded traversal
124
+ if (normalized.includes('%2e%2e') || normalized.includes('%2E%2E')) {
125
+ return true;
126
+ }
127
+
128
+ // Check for null byte injection
129
+ if (normalized.includes('\0') || normalized.includes('%00')) {
130
+ return true;
131
+ }
132
+
133
+ return false;
134
+ }
135
+
136
+ /**
137
+ * Checks if a path is an absolute path
138
+ */
139
+ function isAbsolutePath(inputPath: string): boolean {
140
+ // Windows absolute paths: C:\, D:\, etc.
141
+ if (/^[a-zA-Z]:[/\\]/.test(inputPath)) {
142
+ return true;
143
+ }
144
+
145
+ // Unix absolute paths: /
146
+ if (inputPath.startsWith('/')) {
147
+ return true;
148
+ }
149
+
150
+ // UNC paths: \\server\share
151
+ if (inputPath.startsWith('\\\\')) {
152
+ return true;
153
+ }
154
+
155
+ return false;
156
+ }
157
+
158
+ /**
159
+ * Gets the active exclusion patterns based on config
160
+ */
161
+ export function getActiveExclusions(config: SandboxConfig): string[] {
162
+ const exclusions: string[] = [];
163
+
164
+ // Start with defaults that are always excluded
165
+ exclusions.push('coverage/**', '.git/**', '.cache/**', '.turbo/**');
166
+
167
+ // Add third-party dirs unless explicitly included
168
+ if (!config.includeThirdParty) {
169
+ exclusions.push(...THIRD_PARTY_DIRS);
170
+ }
171
+
172
+ // Add generated dirs unless explicitly included
173
+ if (!config.includeGenerated) {
174
+ exclusions.push(...GENERATED_DIRS);
175
+ }
176
+
177
+ // Add any additional exclusions
178
+ if (config.additionalExclusions) {
179
+ exclusions.push(...config.additionalExclusions);
180
+ }
181
+
182
+ return exclusions;
183
+ }
184
+
185
+ /**
186
+ * Validates and resolves a path within the sandbox.
187
+ *
188
+ * @param inputPath - The path to validate (can be relative or absolute)
189
+ * @param config - Sandbox configuration
190
+ * @returns SandboxResult with validation status and resolved path
191
+ */
192
+ export function resolveSandboxPath(inputPath: string, config: SandboxConfig): SandboxResult {
193
+ // Validate workspace root exists and is absolute
194
+ if (!config.workspaceRoot || !isAbsolutePath(config.workspaceRoot)) {
195
+ return {
196
+ valid: false,
197
+ error: 'Invalid workspace root: must be an absolute path',
198
+ errorCode: 'INVALID_ROOT',
199
+ };
200
+ }
201
+
202
+ // Normalize workspace root
203
+ const normalizedRoot = path.resolve(config.workspaceRoot);
204
+
205
+ // Check for traversal sequences in the input
206
+ if (hasTraversalSequence(inputPath)) {
207
+ return {
208
+ valid: false,
209
+ error: `Path traversal detected in: ${inputPath}`,
210
+ errorCode: 'TRAVERSAL_DETECTED',
211
+ };
212
+ }
213
+
214
+ // Determine the target path
215
+ let targetPath: string;
216
+
217
+ if (isAbsolutePath(inputPath)) {
218
+ // Absolute path - check if it's within root
219
+ targetPath = path.resolve(inputPath);
220
+ } else {
221
+ // Relative path - resolve from workspace root
222
+ targetPath = path.resolve(normalizedRoot, inputPath);
223
+ }
224
+
225
+ // Normalize both paths for comparison
226
+ const normalizedTarget = path.normalize(targetPath);
227
+ const normalizedRootWithSep = normalizedRoot.endsWith(path.sep)
228
+ ? normalizedRoot
229
+ : normalizedRoot + path.sep;
230
+
231
+ // Check if target is within workspace root
232
+ if (!normalizedTarget.startsWith(normalizedRootWithSep) && normalizedTarget !== normalizedRoot) {
233
+ return {
234
+ valid: false,
235
+ error: `Path escapes workspace root: ${inputPath}`,
236
+ errorCode: 'ABSOLUTE_PATH_OUTSIDE_ROOT',
237
+ };
238
+ }
239
+
240
+ // Check for symlink escape (if the path exists)
241
+ try {
242
+ if (fs.existsSync(targetPath)) {
243
+ const realPath = fs.realpathSync(targetPath);
244
+ const normalizedRealPath = path.normalize(realPath);
245
+
246
+ if (
247
+ !normalizedRealPath.startsWith(normalizedRootWithSep) &&
248
+ normalizedRealPath !== normalizedRoot
249
+ ) {
250
+ return {
251
+ valid: false,
252
+ error: `Symlink escape detected: ${inputPath} resolves to ${realPath}`,
253
+ errorCode: 'SYMLINK_ESCAPE',
254
+ };
255
+ }
256
+ }
257
+ } catch {
258
+ // If we can't check realpath, allow it (file may not exist yet)
259
+ }
260
+
261
+ // Get relative path for exclusion checking
262
+ const relativePath = path.relative(normalizedRoot, normalizedTarget);
263
+
264
+ // Check against allowlist first (allowlist overrides exclusions)
265
+ if (config.allowlist && matchesGlob(relativePath, config.allowlist)) {
266
+ return {
267
+ valid: true,
268
+ resolvedPath: normalizedTarget,
269
+ };
270
+ }
271
+
272
+ // Check against exclusions
273
+ const activeExclusions = getActiveExclusions(config);
274
+ if (matchesGlob(relativePath, activeExclusions)) {
275
+ return {
276
+ valid: false,
277
+ error: `Path is excluded: ${relativePath}`,
278
+ errorCode: 'EXCLUDED_PATH',
279
+ };
280
+ }
281
+
282
+ return {
283
+ valid: true,
284
+ resolvedPath: normalizedTarget,
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Validates multiple paths at once.
290
+ *
291
+ * @param inputPaths - Array of paths to validate
292
+ * @param config - Sandbox configuration
293
+ * @returns Object with overall validity and individual results
294
+ */
295
+ export function validateSandboxPaths(
296
+ inputPaths: string[],
297
+ config: SandboxConfig
298
+ ): {
299
+ valid: boolean;
300
+ results: Map<string, SandboxResult>;
301
+ errors: string[];
302
+ } {
303
+ const results = new Map<string, SandboxResult>();
304
+ const errors: string[] = [];
305
+
306
+ for (const inputPath of inputPaths) {
307
+ const result = resolveSandboxPath(inputPath, config);
308
+ results.set(inputPath, result);
309
+
310
+ if (!result.valid && result.error) {
311
+ errors.push(result.error);
312
+ }
313
+ }
314
+
315
+ return {
316
+ valid: errors.length === 0,
317
+ results,
318
+ errors,
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Creates a sandbox-safe path resolver bound to a specific config.
324
+ * Useful for creating a resolver that can be reused multiple times.
325
+ *
326
+ * @param config - Sandbox configuration
327
+ * @returns A function that resolves paths within the sandbox
328
+ */
329
+ export function createSandboxResolver(config: SandboxConfig) {
330
+ return (inputPath: string): SandboxResult => {
331
+ return resolveSandboxPath(inputPath, config);
332
+ };
333
+ }
334
+
335
+ /**
336
+ * RunRequest interface for config overrides.
337
+ * This matches the expected interface from tool requests.
338
+ */
339
+ export interface RunRequest {
340
+ /** Path to resolve */
341
+ path?: string;
342
+ /** Paths to resolve */
343
+ paths?: string[];
344
+ /** Project path / workspace root */
345
+ projectPath?: string;
346
+ /** Include third-party directories (node_modules, venv) */
347
+ includeThirdParty?: boolean;
348
+ /** Include generated directories (dist, build, .next) */
349
+ includeGenerated?: boolean;
350
+ }
351
+
352
+ /**
353
+ * Creates a SandboxConfig from a RunRequest.
354
+ *
355
+ * @param request - The run request with optional overrides
356
+ * @param defaultRoot - Default workspace root if not in request
357
+ * @returns SandboxConfig ready for use
358
+ */
359
+ export function configFromRunRequest(request: RunRequest, defaultRoot: string): SandboxConfig {
360
+ return {
361
+ workspaceRoot: request.projectPath || defaultRoot,
362
+ includeThirdParty: request.includeThirdParty ?? false,
363
+ includeGenerated: request.includeGenerated ?? false,
364
+ };
365
+ }
366
+
367
+ /**
368
+ * Validates a path from a RunRequest, applying config overrides.
369
+ *
370
+ * @param request - The run request containing path and overrides
371
+ * @param defaultRoot - Default workspace root
372
+ * @returns SandboxResult
373
+ */
374
+ export function validateRunRequest(request: RunRequest, defaultRoot: string): SandboxResult {
375
+ const config = configFromRunRequest(request, defaultRoot);
376
+
377
+ if (request.path) {
378
+ return resolveSandboxPath(request.path, config);
379
+ }
380
+
381
+ if (request.paths && request.paths.length > 0) {
382
+ const multiResult = validateSandboxPaths(request.paths, config);
383
+ if (!multiResult.valid) {
384
+ return {
385
+ valid: false,
386
+ error: multiResult.errors.join('; '),
387
+ errorCode: 'TRAVERSAL_DETECTED', // Use first error type
388
+ };
389
+ }
390
+ return { valid: true };
391
+ }
392
+
393
+ // No path specified
394
+ return { valid: true };
395
+ }
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Shared types for MCP tool handler system
3
+ */
4
+
5
+ // ═══════════════════════════════════════════════════════════════════════════════
6
+ // REQUEST/RESPONSE TYPES
7
+ // ═══════════════════════════════════════════════════════════════════════════════
8
+
9
+ export interface RunRequest {
10
+ /** Unique request identifier for tracing */
11
+ requestId: string;
12
+ /** Optional trace ID for distributed tracing */
13
+ traceId?: string;
14
+ /** Tool name to execute */
15
+ tool: string;
16
+ /** Tool arguments */
17
+ args: Record<string, unknown>;
18
+ /** User context */
19
+ context?: RequestContext;
20
+ }
21
+
22
+ export interface RequestContext {
23
+ /** User's subscription tier */
24
+ tier?: "free" | "pro";
25
+ /** User ID for audit */
26
+ userId?: string;
27
+ /** Project root path */
28
+ projectRoot?: string;
29
+ /** API key (hashed) */
30
+ apiKeyHash?: string;
31
+ }
32
+
33
+ export interface RunResponse {
34
+ /** Request ID echo */
35
+ requestId: string;
36
+ /** Trace ID echo */
37
+ traceId?: string;
38
+ /** Success indicator */
39
+ ok: boolean;
40
+ /** Result data (on success) */
41
+ data?: ToolResult;
42
+ /** Error envelope (on failure) */
43
+ error?: ErrorEnvelope;
44
+ /** Execution metadata */
45
+ metadata: ResponseMetadata;
46
+ }
47
+
48
+ export interface ResponseMetadata {
49
+ /** Execution start time (ISO) */
50
+ startedAt: string;
51
+ /** Execution end time (ISO) */
52
+ completedAt: string;
53
+ /** Duration in milliseconds */
54
+ durationMs: number;
55
+ /** Tool that was executed */
56
+ tool: string;
57
+ /** CLI command that was executed (if applicable) */
58
+ cliCommand?: string;
59
+ /** Exit code from CLI */
60
+ exitCode?: number;
61
+ }
62
+
63
+ // ═══════════════════════════════════════════════════════════════════════════════
64
+ // ERROR ENVELOPE
65
+ // ═══════════════════════════════════════════════════════════════════════════════
66
+
67
+ export interface ErrorEnvelope {
68
+ /** Error code for programmatic handling */
69
+ code: ErrorCode;
70
+ /** Human-readable message */
71
+ message: string;
72
+ /** Suggested next steps */
73
+ nextSteps?: string[];
74
+ /** Evidence/receipt (file:line) */
75
+ receipt?: string;
76
+ /** Validation errors (for INPUT_VALIDATION) */
77
+ validationErrors?: ValidationError[];
78
+ /** Request ID for support */
79
+ requestId?: string;
80
+ /** User action to resolve (for tier errors) */
81
+ userAction?: string;
82
+ /** Whether the operation can be retried */
83
+ retryable?: boolean;
84
+ /** Current user tier (for tier errors) */
85
+ tier?: string;
86
+ /** Required tier (for tier errors) */
87
+ required?: string;
88
+ /** Tool name (for tier errors) */
89
+ tool?: string;
90
+ /** Blocked option (for option-level gates) */
91
+ option?: string;
92
+ /** Upgrade URL */
93
+ upgradeUrl?: string;
94
+ }
95
+
96
+ export type ErrorCode =
97
+ | "INPUT_VALIDATION"
98
+ | "TOOL_NOT_FOUND"
99
+ | "TIER_REQUIRED"
100
+ | "NOT_ENTITLED" // Tool requires higher tier
101
+ | "OPTION_NOT_ENTITLED" // Specific option requires higher tier
102
+ | "PATH_VIOLATION"
103
+ | "EXECUTOR_FAILED"
104
+ | "OUTPUT_PARSE_ERROR"
105
+ | "OUTPUT_VALIDATION"
106
+ | "TIMEOUT"
107
+ | "INTERNAL_ERROR"
108
+ | "INVALID_API_KEY"
109
+ | "RATE_LIMITED";
110
+
111
+ export interface ValidationError {
112
+ /** JSON path to the invalid field */
113
+ path: string;
114
+ /** Error message */
115
+ message: string;
116
+ /** Expected value/type */
117
+ expected?: string;
118
+ /** Actual value/type */
119
+ actual?: string;
120
+ }
121
+
122
+ // ═══════════════════════════════════════════════════════════════════════════════
123
+ // TOOL REGISTRY
124
+ // ═══════════════════════════════════════════════════════════════════════════════
125
+
126
+ export interface ToolDefinition {
127
+ /** Tool name (unique identifier) */
128
+ name: string;
129
+ /** Human-readable description */
130
+ description: string;
131
+ /** Subscription tier required */
132
+ tier: "free" | "pro";
133
+ /** Tool category */
134
+ category: ToolCategory;
135
+ /** JSON Schema for input validation */
136
+ inputSchema: JsonSchema;
137
+ /** JSON Schema for output validation */
138
+ outputSchema: JsonSchema;
139
+ /** CLI command mapping */
140
+ cli: CliMapping;
141
+ /** Tool aliases */
142
+ aliases?: string[];
143
+ /** Related tools */
144
+ related?: string[];
145
+ }
146
+
147
+ export type ToolCategory =
148
+ | "scan"
149
+ | "proof"
150
+ | "authority"
151
+ | "report"
152
+ | "setup"
153
+ | "account"
154
+ | "conductor"
155
+ | "firewall";
156
+
157
+ export interface CliMapping {
158
+ /** CLI command to execute */
159
+ command: string;
160
+ /** Argument mapping: { inputField: "--flag" } */
161
+ argMap: Record<string, string>;
162
+ /** Fixed flags always passed */
163
+ fixedFlags?: string[];
164
+ /** Timeout in milliseconds */
165
+ timeoutMs?: number;
166
+ }
167
+
168
+ export interface JsonSchema {
169
+ type: string;
170
+ properties?: Record<string, JsonSchemaProperty>;
171
+ required?: string[];
172
+ additionalProperties?: boolean;
173
+ }
174
+
175
+ export interface JsonSchemaProperty {
176
+ type: string;
177
+ description?: string;
178
+ default?: unknown;
179
+ enum?: unknown[];
180
+ items?: JsonSchemaProperty;
181
+ properties?: Record<string, JsonSchemaProperty>;
182
+ required?: string[];
183
+ minimum?: number;
184
+ maximum?: number;
185
+ pattern?: string;
186
+ }
187
+
188
+ // ═══════════════════════════════════════════════════════════════════════════════
189
+ // TOOL RESULT (CANONICAL OUTPUT)
190
+ // ═══════════════════════════════════════════════════════════════════════════════
191
+
192
+ export interface ToolResult {
193
+ /** Verdict (for scan/ship tools) */
194
+ verdict?: "SHIP" | "WARN" | "BLOCK" | "PASS" | "FAIL";
195
+ /** Findings list */
196
+ findings?: Finding[];
197
+ /** Summary statistics */
198
+ summary?: ResultSummary;
199
+ /** Raw output (if applicable) */
200
+ raw?: unknown;
201
+ }
202
+
203
+ export interface Finding {
204
+ /** Stable finding ID: sha256(rule_id + path + line + message) */
205
+ id: string;
206
+ /** Rule that generated this finding */
207
+ ruleId: string;
208
+ /** Severity level */
209
+ severity: "critical" | "high" | "medium" | "low" | "info";
210
+ /** Finding message */
211
+ message: string;
212
+ /** File path (relative to project root) */
213
+ path: string;
214
+ /** Line number (1-based) */
215
+ line: number;
216
+ /** Column number (1-based, optional) */
217
+ column?: number;
218
+ /** Code snippet */
219
+ snippet?: string;
220
+ /** Finding category */
221
+ category?: string;
222
+ /** Suggested fix */
223
+ fix?: string;
224
+ }
225
+
226
+ export interface ResultSummary {
227
+ /** Total findings count */
228
+ total: number;
229
+ /** Findings by severity */
230
+ bySeverity: Record<string, number>;
231
+ /** Findings by category */
232
+ byCategory?: Record<string, number>;
233
+ /** Files scanned */
234
+ filesScanned?: number;
235
+ /** Duration of scan */
236
+ scanDurationMs?: number;
237
+ }
238
+
239
+ // ═══════════════════════════════════════════════════════════════════════════════
240
+ // EXECUTOR TYPES
241
+ // ═══════════════════════════════════════════════════════════════════════════════
242
+
243
+ export interface ExecutorOptions {
244
+ /** Working directory */
245
+ cwd: string;
246
+ /** Timeout in milliseconds */
247
+ timeoutMs: number;
248
+ /** Environment variables to pass */
249
+ env?: Record<string, string>;
250
+ /** Request ID for logging */
251
+ requestId: string;
252
+ /** Trace ID for logging */
253
+ traceId?: string;
254
+ }
255
+
256
+ export interface ExecutorResult {
257
+ /** Exit code */
258
+ exitCode: number;
259
+ /** Stdout content */
260
+ stdout: string;
261
+ /** Stderr content */
262
+ stderr: string;
263
+ /** Whether command timed out */
264
+ timedOut: boolean;
265
+ /** Execution duration in ms */
266
+ durationMs: number;
267
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecheck-mcp-server",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "Professional MCP server for vibecheck - Intelligent development environment vibechecks",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -9,7 +9,10 @@
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node index.js",
12
- "dev": "VIBECHECK_DEBUG=true node index.js"
12
+ "dev": "VIBECHECK_DEBUG=true node index.js",
13
+ "build": "tsc -p tsconfig.json",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest"
13
16
  },
14
17
  "keywords": [
15
18
  "mcp",
@@ -21,7 +24,13 @@
21
24
  "architecture"
22
25
  ],
23
26
  "dependencies": {
24
- "@modelcontextprotocol/sdk": "^1.0.0"
27
+ "@modelcontextprotocol/sdk": "^1.0.0",
28
+ "ajv": "^8.12.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.11.0",
32
+ "typescript": "^5.3.0",
33
+ "vitest": "^1.2.0"
25
34
  },
26
35
  "engines": {
27
36
  "node": ">=20.11"