@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.
- package/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- 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 +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- 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/scan-output.js +91 -76
- 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 +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -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 +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://vibecheck.dev/schemas/finding-v3.schema.json",
|
|
4
|
+
"x-schemaVersion": "1.0.0",
|
|
5
|
+
"title": "Vibecheck Finding v3",
|
|
6
|
+
"description": "A single finding from static or runtime analysis with stable fields",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": [
|
|
9
|
+
"rule_id",
|
|
10
|
+
"category",
|
|
11
|
+
"severity",
|
|
12
|
+
"confidence",
|
|
13
|
+
"message",
|
|
14
|
+
"explanation",
|
|
15
|
+
"evidence",
|
|
16
|
+
"tags"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"rule_id": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"pattern": "^[A-Z][A-Z0-9_]+$",
|
|
22
|
+
"description": "Unique rule identifier (e.g., MOCK_DATA_001, AUTH_BYPASS_002)"
|
|
23
|
+
},
|
|
24
|
+
"category": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": [
|
|
27
|
+
"truth",
|
|
28
|
+
"routes",
|
|
29
|
+
"auth",
|
|
30
|
+
"env",
|
|
31
|
+
"billing",
|
|
32
|
+
"dead_ui",
|
|
33
|
+
"fake_success",
|
|
34
|
+
"ui_mismatch",
|
|
35
|
+
"contract_drift",
|
|
36
|
+
"security",
|
|
37
|
+
"coverage",
|
|
38
|
+
"quality",
|
|
39
|
+
"performance"
|
|
40
|
+
],
|
|
41
|
+
"description": "Finding category for grouping and filtering"
|
|
42
|
+
},
|
|
43
|
+
"severity": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"enum": ["BLOCK", "WARN", "INFO"],
|
|
46
|
+
"description": "BLOCK stops ship, WARN allows with warning, INFO is informational"
|
|
47
|
+
},
|
|
48
|
+
"confidence": {
|
|
49
|
+
"type": "number",
|
|
50
|
+
"minimum": 0,
|
|
51
|
+
"maximum": 1,
|
|
52
|
+
"description": "Confidence score from 0.0 (low) to 1.0 (high)"
|
|
53
|
+
},
|
|
54
|
+
"message": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"maxLength": 200,
|
|
58
|
+
"description": "Short human-readable message (displayed in lists)"
|
|
59
|
+
},
|
|
60
|
+
"explanation": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"minLength": 10,
|
|
63
|
+
"description": "Detailed explanation of why this is an issue"
|
|
64
|
+
},
|
|
65
|
+
"evidence": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"minItems": 1,
|
|
68
|
+
"items": {
|
|
69
|
+
"$ref": "#/$defs/evidence"
|
|
70
|
+
},
|
|
71
|
+
"description": "Evidence supporting this finding (at least one required)"
|
|
72
|
+
},
|
|
73
|
+
"fix_hint": {
|
|
74
|
+
"type": ["string", "null"],
|
|
75
|
+
"description": "Suggested fix or remediation steps"
|
|
76
|
+
},
|
|
77
|
+
"tags": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"items": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"pattern": "^[a-z][a-z0-9_-]*$"
|
|
82
|
+
},
|
|
83
|
+
"description": "Tags for categorization and filtering"
|
|
84
|
+
},
|
|
85
|
+
"fingerprint": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^sha256:[a-f0-9]{64}$",
|
|
88
|
+
"description": "Stable hash for deduplication across runs"
|
|
89
|
+
},
|
|
90
|
+
"first_seen": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"format": "date-time",
|
|
93
|
+
"description": "ISO 8601 timestamp when first detected"
|
|
94
|
+
},
|
|
95
|
+
"suppressed": {
|
|
96
|
+
"type": "boolean",
|
|
97
|
+
"default": false,
|
|
98
|
+
"description": "Whether this finding is suppressed via allowlist"
|
|
99
|
+
},
|
|
100
|
+
"suppression_reason": {
|
|
101
|
+
"type": ["string", "null"],
|
|
102
|
+
"description": "Reason for suppression if suppressed"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"$defs": {
|
|
106
|
+
"evidence": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"required": ["path", "lineStart", "lineEnd"],
|
|
109
|
+
"properties": {
|
|
110
|
+
"path": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"minLength": 1,
|
|
113
|
+
"description": "File path relative to project root"
|
|
114
|
+
},
|
|
115
|
+
"lineStart": {
|
|
116
|
+
"type": "integer",
|
|
117
|
+
"minimum": 1,
|
|
118
|
+
"description": "Starting line number (1-indexed)"
|
|
119
|
+
},
|
|
120
|
+
"lineEnd": {
|
|
121
|
+
"type": "integer",
|
|
122
|
+
"minimum": 1,
|
|
123
|
+
"description": "Ending line number (1-indexed)"
|
|
124
|
+
},
|
|
125
|
+
"snippet": {
|
|
126
|
+
"type": ["string", "null"],
|
|
127
|
+
"maxLength": 2000,
|
|
128
|
+
"description": "Code snippet (optional, may be truncated)"
|
|
129
|
+
},
|
|
130
|
+
"kind": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"enum": ["file", "request", "runtime", "screenshot", "diff", "trace"],
|
|
133
|
+
"default": "file",
|
|
134
|
+
"description": "Type of evidence"
|
|
135
|
+
},
|
|
136
|
+
"url": {
|
|
137
|
+
"type": ["string", "null"],
|
|
138
|
+
"format": "uri",
|
|
139
|
+
"description": "URL for runtime/request evidence"
|
|
140
|
+
},
|
|
141
|
+
"httpStatus": {
|
|
142
|
+
"type": ["integer", "null"],
|
|
143
|
+
"minimum": 100,
|
|
144
|
+
"maximum": 599,
|
|
145
|
+
"description": "HTTP status code for request evidence"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"additionalProperties": false
|
|
151
|
+
}
|