@vibecheckai/cli 3.5.1 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +406 -154
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/mcp.js +15 -13
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/proof-context.js +248 -1
- package/bin/runners/context/team-conventions.js +33 -7
- 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/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/interceptor/base.js +304 -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 +86 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +162 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +189 -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 +321 -0
- package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -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/analysis-core.js +220 -182
- package/bin/runners/lib/analyzers.js +2145 -224
- package/bin/runners/lib/api-client.js +269 -0
- package/bin/runners/lib/authority-badge.js +425 -0
- package/bin/runners/lib/cli-output.js +242 -210
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/detectors-v2.js +547 -785
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +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/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/entitlements-v2.js +152 -446
- package/bin/runners/lib/error-handler.js +60 -12
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/exit-codes.js +275 -0
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/fingerprint.js +377 -0
- package/bin/runners/lib/global-flags.js +37 -0
- package/bin/runners/lib/help-formatter.js +413 -0
- package/bin/runners/lib/logger.js +38 -0
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/route-truth.js +1167 -322
- package/bin/runners/lib/scan-output.js +504 -463
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +239 -0
- package/bin/runners/lib/ship-output.js +328 -31
- package/bin/runners/lib/terminal-ui.js +234 -731
- package/bin/runners/lib/truth.js +1332 -308
- package/bin/runners/lib/unified-cli-output.js +604 -0
- package/bin/runners/lib/unified-output.js +163 -155
- package/bin/runners/lib/upsell.js +104 -204
- package/bin/runners/runAgent.d.ts +5 -0
- package/bin/runners/runAgent.js +161 -0
- package/bin/runners/runAllowlist.js +166 -101
- package/bin/runners/runApprove.js +1200 -0
- package/bin/runners/runAuth.js +373 -95
- package/bin/runners/runCheckpoint.js +59 -21
- package/bin/runners/runClassify.js +926 -0
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +136 -24
- package/bin/runners/runDoctor.js +115 -67
- package/bin/runners/runEvidencePack.js +239 -96
- 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 +6 -5
- package/bin/runners/runGuard.js +212 -118
- package/bin/runners/runInit.js +66 -21
- package/bin/runners/runLabs.js +204 -121
- package/bin/runners/runMcp.js +131 -60
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +43 -20
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +15 -5
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runReport.js +36 -4
- package/bin/runners/runScan.js +689 -91
- package/bin/runners/runShip.js +96 -40
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/runners/runTruth.js +101 -0
- package/bin/runners/runValidate.js +21 -4
- package/bin/runners/runWatch.js +118 -54
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +297 -52
- package/mcp-server/HARDENING_SUMMARY.md +299 -0
- package/mcp-server/agent-firewall-interceptor.js +500 -0
- package/mcp-server/authority-tools.js +569 -0
- package/mcp-server/conductor/conflict-resolver.js +588 -0
- package/mcp-server/conductor/execution-planner.js +544 -0
- package/mcp-server/conductor/index.js +377 -0
- package/mcp-server/conductor/lock-manager.js +615 -0
- package/mcp-server/conductor/request-queue.js +550 -0
- package/mcp-server/conductor/session-manager.js +500 -0
- package/mcp-server/conductor/tools.js +510 -0
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +474 -591
- package/mcp-server/index.js +1748 -1099
- package/mcp-server/lib/api-client.cjs +13 -0
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +428 -721
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/logger.cjs +30 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +342 -284
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/logger.js +173 -0
- package/mcp-server/package.json +11 -27
- package/mcp-server/premium-tools.js +2 -2
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +507 -378
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +492 -347
- package/mcp-server/tools-v3.js +950 -0
- package/mcp-server/truth-context.js +131 -90
- package/mcp-server/truth-firewall-tools.js +1612 -1001
- package/mcp-server/tsconfig.json +8 -5
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
- package/mcp-server/vibecheck-tools.js +2 -2
- package/package.json +4 -3
- package/bin/runners/runInstall.js +0 -281
- package/mcp-server/ARCHITECTURE.md +0 -339
- package/mcp-server/__tests__/cache.test.ts +0 -313
- package/mcp-server/__tests__/executor.test.ts +0 -239
- package/mcp-server/__tests__/fixtures/exclusion-test/.cache/webpack/cache.pack +0 -1
- package/mcp-server/__tests__/fixtures/exclusion-test/.next/server/chunk.js +0 -3
- package/mcp-server/__tests__/fixtures/exclusion-test/.turbo/cache.json +0 -3
- package/mcp-server/__tests__/fixtures/exclusion-test/.venv/lib/env.py +0 -3
- package/mcp-server/__tests__/fixtures/exclusion-test/dist/bundle.js +0 -3
- package/mcp-server/__tests__/fixtures/exclusion-test/package.json +0 -5
- package/mcp-server/__tests__/fixtures/exclusion-test/src/app.ts +0 -5
- package/mcp-server/__tests__/fixtures/exclusion-test/venv/lib/config.py +0 -4
- package/mcp-server/__tests__/ids.test.ts +0 -345
- package/mcp-server/__tests__/integration/tools.test.ts +0 -410
- package/mcp-server/__tests__/registry.test.ts +0 -365
- package/mcp-server/__tests__/sandbox.test.ts +0 -323
- package/mcp-server/__tests__/schemas.test.ts +0 -372
- package/mcp-server/benchmarks/run-benchmarks.ts +0 -304
- package/mcp-server/examples/doctor.request.json +0 -14
- package/mcp-server/examples/doctor.response.json +0 -53
- package/mcp-server/examples/error.response.json +0 -15
- package/mcp-server/examples/scan.request.json +0 -14
- package/mcp-server/examples/scan.response.json +0 -108
- package/mcp-server/index-v3.ts +0 -293
- package/mcp-server/index.old.js +0 -4137
- package/mcp-server/lib/cache.ts +0 -341
- package/mcp-server/lib/errors.ts +0 -346
- package/mcp-server/lib/ids.ts +0 -238
- package/mcp-server/lib/logger.ts +0 -368
- package/mcp-server/lib/metrics.ts +0 -365
- package/mcp-server/lib/validator.ts +0 -229
- package/mcp-server/package-lock.json +0 -165
- package/mcp-server/schemas/error-envelope.schema.json +0 -125
- package/mcp-server/schemas/finding.schema.json +0 -167
- package/mcp-server/schemas/report-artifact.schema.json +0 -88
- package/mcp-server/schemas/run-request.schema.json +0 -75
- package/mcp-server/schemas/verdict.schema.json +0 -168
- package/mcp-server/tier-auth.d.ts +0 -71
- package/mcp-server/vitest.config.ts +0 -16
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan Runner - Timeout and Cancellation Support
|
|
3
|
+
*
|
|
4
|
+
* Provides timeout and cancellation capabilities for scan execution.
|
|
5
|
+
* Prevents runaway scans from hanging indefinitely.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} ScanOptions
|
|
12
|
+
* @property {number} [timeout] - Timeout in milliseconds (default: 5 minutes)
|
|
13
|
+
* @property {AbortSignal} [signal] - AbortSignal for cancellation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} ScanResult
|
|
18
|
+
* @property {'completed'|'cancelled'|'failed'} status
|
|
19
|
+
* @property {Array} findings
|
|
20
|
+
* @property {string} [error]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
class ScanRunner extends EventEmitter {
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
this.aborted = false;
|
|
27
|
+
this.timeoutId = null;
|
|
28
|
+
}
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.aborted = false;
|
|
32
|
+
this.timeoutId = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run a scan with timeout and cancellation support
|
|
37
|
+
* @param {string} projectPath - Path to project to scan
|
|
38
|
+
* @param {ScanOptions} options - Scan options
|
|
39
|
+
* @returns {Promise<ScanResult>}
|
|
40
|
+
*/
|
|
41
|
+
async run(projectPath, options = {}) {
|
|
42
|
+
const timeout = options.timeout || 5 * 60 * 1000; // Default 5 minutes
|
|
43
|
+
|
|
44
|
+
// Set up abort handling
|
|
45
|
+
if (options.signal) {
|
|
46
|
+
options.signal.addEventListener('abort', () => {
|
|
47
|
+
this.abort('Scan aborted via signal');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Set up timeout
|
|
52
|
+
this.timeoutId = setTimeout(() => {
|
|
53
|
+
this.abort('Scan timed out');
|
|
54
|
+
}, timeout);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
this.emit('start', { projectPath, timeout });
|
|
58
|
+
|
|
59
|
+
const result = await this.executeAnalyzers(projectPath);
|
|
60
|
+
|
|
61
|
+
if (this.aborted) {
|
|
62
|
+
return { status: 'cancelled', findings: [] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (this.aborted) {
|
|
68
|
+
return { status: 'cancelled', findings: [], error: error.message };
|
|
69
|
+
}
|
|
70
|
+
return { status: 'failed', findings: [], error: error.message };
|
|
71
|
+
} finally {
|
|
72
|
+
if (this.timeoutId) {
|
|
73
|
+
clearTimeout(this.timeoutId);
|
|
74
|
+
this.timeoutId = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Abort the current scan
|
|
81
|
+
* @param {string} reason - Reason for abortion
|
|
82
|
+
*/
|
|
83
|
+
abort(reason = 'Scan aborted') {
|
|
84
|
+
this.aborted = true;
|
|
85
|
+
this.emit('abort', { reason });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Execute analyzers with abort checks between each
|
|
90
|
+
* @private
|
|
91
|
+
* @param {string} projectPath - Path to project
|
|
92
|
+
* @returns {Promise<ScanResult>}
|
|
93
|
+
*/
|
|
94
|
+
async executeAnalyzers(projectPath) {
|
|
95
|
+
// Import analysis core dynamically to avoid circular dependencies
|
|
96
|
+
const { runAnalysis } = require('./analysis-core');
|
|
97
|
+
|
|
98
|
+
// Check abort before starting
|
|
99
|
+
if (this.aborted) {
|
|
100
|
+
return { status: 'cancelled', findings: [] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Run analysis with progress tracking
|
|
105
|
+
this.emit('progress', { phase: 'analysis', status: 'running' });
|
|
106
|
+
|
|
107
|
+
const result = await runAnalysis({
|
|
108
|
+
repoRoot: projectPath,
|
|
109
|
+
extended: true,
|
|
110
|
+
noWrite: false
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Check abort after analysis
|
|
114
|
+
if (this.aborted) {
|
|
115
|
+
return { status: 'cancelled', findings: result.findings || [] };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.emit('progress', { phase: 'analysis', status: 'complete', count: result.findings?.length || 0 });
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
status: 'completed',
|
|
122
|
+
findings: result.findings || [],
|
|
123
|
+
verdict: result.verdict,
|
|
124
|
+
report: result.report
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (this.aborted) {
|
|
128
|
+
return { status: 'cancelled', findings: [], error: error.message };
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { ScanRunner };
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Validator v3 - Production AJV Implementation
|
|
3
|
+
*
|
|
4
|
+
* Full JSON Schema Draft 2020-12 validation using AJV.
|
|
5
|
+
* Replaces the minimal validator for production use.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const crypto = require("crypto");
|
|
13
|
+
|
|
14
|
+
// Lazy-load AJV to avoid startup cost if not needed
|
|
15
|
+
let ajv = null;
|
|
16
|
+
let ajvFormats = null;
|
|
17
|
+
|
|
18
|
+
function getAjv() {
|
|
19
|
+
if (!ajv) {
|
|
20
|
+
const Ajv = require("ajv");
|
|
21
|
+
ajvFormats = require("ajv-formats");
|
|
22
|
+
|
|
23
|
+
ajv = new Ajv({
|
|
24
|
+
strict: true,
|
|
25
|
+
allErrors: true,
|
|
26
|
+
verbose: true,
|
|
27
|
+
validateFormats: true,
|
|
28
|
+
// Allow unknown keywords for custom extensions
|
|
29
|
+
strictTypes: true,
|
|
30
|
+
strictTuples: true,
|
|
31
|
+
// Cache schemas for performance
|
|
32
|
+
cache: new Map(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Add standard formats (date-time, uri, email, etc.)
|
|
36
|
+
ajvFormats(ajv);
|
|
37
|
+
|
|
38
|
+
// Add custom formats
|
|
39
|
+
ajv.addFormat("sha256", /^sha256:[a-f0-9]{64}$/);
|
|
40
|
+
ajv.addFormat("fingerprint", /^sha256:[a-f0-9]{64}$/);
|
|
41
|
+
ajv.addFormat("finding-id", /^F_[A-Z0-9_]+$/);
|
|
42
|
+
ajv.addFormat("evidence-id", /^E_[A-Z0-9]+$/);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return ajv;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Schema cache
|
|
49
|
+
const schemaCache = new Map();
|
|
50
|
+
const validatorCache = new Map();
|
|
51
|
+
|
|
52
|
+
// Schema IDs (same as validator.js for compatibility)
|
|
53
|
+
const SCHEMAS = {
|
|
54
|
+
FINDING: "finding.schema.json",
|
|
55
|
+
FINDING_V3: "finding-v3.schema.json",
|
|
56
|
+
SHIP_REPORT: "ship-report.schema.json",
|
|
57
|
+
REALITY_REPORT: "reality-report.schema.json",
|
|
58
|
+
TRUTHPACK_V2: "truthpack-v2.schema.json",
|
|
59
|
+
CONTRACTS: "contracts.schema.json",
|
|
60
|
+
PROOF_GRAPH: "proof-graph.schema.json",
|
|
61
|
+
MISSION_PACK: "mission-pack.schema.json",
|
|
62
|
+
SHARE_PACK: "share-pack.schema.json",
|
|
63
|
+
VERDICT: "verdict.schema.json",
|
|
64
|
+
ERROR_ENVELOPE: "error-envelope.schema.json",
|
|
65
|
+
RUN_REQUEST: "run-request.schema.json",
|
|
66
|
+
REPORT_ARTIFACT: "report-artifact.schema.json",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// SCHEMA LOADING
|
|
71
|
+
// =============================================================================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Load a schema by ID
|
|
75
|
+
*/
|
|
76
|
+
function loadSchema(schemaId) {
|
|
77
|
+
if (schemaCache.has(schemaId)) {
|
|
78
|
+
return schemaCache.get(schemaId);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const schemaPath = path.join(__dirname, schemaId);
|
|
82
|
+
if (!fs.existsSync(schemaPath)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
|
88
|
+
schemaCache.set(schemaId, schema);
|
|
89
|
+
return schema;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`Failed to load schema ${schemaId}:`, e.message);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get compiled validator for schema
|
|
98
|
+
*/
|
|
99
|
+
function getValidator(schemaId) {
|
|
100
|
+
if (validatorCache.has(schemaId)) {
|
|
101
|
+
return validatorCache.get(schemaId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const schema = loadSchema(schemaId);
|
|
105
|
+
if (!schema) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const ajvInstance = getAjv();
|
|
111
|
+
|
|
112
|
+
// Remove $id to avoid conflicts when compiling multiple schemas
|
|
113
|
+
const schemaCopy = { ...schema };
|
|
114
|
+
delete schemaCopy.$id;
|
|
115
|
+
|
|
116
|
+
const validator = ajvInstance.compile(schemaCopy);
|
|
117
|
+
validatorCache.set(schemaId, validator);
|
|
118
|
+
return validator;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`Failed to compile schema ${schemaId}:`, e.message);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate data against schema using AJV
|
|
127
|
+
*/
|
|
128
|
+
function validateAgainstSchema(data, schema, contextPath = "") {
|
|
129
|
+
if (!schema || data === undefined) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const ajvInstance = getAjv();
|
|
135
|
+
const schemaCopy = { ...schema };
|
|
136
|
+
delete schemaCopy.$id;
|
|
137
|
+
|
|
138
|
+
const validate = ajvInstance.compile(schemaCopy);
|
|
139
|
+
const valid = validate(data);
|
|
140
|
+
|
|
141
|
+
if (valid) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Convert AJV errors to our format
|
|
146
|
+
return (validate.errors || []).map(err => ({
|
|
147
|
+
path: contextPath + (err.instancePath || ""),
|
|
148
|
+
message: err.message || "Validation failed",
|
|
149
|
+
keyword: err.keyword,
|
|
150
|
+
params: err.params,
|
|
151
|
+
}));
|
|
152
|
+
} catch (e) {
|
|
153
|
+
return [{
|
|
154
|
+
path: contextPath,
|
|
155
|
+
message: `Schema compilation error: ${e.message}`,
|
|
156
|
+
keyword: "compile",
|
|
157
|
+
}];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// ARTIFACT VALIDATION
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Validate an artifact file against its schema
|
|
167
|
+
*/
|
|
168
|
+
function validateArtifact(filePath, schemaId) {
|
|
169
|
+
const validator = getValidator(schemaId);
|
|
170
|
+
|
|
171
|
+
if (!validator) {
|
|
172
|
+
return {
|
|
173
|
+
valid: false,
|
|
174
|
+
errors: [{ path: "", message: `Schema ${schemaId} not found or invalid`, keyword: "schema" }],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let data;
|
|
179
|
+
try {
|
|
180
|
+
data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
181
|
+
} catch (e) {
|
|
182
|
+
return {
|
|
183
|
+
valid: false,
|
|
184
|
+
errors: [{ path: "", message: `Failed to read/parse file: ${e.message}`, keyword: "parse" }],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const valid = validator(data);
|
|
189
|
+
|
|
190
|
+
if (valid) {
|
|
191
|
+
return { valid: true, errors: [], data };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const errors = (validator.errors || []).map(err => ({
|
|
195
|
+
path: err.instancePath || "",
|
|
196
|
+
message: err.message || "Validation failed",
|
|
197
|
+
keyword: err.keyword,
|
|
198
|
+
params: err.params,
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
return { valid: false, errors, data };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Validate data object against schema
|
|
206
|
+
*/
|
|
207
|
+
function validateData(data, schemaId) {
|
|
208
|
+
const validator = getValidator(schemaId);
|
|
209
|
+
|
|
210
|
+
if (!validator) {
|
|
211
|
+
return {
|
|
212
|
+
valid: false,
|
|
213
|
+
errors: [{ path: "", message: `Schema ${schemaId} not found`, keyword: "schema" }],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const valid = validator(data);
|
|
218
|
+
|
|
219
|
+
if (valid) {
|
|
220
|
+
return { valid: true, errors: [] };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const errors = (validator.errors || []).map(err => ({
|
|
224
|
+
path: err.instancePath || "",
|
|
225
|
+
message: err.message || "Validation failed",
|
|
226
|
+
keyword: err.keyword,
|
|
227
|
+
}));
|
|
228
|
+
|
|
229
|
+
return { valid: false, errors };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Validate and write artifact with schema check
|
|
234
|
+
*/
|
|
235
|
+
function writeValidatedArtifact(filePath, data, schemaId, options = {}) {
|
|
236
|
+
const { strict = false, warnOnly = true } = options;
|
|
237
|
+
|
|
238
|
+
const result = validateData(data, schemaId);
|
|
239
|
+
|
|
240
|
+
if (!result.valid) {
|
|
241
|
+
const message = `Artifact ${filePath} has ${result.errors.length} schema violations`;
|
|
242
|
+
|
|
243
|
+
if (strict) {
|
|
244
|
+
throw new Error(`${message}: ${result.errors.map(e => e.message).join(", ")}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (warnOnly) {
|
|
248
|
+
console.warn(`Warning: ${message}:`);
|
|
249
|
+
result.errors.slice(0, 5).forEach(e =>
|
|
250
|
+
console.warn(` - ${e.path || "/"}: ${e.message}`)
|
|
251
|
+
);
|
|
252
|
+
if (result.errors.length > 5) {
|
|
253
|
+
console.warn(` ... and ${result.errors.length - 5} more errors`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
259
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
written: true,
|
|
263
|
+
path: filePath,
|
|
264
|
+
valid: result.valid,
|
|
265
|
+
errors: result.errors,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// =============================================================================
|
|
270
|
+
// FINDING GENERATION (kept for compatibility with validator.js)
|
|
271
|
+
// =============================================================================
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Generate stable fingerprint for deduplication
|
|
275
|
+
*/
|
|
276
|
+
function generateFingerprint(parts) {
|
|
277
|
+
const content = parts.filter(Boolean).join("|");
|
|
278
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Generate finding ID
|
|
283
|
+
*/
|
|
284
|
+
function generateFindingId(detectorId, fingerprint) {
|
|
285
|
+
return `F_${detectorId}_${fingerprint.slice(0, 8).toUpperCase()}`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Generate evidence ID
|
|
290
|
+
*/
|
|
291
|
+
function generateEvidenceId(fingerprint) {
|
|
292
|
+
return `E_${fingerprint.slice(0, 12).toUpperCase()}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Create a spec-compliant finding
|
|
297
|
+
*/
|
|
298
|
+
function createFinding(options = {}) {
|
|
299
|
+
const {
|
|
300
|
+
detectorId,
|
|
301
|
+
severity = "WARN",
|
|
302
|
+
category = "Routes",
|
|
303
|
+
scope = "client",
|
|
304
|
+
title,
|
|
305
|
+
why,
|
|
306
|
+
confidence = "medium",
|
|
307
|
+
evidence = [],
|
|
308
|
+
repro = null,
|
|
309
|
+
related = [],
|
|
310
|
+
proofNode = null,
|
|
311
|
+
missionType = null,
|
|
312
|
+
file = null,
|
|
313
|
+
path: routePath = null,
|
|
314
|
+
method = null,
|
|
315
|
+
} = options;
|
|
316
|
+
|
|
317
|
+
const fingerprint = generateFingerprint([
|
|
318
|
+
detectorId,
|
|
319
|
+
file,
|
|
320
|
+
routePath,
|
|
321
|
+
method,
|
|
322
|
+
title,
|
|
323
|
+
]);
|
|
324
|
+
|
|
325
|
+
const finding = {
|
|
326
|
+
id: generateFindingId(detectorId, fingerprint),
|
|
327
|
+
detectorId,
|
|
328
|
+
fingerprint: `sha256:${fingerprint}`,
|
|
329
|
+
severity,
|
|
330
|
+
category,
|
|
331
|
+
scope,
|
|
332
|
+
title,
|
|
333
|
+
why,
|
|
334
|
+
confidence,
|
|
335
|
+
evidence: evidence.map((e, i) => ({
|
|
336
|
+
id: e.id || generateEvidenceId(fingerprint + i),
|
|
337
|
+
kind: e.kind || "file",
|
|
338
|
+
...e,
|
|
339
|
+
})),
|
|
340
|
+
repro,
|
|
341
|
+
related,
|
|
342
|
+
proofNode,
|
|
343
|
+
missionType,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Validate the finding
|
|
347
|
+
const result = validateData(finding, SCHEMAS.FINDING_V3);
|
|
348
|
+
if (!result.valid) {
|
|
349
|
+
console.warn(`Warning: Created finding has schema violations:`, result.errors.slice(0, 3));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return finding;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Create file evidence
|
|
357
|
+
*/
|
|
358
|
+
function createFileEvidence(options = {}) {
|
|
359
|
+
const { file, lines, snippetHash, reason } = options;
|
|
360
|
+
const fp = generateFingerprint([file, lines, reason]);
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
id: generateEvidenceId(fp),
|
|
364
|
+
kind: "file",
|
|
365
|
+
file,
|
|
366
|
+
lines,
|
|
367
|
+
snippetHash,
|
|
368
|
+
reason,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Create runtime evidence
|
|
374
|
+
*/
|
|
375
|
+
function createRuntimeEvidence(options = {}) {
|
|
376
|
+
const { url, httpStatus, reason, data } = options;
|
|
377
|
+
const fp = generateFingerprint([url, httpStatus, reason]);
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
id: generateEvidenceId(fp),
|
|
381
|
+
kind: "request",
|
|
382
|
+
url,
|
|
383
|
+
httpStatus,
|
|
384
|
+
reason,
|
|
385
|
+
data,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// =============================================================================
|
|
390
|
+
// SEVERITY POLICY (kept for compatibility with validator.js)
|
|
391
|
+
// =============================================================================
|
|
392
|
+
|
|
393
|
+
const SEVERITY_POLICY = {
|
|
394
|
+
ROUTE_MISSING: { default: "BLOCK", withRuntimeProof: "BLOCK" },
|
|
395
|
+
ROUTE_404: { default: "BLOCK" },
|
|
396
|
+
ROUTE_405: { default: "WARN" },
|
|
397
|
+
AUTH_BYPASS: { default: "BLOCK" },
|
|
398
|
+
AUTH_MISSING: { default: "WARN", criticalPath: "BLOCK" },
|
|
399
|
+
FAKE_SUCCESS: { default: "BLOCK" },
|
|
400
|
+
SUCCESS_TOAST_NO_CHANGE: { default: "BLOCK" },
|
|
401
|
+
SUCCESS_BEFORE_REQUEST: { default: "BLOCK" },
|
|
402
|
+
DEAD_CLICK: { default: "BLOCK" },
|
|
403
|
+
NO_FEEDBACK: { default: "WARN" },
|
|
404
|
+
CONTRACT_DRIFT_ROUTE: { default: "WARN", blocking: "BLOCK" },
|
|
405
|
+
CONTRACT_DRIFT_ENV: { default: "WARN", required: "BLOCK" },
|
|
406
|
+
CONTRACT_DRIFT_AUTH: { default: "BLOCK" },
|
|
407
|
+
STRIPE_WEBHOOK_UNVERIFIED: { default: "BLOCK" },
|
|
408
|
+
PAID_SURFACE_UNENFORCED: { default: "BLOCK" },
|
|
409
|
+
OWNER_MODE_BYPASS: { default: "BLOCK" },
|
|
410
|
+
HARDCODED_SECRET: { default: "BLOCK" },
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get severity based on policy
|
|
415
|
+
*/
|
|
416
|
+
function getSeverity(issueType, context = {}) {
|
|
417
|
+
const policy = SEVERITY_POLICY[issueType];
|
|
418
|
+
if (!policy) return "WARN";
|
|
419
|
+
|
|
420
|
+
if (context.hasRuntimeProof && policy.withRuntimeProof) {
|
|
421
|
+
return policy.withRuntimeProof;
|
|
422
|
+
}
|
|
423
|
+
if (context.isCriticalPath && policy.criticalPath) {
|
|
424
|
+
return policy.criticalPath;
|
|
425
|
+
}
|
|
426
|
+
if (context.isBlocking && policy.blocking) {
|
|
427
|
+
return policy.blocking;
|
|
428
|
+
}
|
|
429
|
+
if (context.isRequired && policy.required) {
|
|
430
|
+
return policy.required;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return policy.default;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// =============================================================================
|
|
437
|
+
// EXPORTS
|
|
438
|
+
// =============================================================================
|
|
439
|
+
|
|
440
|
+
module.exports = {
|
|
441
|
+
// Schema operations
|
|
442
|
+
SCHEMAS,
|
|
443
|
+
loadSchema,
|
|
444
|
+
getValidator,
|
|
445
|
+
validateAgainstSchema,
|
|
446
|
+
validateArtifact,
|
|
447
|
+
validateData,
|
|
448
|
+
writeValidatedArtifact,
|
|
449
|
+
|
|
450
|
+
// Finding generation
|
|
451
|
+
generateFingerprint,
|
|
452
|
+
generateFindingId,
|
|
453
|
+
generateEvidenceId,
|
|
454
|
+
createFinding,
|
|
455
|
+
createFileEvidence,
|
|
456
|
+
createRuntimeEvidence,
|
|
457
|
+
|
|
458
|
+
// Severity policy
|
|
459
|
+
SEVERITY_POLICY,
|
|
460
|
+
getSeverity,
|
|
461
|
+
|
|
462
|
+
// AJV access for advanced usage
|
|
463
|
+
getAjv,
|
|
464
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://vibecheck.dev/schemas/error-envelope.schema.json",
|
|
4
|
+
"x-schemaVersion": "1.0.0",
|
|
5
|
+
"title": "Vibecheck Error Envelope",
|
|
6
|
+
"description": "Standardized error response wrapper",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": [
|
|
9
|
+
"error",
|
|
10
|
+
"code",
|
|
11
|
+
"message"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"error": {
|
|
15
|
+
"type": "boolean",
|
|
16
|
+
"const": true,
|
|
17
|
+
"description": "Always true for error responses"
|
|
18
|
+
},
|
|
19
|
+
"code": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": [
|
|
22
|
+
"SCHEMA_INVALID",
|
|
23
|
+
"TOOL_NOT_FOUND",
|
|
24
|
+
"TIER_REQUIRED",
|
|
25
|
+
"AUTH_REQUIRED",
|
|
26
|
+
"AUTH_INVALID",
|
|
27
|
+
"RATE_LIMITED",
|
|
28
|
+
"TIMEOUT",
|
|
29
|
+
"PROJECT_NOT_FOUND",
|
|
30
|
+
"CONFIG_INVALID",
|
|
31
|
+
"INTERNAL_ERROR",
|
|
32
|
+
"NETWORK_ERROR",
|
|
33
|
+
"PARSE_ERROR"
|
|
34
|
+
],
|
|
35
|
+
"description": "Machine-readable error code"
|
|
36
|
+
},
|
|
37
|
+
"message": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"minLength": 1,
|
|
40
|
+
"description": "Human-readable error message"
|
|
41
|
+
},
|
|
42
|
+
"details": {
|
|
43
|
+
"type": ["object", "null"],
|
|
44
|
+
"description": "Additional error details",
|
|
45
|
+
"properties": {
|
|
46
|
+
"path": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "JSON path to invalid field (for SCHEMA_INVALID)"
|
|
49
|
+
},
|
|
50
|
+
"expected": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Expected value or type"
|
|
53
|
+
},
|
|
54
|
+
"received": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"description": "Received value or type"
|
|
57
|
+
},
|
|
58
|
+
"schema_errors": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"instancePath": { "type": "string" },
|
|
64
|
+
"schemaPath": { "type": "string" },
|
|
65
|
+
"keyword": { "type": "string" },
|
|
66
|
+
"params": { "type": "object" },
|
|
67
|
+
"message": { "type": "string" }
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"description": "Detailed AJV validation errors"
|
|
71
|
+
},
|
|
72
|
+
"required_tier": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"enum": ["free", "pro", "enterprise"],
|
|
75
|
+
"description": "Required tier (for TIER_REQUIRED)"
|
|
76
|
+
},
|
|
77
|
+
"current_tier": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"enum": ["free", "pro", "enterprise"],
|
|
80
|
+
"description": "Current user tier"
|
|
81
|
+
},
|
|
82
|
+
"retry_after_ms": {
|
|
83
|
+
"type": "integer",
|
|
84
|
+
"minimum": 0,
|
|
85
|
+
"description": "Milliseconds to wait before retry (for RATE_LIMITED)"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"timestamp": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"format": "date-time",
|
|
92
|
+
"description": "ISO 8601 timestamp of error occurrence"
|
|
93
|
+
},
|
|
94
|
+
"correlation_id": {
|
|
95
|
+
"type": ["string", "null"],
|
|
96
|
+
"description": "Correlation ID for tracing"
|
|
97
|
+
},
|
|
98
|
+
"help_url": {
|
|
99
|
+
"type": ["string", "null"],
|
|
100
|
+
"format": "uri",
|
|
101
|
+
"description": "URL to documentation for this error"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"additionalProperties": false
|
|
105
|
+
}
|