@vibecheckai/cli 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/registry.js +105 -105
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/analysis-core.js +271 -271
- package/bin/runners/lib/analyzers.js +579 -579
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-output.js +368 -368
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/detectors-v2.js +703 -703
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/entitlements-v2.js +490 -489
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/init-wizard.js +308 -308
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/missions/plan.js +69 -69
- package/bin/runners/lib/missions/templates.js +192 -192
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-engine.js +447 -447
- package/bin/runners/lib/report-html.js +1499 -1499
- package/bin/runners/lib/report-templates.js +969 -969
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/route-truth.js +477 -477
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/truth.js +667 -667
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runAuth.js +51 -0
- package/bin/runners/runClaimVerifier.js +483 -483
- package/bin/runners/runContext.js +56 -56
- package/bin/runners/runContextCompiler.js +385 -385
- package/bin/runners/runCtx.js +674 -674
- package/bin/runners/runCtxDiff.js +301 -301
- package/bin/runners/runCtxGuard.js +176 -176
- package/bin/runners/runCtxSync.js +116 -116
- package/bin/runners/runGate.js +17 -17
- package/bin/runners/runGraph.js +454 -454
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runInitGha.js +164 -164
- package/bin/runners/runInstall.js +277 -277
- package/bin/runners/runInteractive.js +388 -388
- package/bin/runners/runLabs.js +340 -340
- package/bin/runners/runMissionGenerator.js +282 -282
- package/bin/runners/runPR.js +255 -255
- package/bin/runners/runPermissions.js +304 -304
- package/bin/runners/runPreflight.js +580 -553
- package/bin/runners/runProve.js +1252 -1252
- package/bin/runners/runReality.js +1328 -1328
- package/bin/runners/runReplay.js +499 -499
- package/bin/runners/runReport.js +584 -584
- package/bin/runners/runShare.js +212 -212
- package/bin/runners/runStatus.js +138 -138
- package/bin/runners/runTruthpack.js +636 -636
- package/bin/runners/runVerify.js +272 -272
- package/bin/runners/runWatch.js +407 -407
- package/bin/vibecheck.js +2 -1
- package/mcp-server/consolidated-tools.js +804 -804
- package/mcp-server/package.json +1 -1
- package/mcp-server/tools/index.js +72 -72
- package/mcp-server/truth-context.js +581 -581
- package/mcp-server/truth-firewall-tools.js +1500 -1500
- package/package.json +1 -1
- package/bin/runners/runProof.zip +0 -0
|
@@ -1,271 +1,271 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analysis Core - Shared analysis engine for Ship and Scan
|
|
3
|
-
*
|
|
4
|
-
* Consolidates common logic:
|
|
5
|
-
* - Truthpack generation
|
|
6
|
-
* - Finding collection
|
|
7
|
-
* - Verdict calculation
|
|
8
|
-
* - Output formatting
|
|
9
|
-
*
|
|
10
|
-
* Ship = Fast path (core analyzers only)
|
|
11
|
-
* Scan = Deep path (core + extended analyzers + optional layers)
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const path = require("path");
|
|
15
|
-
const fs = require("fs");
|
|
16
|
-
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./truth");
|
|
17
|
-
const {
|
|
18
|
-
findMissingRoutes,
|
|
19
|
-
findEnvGaps,
|
|
20
|
-
findFakeSuccess,
|
|
21
|
-
findGhostAuth,
|
|
22
|
-
findStripeWebhookViolations,
|
|
23
|
-
findPaidSurfaceNotEnforced,
|
|
24
|
-
findOwnerModeBypass
|
|
25
|
-
} = require("./analyzers");
|
|
26
|
-
const { findingsFromReality } = require("./reality-findings");
|
|
27
|
-
|
|
28
|
-
// ============================================================================
|
|
29
|
-
// CORE ANALYSIS (Used by both Ship and Scan)
|
|
30
|
-
// ============================================================================
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Run core analyzers that apply to all projects
|
|
34
|
-
* @param {string} root - Project root path
|
|
35
|
-
* @param {object} truthpack - Generated truthpack
|
|
36
|
-
* @returns {Array} - Array of findings
|
|
37
|
-
*/
|
|
38
|
-
function runCoreAnalyzers(root, truthpack) {
|
|
39
|
-
return [
|
|
40
|
-
...findMissingRoutes(truthpack),
|
|
41
|
-
...findEnvGaps(truthpack),
|
|
42
|
-
...findFakeSuccess(root),
|
|
43
|
-
...findGhostAuth(truthpack, root),
|
|
44
|
-
...findStripeWebhookViolations(truthpack),
|
|
45
|
-
...findPaidSurfaceNotEnforced(truthpack),
|
|
46
|
-
...findOwnerModeBypass(root),
|
|
47
|
-
...findingsFromReality(root)
|
|
48
|
-
];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Calculate verdict from findings
|
|
53
|
-
* @param {Array} findings - Array of findings
|
|
54
|
-
* @returns {string} - SHIP | WARN | BLOCK
|
|
55
|
-
*/
|
|
56
|
-
function calculateVerdict(findings) {
|
|
57
|
-
if (findings.some(f => f.severity === "BLOCK")) return "BLOCK";
|
|
58
|
-
if (findings.some(f => f.severity === "WARN")) return "WARN";
|
|
59
|
-
return "SHIP";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Build proof graph from findings
|
|
64
|
-
* @param {Array} findings - Array of findings
|
|
65
|
-
* @param {object} truthpack - Truthpack data
|
|
66
|
-
* @param {string} root - Project root path
|
|
67
|
-
* @returns {object} - Proof graph
|
|
68
|
-
*/
|
|
69
|
-
function buildProofGraph(findings, truthpack, root) {
|
|
70
|
-
const claims = [];
|
|
71
|
-
let claimId = 0;
|
|
72
|
-
|
|
73
|
-
for (const finding of findings) {
|
|
74
|
-
const claim = {
|
|
75
|
-
id: `claim-${++claimId}`,
|
|
76
|
-
type: getClaimType(finding.category),
|
|
77
|
-
assertion: finding.title || finding.message,
|
|
78
|
-
verified: finding.severity !== 'BLOCK',
|
|
79
|
-
confidence: finding.confidence === 'high' ? 0.9 : finding.confidence === 'medium' ? 0.7 : 0.5,
|
|
80
|
-
evidence: (finding.evidence || []).map((e, i) => ({
|
|
81
|
-
id: `evidence-${claimId}-${i}`,
|
|
82
|
-
type: 'file_citation',
|
|
83
|
-
file: e.file,
|
|
84
|
-
line: parseInt(e.lines?.split('-')[0]) || e.line || 1,
|
|
85
|
-
snippet: e.snippetHash || '',
|
|
86
|
-
strength: 0.8,
|
|
87
|
-
verifiedAt: new Date().toISOString(),
|
|
88
|
-
method: 'static'
|
|
89
|
-
})),
|
|
90
|
-
gaps: [{
|
|
91
|
-
id: `gap-${claimId}`,
|
|
92
|
-
type: getGapType(finding.category),
|
|
93
|
-
description: finding.why || finding.title,
|
|
94
|
-
severity: finding.severity === 'BLOCK' ? 'critical' : finding.severity === 'WARN' ? 'medium' : 'low',
|
|
95
|
-
suggestion: (finding.fixHints || [])[0] || ''
|
|
96
|
-
}],
|
|
97
|
-
severity: finding.severity === 'BLOCK' ? 'critical' : finding.severity === 'WARN' ? 'medium' : 'low',
|
|
98
|
-
file: finding.evidence?.[0]?.file || '',
|
|
99
|
-
line: parseInt(finding.evidence?.[0]?.lines?.split('-')[0]) || 1
|
|
100
|
-
};
|
|
101
|
-
claims.push(claim);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const verifiedClaims = claims.filter(c => c.verified);
|
|
105
|
-
const failedClaims = claims.filter(c => !c.verified);
|
|
106
|
-
const allGaps = claims.flatMap(c => c.gaps);
|
|
107
|
-
|
|
108
|
-
const riskScore = Math.min(100, failedClaims.reduce((sum, c) => {
|
|
109
|
-
if (c.severity === 'critical') return sum + 30;
|
|
110
|
-
if (c.severity === 'high') return sum + 20;
|
|
111
|
-
if (c.severity === 'medium') return sum + 10;
|
|
112
|
-
return sum + 5;
|
|
113
|
-
}, 0));
|
|
114
|
-
|
|
115
|
-
const confidence = claims.length > 0
|
|
116
|
-
? claims.reduce((sum, c) => sum + c.confidence, 0) / claims.length
|
|
117
|
-
: 1.0;
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
version: '1.0.0',
|
|
121
|
-
generatedAt: new Date().toISOString(),
|
|
122
|
-
projectPath: root,
|
|
123
|
-
claims,
|
|
124
|
-
summary: {
|
|
125
|
-
totalClaims: claims.length,
|
|
126
|
-
verifiedClaims: verifiedClaims.length,
|
|
127
|
-
failedClaims: failedClaims.length,
|
|
128
|
-
gaps: allGaps.length,
|
|
129
|
-
riskScore,
|
|
130
|
-
confidence
|
|
131
|
-
},
|
|
132
|
-
verdict: calculateVerdict(findings),
|
|
133
|
-
topBlockers: failedClaims.filter(c => c.severity === 'critical' || c.severity === 'high').slice(0, 5),
|
|
134
|
-
topGaps: allGaps.filter(g => g.severity === 'critical' || g.severity === 'high').slice(0, 5)
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function getClaimType(category) {
|
|
139
|
-
const map = {
|
|
140
|
-
'MissingRoute': 'route_exists',
|
|
141
|
-
'EnvContract': 'env_declared',
|
|
142
|
-
'FakeSuccess': 'success_verified',
|
|
143
|
-
'GhostAuth': 'auth_protected',
|
|
144
|
-
'StripeWebhook': 'billing_enforced',
|
|
145
|
-
'PaidSurface': 'billing_enforced',
|
|
146
|
-
'OwnerModeBypass': 'billing_enforced',
|
|
147
|
-
'DeadUI': 'ui_wired'
|
|
148
|
-
};
|
|
149
|
-
return map[category] || 'ui_wired';
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function getGapType(category) {
|
|
153
|
-
const map = {
|
|
154
|
-
'MissingRoute': 'missing_handler',
|
|
155
|
-
'EnvContract': 'missing_verification',
|
|
156
|
-
'FakeSuccess': 'missing_verification',
|
|
157
|
-
'GhostAuth': 'missing_gate',
|
|
158
|
-
'StripeWebhook': 'missing_verification',
|
|
159
|
-
'PaidSurface': 'missing_gate',
|
|
160
|
-
'OwnerModeBypass': 'missing_gate',
|
|
161
|
-
'DeadUI': 'missing_handler'
|
|
162
|
-
};
|
|
163
|
-
return map[category] || 'untested_path';
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ============================================================================
|
|
167
|
-
// UNIFIED ANALYSIS RUNNER
|
|
168
|
-
// ============================================================================
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Run unified analysis (used by both ship and scan)
|
|
172
|
-
* @param {object} options - Analysis options
|
|
173
|
-
* @returns {object} - Analysis result with findings, verdict, proofGraph
|
|
174
|
-
*/
|
|
175
|
-
async function runAnalysis({
|
|
176
|
-
repoRoot = process.cwd(),
|
|
177
|
-
fastifyEntry = null,
|
|
178
|
-
extended = false, // If true, run extended analyzers (scan mode)
|
|
179
|
-
noWrite = false
|
|
180
|
-
} = {}) {
|
|
181
|
-
const root = repoRoot;
|
|
182
|
-
const fastEntry = fastifyEntry || detectFastifyEntry(root);
|
|
183
|
-
|
|
184
|
-
// Build truthpack
|
|
185
|
-
const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: fastEntry });
|
|
186
|
-
if (!noWrite) {
|
|
187
|
-
writeTruthpack(root, truthpack);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Run core analyzers
|
|
191
|
-
const findings = runCoreAnalyzers(root, truthpack);
|
|
192
|
-
|
|
193
|
-
// Calculate verdict
|
|
194
|
-
const verdict = calculateVerdict(findings);
|
|
195
|
-
|
|
196
|
-
// Build proof graph
|
|
197
|
-
const proofGraph = buildProofGraph(findings, truthpack, root);
|
|
198
|
-
|
|
199
|
-
// Build report
|
|
200
|
-
const report = {
|
|
201
|
-
meta: {
|
|
202
|
-
generatedAt: new Date().toISOString(),
|
|
203
|
-
verdict,
|
|
204
|
-
mode: extended ? 'scan' : 'ship'
|
|
205
|
-
},
|
|
206
|
-
truthpackHash: truthpack.index?.hashes?.truthpackHash,
|
|
207
|
-
findings,
|
|
208
|
-
proofGraph: {
|
|
209
|
-
summary: proofGraph.summary,
|
|
210
|
-
topBlockers: proofGraph.topBlockers.slice(0, 5),
|
|
211
|
-
topGaps: proofGraph.topGaps.slice(0, 5)
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// Write outputs
|
|
216
|
-
if (!noWrite) {
|
|
217
|
-
const outDir = path.join(root, ".vibecheck");
|
|
218
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
219
|
-
fs.writeFileSync(path.join(outDir, "last_ship.json"), JSON.stringify(report, null, 2));
|
|
220
|
-
fs.writeFileSync(path.join(outDir, "proof-graph.json"), JSON.stringify(proofGraph, null, 2));
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return { report, truthpack, verdict, proofGraph, findings };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ============================================================================
|
|
227
|
-
// FINDING UTILITIES
|
|
228
|
-
// ============================================================================
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Group findings by category
|
|
232
|
-
*/
|
|
233
|
-
function groupFindings(findings) {
|
|
234
|
-
const groups = {};
|
|
235
|
-
for (const f of findings) {
|
|
236
|
-
const cat = f.category || 'Other';
|
|
237
|
-
if (!groups[cat]) groups[cat] = [];
|
|
238
|
-
groups[cat].push(f);
|
|
239
|
-
}
|
|
240
|
-
return groups;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Count findings by severity
|
|
245
|
-
*/
|
|
246
|
-
function countBySeverity(findings) {
|
|
247
|
-
return {
|
|
248
|
-
block: findings.filter(f => f.severity === 'BLOCK').length,
|
|
249
|
-
warn: findings.filter(f => f.severity === 'WARN').length,
|
|
250
|
-
info: findings.filter(f => f.severity === 'INFO').length
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get top N blockers
|
|
256
|
-
*/
|
|
257
|
-
function getTopBlockers(findings, n = 5) {
|
|
258
|
-
return findings
|
|
259
|
-
.filter(f => f.severity === 'BLOCK')
|
|
260
|
-
.slice(0, n);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
module.exports = {
|
|
264
|
-
runCoreAnalyzers,
|
|
265
|
-
calculateVerdict,
|
|
266
|
-
buildProofGraph,
|
|
267
|
-
runAnalysis,
|
|
268
|
-
groupFindings,
|
|
269
|
-
countBySeverity,
|
|
270
|
-
getTopBlockers
|
|
271
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Analysis Core - Shared analysis engine for Ship and Scan
|
|
3
|
+
*
|
|
4
|
+
* Consolidates common logic:
|
|
5
|
+
* - Truthpack generation
|
|
6
|
+
* - Finding collection
|
|
7
|
+
* - Verdict calculation
|
|
8
|
+
* - Output formatting
|
|
9
|
+
*
|
|
10
|
+
* Ship = Fast path (core analyzers only)
|
|
11
|
+
* Scan = Deep path (core + extended analyzers + optional layers)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./truth");
|
|
17
|
+
const {
|
|
18
|
+
findMissingRoutes,
|
|
19
|
+
findEnvGaps,
|
|
20
|
+
findFakeSuccess,
|
|
21
|
+
findGhostAuth,
|
|
22
|
+
findStripeWebhookViolations,
|
|
23
|
+
findPaidSurfaceNotEnforced,
|
|
24
|
+
findOwnerModeBypass
|
|
25
|
+
} = require("./analyzers");
|
|
26
|
+
const { findingsFromReality } = require("./reality-findings");
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// CORE ANALYSIS (Used by both Ship and Scan)
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Run core analyzers that apply to all projects
|
|
34
|
+
* @param {string} root - Project root path
|
|
35
|
+
* @param {object} truthpack - Generated truthpack
|
|
36
|
+
* @returns {Array} - Array of findings
|
|
37
|
+
*/
|
|
38
|
+
function runCoreAnalyzers(root, truthpack) {
|
|
39
|
+
return [
|
|
40
|
+
...findMissingRoutes(truthpack),
|
|
41
|
+
...findEnvGaps(truthpack),
|
|
42
|
+
...findFakeSuccess(root),
|
|
43
|
+
...findGhostAuth(truthpack, root),
|
|
44
|
+
...findStripeWebhookViolations(truthpack),
|
|
45
|
+
...findPaidSurfaceNotEnforced(truthpack),
|
|
46
|
+
...findOwnerModeBypass(root),
|
|
47
|
+
...findingsFromReality(root)
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Calculate verdict from findings
|
|
53
|
+
* @param {Array} findings - Array of findings
|
|
54
|
+
* @returns {string} - SHIP | WARN | BLOCK
|
|
55
|
+
*/
|
|
56
|
+
function calculateVerdict(findings) {
|
|
57
|
+
if (findings.some(f => f.severity === "BLOCK")) return "BLOCK";
|
|
58
|
+
if (findings.some(f => f.severity === "WARN")) return "WARN";
|
|
59
|
+
return "SHIP";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build proof graph from findings
|
|
64
|
+
* @param {Array} findings - Array of findings
|
|
65
|
+
* @param {object} truthpack - Truthpack data
|
|
66
|
+
* @param {string} root - Project root path
|
|
67
|
+
* @returns {object} - Proof graph
|
|
68
|
+
*/
|
|
69
|
+
function buildProofGraph(findings, truthpack, root) {
|
|
70
|
+
const claims = [];
|
|
71
|
+
let claimId = 0;
|
|
72
|
+
|
|
73
|
+
for (const finding of findings) {
|
|
74
|
+
const claim = {
|
|
75
|
+
id: `claim-${++claimId}`,
|
|
76
|
+
type: getClaimType(finding.category),
|
|
77
|
+
assertion: finding.title || finding.message,
|
|
78
|
+
verified: finding.severity !== 'BLOCK',
|
|
79
|
+
confidence: finding.confidence === 'high' ? 0.9 : finding.confidence === 'medium' ? 0.7 : 0.5,
|
|
80
|
+
evidence: (finding.evidence || []).map((e, i) => ({
|
|
81
|
+
id: `evidence-${claimId}-${i}`,
|
|
82
|
+
type: 'file_citation',
|
|
83
|
+
file: e.file,
|
|
84
|
+
line: parseInt(e.lines?.split('-')[0]) || e.line || 1,
|
|
85
|
+
snippet: e.snippetHash || '',
|
|
86
|
+
strength: 0.8,
|
|
87
|
+
verifiedAt: new Date().toISOString(),
|
|
88
|
+
method: 'static'
|
|
89
|
+
})),
|
|
90
|
+
gaps: [{
|
|
91
|
+
id: `gap-${claimId}`,
|
|
92
|
+
type: getGapType(finding.category),
|
|
93
|
+
description: finding.why || finding.title,
|
|
94
|
+
severity: finding.severity === 'BLOCK' ? 'critical' : finding.severity === 'WARN' ? 'medium' : 'low',
|
|
95
|
+
suggestion: (finding.fixHints || [])[0] || ''
|
|
96
|
+
}],
|
|
97
|
+
severity: finding.severity === 'BLOCK' ? 'critical' : finding.severity === 'WARN' ? 'medium' : 'low',
|
|
98
|
+
file: finding.evidence?.[0]?.file || '',
|
|
99
|
+
line: parseInt(finding.evidence?.[0]?.lines?.split('-')[0]) || 1
|
|
100
|
+
};
|
|
101
|
+
claims.push(claim);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const verifiedClaims = claims.filter(c => c.verified);
|
|
105
|
+
const failedClaims = claims.filter(c => !c.verified);
|
|
106
|
+
const allGaps = claims.flatMap(c => c.gaps);
|
|
107
|
+
|
|
108
|
+
const riskScore = Math.min(100, failedClaims.reduce((sum, c) => {
|
|
109
|
+
if (c.severity === 'critical') return sum + 30;
|
|
110
|
+
if (c.severity === 'high') return sum + 20;
|
|
111
|
+
if (c.severity === 'medium') return sum + 10;
|
|
112
|
+
return sum + 5;
|
|
113
|
+
}, 0));
|
|
114
|
+
|
|
115
|
+
const confidence = claims.length > 0
|
|
116
|
+
? claims.reduce((sum, c) => sum + c.confidence, 0) / claims.length
|
|
117
|
+
: 1.0;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
version: '1.0.0',
|
|
121
|
+
generatedAt: new Date().toISOString(),
|
|
122
|
+
projectPath: root,
|
|
123
|
+
claims,
|
|
124
|
+
summary: {
|
|
125
|
+
totalClaims: claims.length,
|
|
126
|
+
verifiedClaims: verifiedClaims.length,
|
|
127
|
+
failedClaims: failedClaims.length,
|
|
128
|
+
gaps: allGaps.length,
|
|
129
|
+
riskScore,
|
|
130
|
+
confidence
|
|
131
|
+
},
|
|
132
|
+
verdict: calculateVerdict(findings),
|
|
133
|
+
topBlockers: failedClaims.filter(c => c.severity === 'critical' || c.severity === 'high').slice(0, 5),
|
|
134
|
+
topGaps: allGaps.filter(g => g.severity === 'critical' || g.severity === 'high').slice(0, 5)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function getClaimType(category) {
|
|
139
|
+
const map = {
|
|
140
|
+
'MissingRoute': 'route_exists',
|
|
141
|
+
'EnvContract': 'env_declared',
|
|
142
|
+
'FakeSuccess': 'success_verified',
|
|
143
|
+
'GhostAuth': 'auth_protected',
|
|
144
|
+
'StripeWebhook': 'billing_enforced',
|
|
145
|
+
'PaidSurface': 'billing_enforced',
|
|
146
|
+
'OwnerModeBypass': 'billing_enforced',
|
|
147
|
+
'DeadUI': 'ui_wired'
|
|
148
|
+
};
|
|
149
|
+
return map[category] || 'ui_wired';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getGapType(category) {
|
|
153
|
+
const map = {
|
|
154
|
+
'MissingRoute': 'missing_handler',
|
|
155
|
+
'EnvContract': 'missing_verification',
|
|
156
|
+
'FakeSuccess': 'missing_verification',
|
|
157
|
+
'GhostAuth': 'missing_gate',
|
|
158
|
+
'StripeWebhook': 'missing_verification',
|
|
159
|
+
'PaidSurface': 'missing_gate',
|
|
160
|
+
'OwnerModeBypass': 'missing_gate',
|
|
161
|
+
'DeadUI': 'missing_handler'
|
|
162
|
+
};
|
|
163
|
+
return map[category] || 'untested_path';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// UNIFIED ANALYSIS RUNNER
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Run unified analysis (used by both ship and scan)
|
|
172
|
+
* @param {object} options - Analysis options
|
|
173
|
+
* @returns {object} - Analysis result with findings, verdict, proofGraph
|
|
174
|
+
*/
|
|
175
|
+
async function runAnalysis({
|
|
176
|
+
repoRoot = process.cwd(),
|
|
177
|
+
fastifyEntry = null,
|
|
178
|
+
extended = false, // If true, run extended analyzers (scan mode)
|
|
179
|
+
noWrite = false
|
|
180
|
+
} = {}) {
|
|
181
|
+
const root = repoRoot;
|
|
182
|
+
const fastEntry = fastifyEntry || detectFastifyEntry(root);
|
|
183
|
+
|
|
184
|
+
// Build truthpack
|
|
185
|
+
const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: fastEntry });
|
|
186
|
+
if (!noWrite) {
|
|
187
|
+
writeTruthpack(root, truthpack);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Run core analyzers
|
|
191
|
+
const findings = runCoreAnalyzers(root, truthpack);
|
|
192
|
+
|
|
193
|
+
// Calculate verdict
|
|
194
|
+
const verdict = calculateVerdict(findings);
|
|
195
|
+
|
|
196
|
+
// Build proof graph
|
|
197
|
+
const proofGraph = buildProofGraph(findings, truthpack, root);
|
|
198
|
+
|
|
199
|
+
// Build report
|
|
200
|
+
const report = {
|
|
201
|
+
meta: {
|
|
202
|
+
generatedAt: new Date().toISOString(),
|
|
203
|
+
verdict,
|
|
204
|
+
mode: extended ? 'scan' : 'ship'
|
|
205
|
+
},
|
|
206
|
+
truthpackHash: truthpack.index?.hashes?.truthpackHash,
|
|
207
|
+
findings,
|
|
208
|
+
proofGraph: {
|
|
209
|
+
summary: proofGraph.summary,
|
|
210
|
+
topBlockers: proofGraph.topBlockers.slice(0, 5),
|
|
211
|
+
topGaps: proofGraph.topGaps.slice(0, 5)
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Write outputs
|
|
216
|
+
if (!noWrite) {
|
|
217
|
+
const outDir = path.join(root, ".vibecheck");
|
|
218
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
219
|
+
fs.writeFileSync(path.join(outDir, "last_ship.json"), JSON.stringify(report, null, 2));
|
|
220
|
+
fs.writeFileSync(path.join(outDir, "proof-graph.json"), JSON.stringify(proofGraph, null, 2));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { report, truthpack, verdict, proofGraph, findings };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// FINDING UTILITIES
|
|
228
|
+
// ============================================================================
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Group findings by category
|
|
232
|
+
*/
|
|
233
|
+
function groupFindings(findings) {
|
|
234
|
+
const groups = {};
|
|
235
|
+
for (const f of findings) {
|
|
236
|
+
const cat = f.category || 'Other';
|
|
237
|
+
if (!groups[cat]) groups[cat] = [];
|
|
238
|
+
groups[cat].push(f);
|
|
239
|
+
}
|
|
240
|
+
return groups;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Count findings by severity
|
|
245
|
+
*/
|
|
246
|
+
function countBySeverity(findings) {
|
|
247
|
+
return {
|
|
248
|
+
block: findings.filter(f => f.severity === 'BLOCK').length,
|
|
249
|
+
warn: findings.filter(f => f.severity === 'WARN').length,
|
|
250
|
+
info: findings.filter(f => f.severity === 'INFO').length
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get top N blockers
|
|
256
|
+
*/
|
|
257
|
+
function getTopBlockers(findings, n = 5) {
|
|
258
|
+
return findings
|
|
259
|
+
.filter(f => f.severity === 'BLOCK')
|
|
260
|
+
.slice(0, n);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
module.exports = {
|
|
264
|
+
runCoreAnalyzers,
|
|
265
|
+
calculateVerdict,
|
|
266
|
+
buildProofGraph,
|
|
267
|
+
runAnalysis,
|
|
268
|
+
groupFindings,
|
|
269
|
+
countBySeverity,
|
|
270
|
+
getTopBlockers
|
|
271
|
+
};
|