@vibecheckai/cli 3.2.2 → 3.2.4
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/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- 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-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/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/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/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- 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/global-flags.js +213 -213
- 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/interactive-menu.js +1496 -1496
- 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/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-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- 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/scan-output.js +525 -190
- 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/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- 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/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,281 +1,281 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Findings Schema v2
|
|
3
|
-
*
|
|
4
|
-
* Per spec section 7.1, findings must include:
|
|
5
|
-
* - fingerprint: stable hash for dedupe across runs
|
|
6
|
-
* - scope: client|server|runtime|contracts
|
|
7
|
-
* - repro: minimal reproduction steps (for runtime findings)
|
|
8
|
-
* - related: linked finding IDs
|
|
9
|
-
*
|
|
10
|
-
* This module provides helpers to create and validate v2 findings.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
"use strict";
|
|
14
|
-
|
|
15
|
-
const crypto = require("crypto");
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generate a stable fingerprint for a finding
|
|
19
|
-
* Used for dedupe across runs - same issue = same fingerprint
|
|
20
|
-
*/
|
|
21
|
-
function generateFingerprint(category, ...identifiers) {
|
|
22
|
-
const data = [category, ...identifiers.filter(Boolean)].join("|");
|
|
23
|
-
return crypto.createHash("sha256").update(data).digest("hex").slice(0, 12);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Infer scope from finding category and evidence
|
|
28
|
-
*/
|
|
29
|
-
function inferScope(category, evidence = []) {
|
|
30
|
-
const scopeMap = {
|
|
31
|
-
// Client-side findings
|
|
32
|
-
'DeadUI': 'runtime',
|
|
33
|
-
'FakeSuccess': 'client',
|
|
34
|
-
|
|
35
|
-
// Server-side findings
|
|
36
|
-
'MissingRoute': 'server',
|
|
37
|
-
'GhostAuth': 'server',
|
|
38
|
-
'StripeWebhook': 'server',
|
|
39
|
-
'PaidSurface': 'server',
|
|
40
|
-
'OwnerModeBypass': 'server',
|
|
41
|
-
|
|
42
|
-
// Contract findings
|
|
43
|
-
'ContractDrift': 'contracts',
|
|
44
|
-
'EnvContract': 'contracts',
|
|
45
|
-
|
|
46
|
-
// Runtime findings
|
|
47
|
-
'AuthCoverage': 'runtime',
|
|
48
|
-
'RouteCoverage': 'runtime',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return scopeMap[category] || 'server';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Create a v2-compliant finding
|
|
56
|
-
*/
|
|
57
|
-
function createFinding({
|
|
58
|
-
id,
|
|
59
|
-
category,
|
|
60
|
-
type,
|
|
61
|
-
severity,
|
|
62
|
-
title,
|
|
63
|
-
message,
|
|
64
|
-
why,
|
|
65
|
-
confidence = 'med',
|
|
66
|
-
evidence = [],
|
|
67
|
-
fixHints = [],
|
|
68
|
-
repro = null,
|
|
69
|
-
related = [],
|
|
70
|
-
scope = null,
|
|
71
|
-
source = null,
|
|
72
|
-
}) {
|
|
73
|
-
// Generate stable fingerprint
|
|
74
|
-
const fingerprint = generateFingerprint(
|
|
75
|
-
category,
|
|
76
|
-
type || title,
|
|
77
|
-
evidence[0]?.file,
|
|
78
|
-
evidence[0]?.lines
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Infer scope if not provided
|
|
82
|
-
const resolvedScope = scope || inferScope(category, evidence);
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
// Core fields
|
|
86
|
-
id: id || `F_${category.toUpperCase()}_${fingerprint}`,
|
|
87
|
-
category,
|
|
88
|
-
type: type || category.toLowerCase(),
|
|
89
|
-
severity,
|
|
90
|
-
title,
|
|
91
|
-
message: message || title,
|
|
92
|
-
why,
|
|
93
|
-
confidence,
|
|
94
|
-
|
|
95
|
-
// Evidence
|
|
96
|
-
evidence,
|
|
97
|
-
fixHints,
|
|
98
|
-
|
|
99
|
-
// v2 fields (spec 7.1)
|
|
100
|
-
fingerprint,
|
|
101
|
-
scope: resolvedScope,
|
|
102
|
-
repro: repro || null,
|
|
103
|
-
related: related || [],
|
|
104
|
-
|
|
105
|
-
// Metadata
|
|
106
|
-
source: source || 'static',
|
|
107
|
-
createdAt: new Date().toISOString(),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Create a runtime finding with reproduction steps
|
|
113
|
-
*/
|
|
114
|
-
function createRuntimeFinding({
|
|
115
|
-
category,
|
|
116
|
-
type,
|
|
117
|
-
severity,
|
|
118
|
-
title,
|
|
119
|
-
why,
|
|
120
|
-
url,
|
|
121
|
-
action,
|
|
122
|
-
expected,
|
|
123
|
-
actual,
|
|
124
|
-
screenshot = null,
|
|
125
|
-
trace = null,
|
|
126
|
-
evidence = [],
|
|
127
|
-
related = [],
|
|
128
|
-
}) {
|
|
129
|
-
const repro = {
|
|
130
|
-
url,
|
|
131
|
-
steps: [
|
|
132
|
-
`Navigate to ${url}`,
|
|
133
|
-
action ? `Action: ${action}` : null,
|
|
134
|
-
`Expected: ${expected}`,
|
|
135
|
-
`Actual: ${actual}`,
|
|
136
|
-
].filter(Boolean),
|
|
137
|
-
screenshot,
|
|
138
|
-
trace,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return createFinding({
|
|
142
|
-
category,
|
|
143
|
-
type,
|
|
144
|
-
severity,
|
|
145
|
-
title,
|
|
146
|
-
why,
|
|
147
|
-
evidence,
|
|
148
|
-
related,
|
|
149
|
-
repro,
|
|
150
|
-
scope: 'runtime',
|
|
151
|
-
source: 'reality',
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Create a contract drift finding
|
|
157
|
-
*/
|
|
158
|
-
function createDriftFinding({
|
|
159
|
-
type,
|
|
160
|
-
severity,
|
|
161
|
-
title,
|
|
162
|
-
message,
|
|
163
|
-
subject,
|
|
164
|
-
contractFile,
|
|
165
|
-
evidence = [],
|
|
166
|
-
}) {
|
|
167
|
-
return createFinding({
|
|
168
|
-
category: 'ContractDrift',
|
|
169
|
-
type,
|
|
170
|
-
severity,
|
|
171
|
-
title,
|
|
172
|
-
message,
|
|
173
|
-
why: 'Contracts are the source of truth. Drift causes AI hallucinations.',
|
|
174
|
-
evidence: evidence.length > 0 ? evidence : [{
|
|
175
|
-
file: `.vibecheck/contracts/${contractFile || 'contracts'}`,
|
|
176
|
-
lines: '1',
|
|
177
|
-
reason: title,
|
|
178
|
-
}],
|
|
179
|
-
fixHints: [
|
|
180
|
-
"Run 'vibecheck ctx sync' to update contracts",
|
|
181
|
-
"Or revert the code change if unintended",
|
|
182
|
-
],
|
|
183
|
-
scope: 'contracts',
|
|
184
|
-
source: 'drift',
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Link related findings
|
|
190
|
-
*/
|
|
191
|
-
function linkFindings(findings) {
|
|
192
|
-
// Group findings by file
|
|
193
|
-
const byFile = new Map();
|
|
194
|
-
for (const f of findings) {
|
|
195
|
-
for (const ev of f.evidence || []) {
|
|
196
|
-
if (ev.file) {
|
|
197
|
-
if (!byFile.has(ev.file)) byFile.set(ev.file, []);
|
|
198
|
-
byFile.get(ev.file).push(f.id);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Link findings that share files
|
|
204
|
-
for (const f of findings) {
|
|
205
|
-
const relatedIds = new Set();
|
|
206
|
-
for (const ev of f.evidence || []) {
|
|
207
|
-
if (ev.file && byFile.has(ev.file)) {
|
|
208
|
-
for (const id of byFile.get(ev.file)) {
|
|
209
|
-
if (id !== f.id) relatedIds.add(id);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
f.related = [...relatedIds].slice(0, 5); // Max 5 related
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return findings;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Validate a finding against v2 schema
|
|
221
|
-
*/
|
|
222
|
-
function validateFinding(finding) {
|
|
223
|
-
const errors = [];
|
|
224
|
-
|
|
225
|
-
// Required fields
|
|
226
|
-
if (!finding.id) errors.push('Missing id');
|
|
227
|
-
if (!finding.category) errors.push('Missing category');
|
|
228
|
-
if (!finding.severity) errors.push('Missing severity');
|
|
229
|
-
if (!['BLOCK', 'WARN', 'INFO'].includes(finding.severity)) {
|
|
230
|
-
errors.push(`Invalid severity: ${finding.severity}`);
|
|
231
|
-
}
|
|
232
|
-
if (!finding.title) errors.push('Missing title');
|
|
233
|
-
|
|
234
|
-
// v2 fields
|
|
235
|
-
if (!finding.fingerprint) errors.push('Missing fingerprint (v2)');
|
|
236
|
-
if (!finding.scope) errors.push('Missing scope (v2)');
|
|
237
|
-
if (!['client', 'server', 'runtime', 'contracts'].includes(finding.scope)) {
|
|
238
|
-
errors.push(`Invalid scope: ${finding.scope}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
valid: errors.length === 0,
|
|
243
|
-
errors,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Migrate a v1 finding to v2 format
|
|
249
|
-
*/
|
|
250
|
-
function migrateFindingToV2(finding) {
|
|
251
|
-
// Already has fingerprint = already v2
|
|
252
|
-
if (finding.fingerprint && finding.scope) {
|
|
253
|
-
return finding;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Generate fingerprint
|
|
257
|
-
const fingerprint = generateFingerprint(
|
|
258
|
-
finding.category || 'Unknown',
|
|
259
|
-
finding.type || finding.title,
|
|
260
|
-
finding.evidence?.[0]?.file
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
...finding,
|
|
265
|
-
fingerprint,
|
|
266
|
-
scope: finding.scope || inferScope(finding.category, finding.evidence),
|
|
267
|
-
repro: finding.repro || null,
|
|
268
|
-
related: finding.related || [],
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
module.exports = {
|
|
273
|
-
generateFingerprint,
|
|
274
|
-
inferScope,
|
|
275
|
-
createFinding,
|
|
276
|
-
createRuntimeFinding,
|
|
277
|
-
createDriftFinding,
|
|
278
|
-
linkFindings,
|
|
279
|
-
validateFinding,
|
|
280
|
-
migrateFindingToV2,
|
|
281
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Findings Schema v2
|
|
3
|
+
*
|
|
4
|
+
* Per spec section 7.1, findings must include:
|
|
5
|
+
* - fingerprint: stable hash for dedupe across runs
|
|
6
|
+
* - scope: client|server|runtime|contracts
|
|
7
|
+
* - repro: minimal reproduction steps (for runtime findings)
|
|
8
|
+
* - related: linked finding IDs
|
|
9
|
+
*
|
|
10
|
+
* This module provides helpers to create and validate v2 findings.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const crypto = require("crypto");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate a stable fingerprint for a finding
|
|
19
|
+
* Used for dedupe across runs - same issue = same fingerprint
|
|
20
|
+
*/
|
|
21
|
+
function generateFingerprint(category, ...identifiers) {
|
|
22
|
+
const data = [category, ...identifiers.filter(Boolean)].join("|");
|
|
23
|
+
return crypto.createHash("sha256").update(data).digest("hex").slice(0, 12);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Infer scope from finding category and evidence
|
|
28
|
+
*/
|
|
29
|
+
function inferScope(category, evidence = []) {
|
|
30
|
+
const scopeMap = {
|
|
31
|
+
// Client-side findings
|
|
32
|
+
'DeadUI': 'runtime',
|
|
33
|
+
'FakeSuccess': 'client',
|
|
34
|
+
|
|
35
|
+
// Server-side findings
|
|
36
|
+
'MissingRoute': 'server',
|
|
37
|
+
'GhostAuth': 'server',
|
|
38
|
+
'StripeWebhook': 'server',
|
|
39
|
+
'PaidSurface': 'server',
|
|
40
|
+
'OwnerModeBypass': 'server',
|
|
41
|
+
|
|
42
|
+
// Contract findings
|
|
43
|
+
'ContractDrift': 'contracts',
|
|
44
|
+
'EnvContract': 'contracts',
|
|
45
|
+
|
|
46
|
+
// Runtime findings
|
|
47
|
+
'AuthCoverage': 'runtime',
|
|
48
|
+
'RouteCoverage': 'runtime',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return scopeMap[category] || 'server';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a v2-compliant finding
|
|
56
|
+
*/
|
|
57
|
+
function createFinding({
|
|
58
|
+
id,
|
|
59
|
+
category,
|
|
60
|
+
type,
|
|
61
|
+
severity,
|
|
62
|
+
title,
|
|
63
|
+
message,
|
|
64
|
+
why,
|
|
65
|
+
confidence = 'med',
|
|
66
|
+
evidence = [],
|
|
67
|
+
fixHints = [],
|
|
68
|
+
repro = null,
|
|
69
|
+
related = [],
|
|
70
|
+
scope = null,
|
|
71
|
+
source = null,
|
|
72
|
+
}) {
|
|
73
|
+
// Generate stable fingerprint
|
|
74
|
+
const fingerprint = generateFingerprint(
|
|
75
|
+
category,
|
|
76
|
+
type || title,
|
|
77
|
+
evidence[0]?.file,
|
|
78
|
+
evidence[0]?.lines
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Infer scope if not provided
|
|
82
|
+
const resolvedScope = scope || inferScope(category, evidence);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
// Core fields
|
|
86
|
+
id: id || `F_${category.toUpperCase()}_${fingerprint}`,
|
|
87
|
+
category,
|
|
88
|
+
type: type || category.toLowerCase(),
|
|
89
|
+
severity,
|
|
90
|
+
title,
|
|
91
|
+
message: message || title,
|
|
92
|
+
why,
|
|
93
|
+
confidence,
|
|
94
|
+
|
|
95
|
+
// Evidence
|
|
96
|
+
evidence,
|
|
97
|
+
fixHints,
|
|
98
|
+
|
|
99
|
+
// v2 fields (spec 7.1)
|
|
100
|
+
fingerprint,
|
|
101
|
+
scope: resolvedScope,
|
|
102
|
+
repro: repro || null,
|
|
103
|
+
related: related || [],
|
|
104
|
+
|
|
105
|
+
// Metadata
|
|
106
|
+
source: source || 'static',
|
|
107
|
+
createdAt: new Date().toISOString(),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create a runtime finding with reproduction steps
|
|
113
|
+
*/
|
|
114
|
+
function createRuntimeFinding({
|
|
115
|
+
category,
|
|
116
|
+
type,
|
|
117
|
+
severity,
|
|
118
|
+
title,
|
|
119
|
+
why,
|
|
120
|
+
url,
|
|
121
|
+
action,
|
|
122
|
+
expected,
|
|
123
|
+
actual,
|
|
124
|
+
screenshot = null,
|
|
125
|
+
trace = null,
|
|
126
|
+
evidence = [],
|
|
127
|
+
related = [],
|
|
128
|
+
}) {
|
|
129
|
+
const repro = {
|
|
130
|
+
url,
|
|
131
|
+
steps: [
|
|
132
|
+
`Navigate to ${url}`,
|
|
133
|
+
action ? `Action: ${action}` : null,
|
|
134
|
+
`Expected: ${expected}`,
|
|
135
|
+
`Actual: ${actual}`,
|
|
136
|
+
].filter(Boolean),
|
|
137
|
+
screenshot,
|
|
138
|
+
trace,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return createFinding({
|
|
142
|
+
category,
|
|
143
|
+
type,
|
|
144
|
+
severity,
|
|
145
|
+
title,
|
|
146
|
+
why,
|
|
147
|
+
evidence,
|
|
148
|
+
related,
|
|
149
|
+
repro,
|
|
150
|
+
scope: 'runtime',
|
|
151
|
+
source: 'reality',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a contract drift finding
|
|
157
|
+
*/
|
|
158
|
+
function createDriftFinding({
|
|
159
|
+
type,
|
|
160
|
+
severity,
|
|
161
|
+
title,
|
|
162
|
+
message,
|
|
163
|
+
subject,
|
|
164
|
+
contractFile,
|
|
165
|
+
evidence = [],
|
|
166
|
+
}) {
|
|
167
|
+
return createFinding({
|
|
168
|
+
category: 'ContractDrift',
|
|
169
|
+
type,
|
|
170
|
+
severity,
|
|
171
|
+
title,
|
|
172
|
+
message,
|
|
173
|
+
why: 'Contracts are the source of truth. Drift causes AI hallucinations.',
|
|
174
|
+
evidence: evidence.length > 0 ? evidence : [{
|
|
175
|
+
file: `.vibecheck/contracts/${contractFile || 'contracts'}`,
|
|
176
|
+
lines: '1',
|
|
177
|
+
reason: title,
|
|
178
|
+
}],
|
|
179
|
+
fixHints: [
|
|
180
|
+
"Run 'vibecheck ctx sync' to update contracts",
|
|
181
|
+
"Or revert the code change if unintended",
|
|
182
|
+
],
|
|
183
|
+
scope: 'contracts',
|
|
184
|
+
source: 'drift',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Link related findings
|
|
190
|
+
*/
|
|
191
|
+
function linkFindings(findings) {
|
|
192
|
+
// Group findings by file
|
|
193
|
+
const byFile = new Map();
|
|
194
|
+
for (const f of findings) {
|
|
195
|
+
for (const ev of f.evidence || []) {
|
|
196
|
+
if (ev.file) {
|
|
197
|
+
if (!byFile.has(ev.file)) byFile.set(ev.file, []);
|
|
198
|
+
byFile.get(ev.file).push(f.id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Link findings that share files
|
|
204
|
+
for (const f of findings) {
|
|
205
|
+
const relatedIds = new Set();
|
|
206
|
+
for (const ev of f.evidence || []) {
|
|
207
|
+
if (ev.file && byFile.has(ev.file)) {
|
|
208
|
+
for (const id of byFile.get(ev.file)) {
|
|
209
|
+
if (id !== f.id) relatedIds.add(id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
f.related = [...relatedIds].slice(0, 5); // Max 5 related
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return findings;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Validate a finding against v2 schema
|
|
221
|
+
*/
|
|
222
|
+
function validateFinding(finding) {
|
|
223
|
+
const errors = [];
|
|
224
|
+
|
|
225
|
+
// Required fields
|
|
226
|
+
if (!finding.id) errors.push('Missing id');
|
|
227
|
+
if (!finding.category) errors.push('Missing category');
|
|
228
|
+
if (!finding.severity) errors.push('Missing severity');
|
|
229
|
+
if (!['BLOCK', 'WARN', 'INFO'].includes(finding.severity)) {
|
|
230
|
+
errors.push(`Invalid severity: ${finding.severity}`);
|
|
231
|
+
}
|
|
232
|
+
if (!finding.title) errors.push('Missing title');
|
|
233
|
+
|
|
234
|
+
// v2 fields
|
|
235
|
+
if (!finding.fingerprint) errors.push('Missing fingerprint (v2)');
|
|
236
|
+
if (!finding.scope) errors.push('Missing scope (v2)');
|
|
237
|
+
if (!['client', 'server', 'runtime', 'contracts'].includes(finding.scope)) {
|
|
238
|
+
errors.push(`Invalid scope: ${finding.scope}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
valid: errors.length === 0,
|
|
243
|
+
errors,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Migrate a v1 finding to v2 format
|
|
249
|
+
*/
|
|
250
|
+
function migrateFindingToV2(finding) {
|
|
251
|
+
// Already has fingerprint = already v2
|
|
252
|
+
if (finding.fingerprint && finding.scope) {
|
|
253
|
+
return finding;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Generate fingerprint
|
|
257
|
+
const fingerprint = generateFingerprint(
|
|
258
|
+
finding.category || 'Unknown',
|
|
259
|
+
finding.type || finding.title,
|
|
260
|
+
finding.evidence?.[0]?.file
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
...finding,
|
|
265
|
+
fingerprint,
|
|
266
|
+
scope: finding.scope || inferScope(finding.category, finding.evidence),
|
|
267
|
+
repro: finding.repro || null,
|
|
268
|
+
related: finding.related || [],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
generateFingerprint,
|
|
274
|
+
inferScope,
|
|
275
|
+
createFinding,
|
|
276
|
+
createRuntimeFinding,
|
|
277
|
+
createDriftFinding,
|
|
278
|
+
linkFindings,
|
|
279
|
+
validateFinding,
|
|
280
|
+
migrateFindingToV2,
|
|
281
|
+
};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
// bin/runners/lib/firewall-prompt.js
|
|
2
|
-
function buildRealityFirewall({ truthpackSummary, mission, template, findings, fileSnippets, allowedFiles }) {
|
|
3
|
-
return `
|
|
4
|
-
You are Vibecheck Fix Engine.
|
|
5
|
-
|
|
6
|
-
REALITY RULES (non-negotiable):
|
|
7
|
-
- Do NOT invent files, routes, env vars, middleware, or functions.
|
|
8
|
-
- You may ONLY edit files in ALLOWED_FILES.
|
|
9
|
-
- Use ONLY the provided evidence/snippets.
|
|
10
|
-
- If evidence is insufficient, return {"status":"needs_info","questions":[...]} and STOP.
|
|
11
|
-
- Output MUST be valid JSON only. No markdown. No commentary.
|
|
12
|
-
|
|
13
|
-
ALLOWED_FILES:
|
|
14
|
-
${JSON.stringify(allowedFiles || [], null, 2)}
|
|
15
|
-
|
|
16
|
-
MISSION TEMPLATE (how to fix):
|
|
17
|
-
${JSON.stringify(template || {}, null, 2)}
|
|
18
|
-
|
|
19
|
-
OUTPUT SCHEMA (strict):
|
|
20
|
-
{
|
|
21
|
-
"status": "ok" | "needs_info" | "cannot_fix",
|
|
22
|
-
"summary": "one sentence",
|
|
23
|
-
"edits": [
|
|
24
|
-
{
|
|
25
|
-
"path": "relative/file/path (must be in ALLOWED_FILES)",
|
|
26
|
-
"diff": "unified diff starting with ---/+++",
|
|
27
|
-
"reason": "why this edit fixes the mission"
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"notes": ["optional"],
|
|
31
|
-
"questions": ["only when needs_info"]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
TRUTHPACK SUMMARY:
|
|
35
|
-
${JSON.stringify(truthpackSummary, null, 2)}
|
|
36
|
-
|
|
37
|
-
MISSION:
|
|
38
|
-
${JSON.stringify(mission, null, 2)}
|
|
39
|
-
|
|
40
|
-
TARGET FINDINGS:
|
|
41
|
-
${JSON.stringify(findings, null, 2)}
|
|
42
|
-
|
|
43
|
-
EVIDENCE SNIPPETS:
|
|
44
|
-
${JSON.stringify(fileSnippets, null, 2)}
|
|
45
|
-
|
|
46
|
-
TASK:
|
|
47
|
-
Produce the smallest correct patch(es) to satisfy the mission success criteria.`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = { buildRealityFirewall };
|
|
1
|
+
// bin/runners/lib/firewall-prompt.js
|
|
2
|
+
function buildRealityFirewall({ truthpackSummary, mission, template, findings, fileSnippets, allowedFiles }) {
|
|
3
|
+
return `
|
|
4
|
+
You are Vibecheck Fix Engine.
|
|
5
|
+
|
|
6
|
+
REALITY RULES (non-negotiable):
|
|
7
|
+
- Do NOT invent files, routes, env vars, middleware, or functions.
|
|
8
|
+
- You may ONLY edit files in ALLOWED_FILES.
|
|
9
|
+
- Use ONLY the provided evidence/snippets.
|
|
10
|
+
- If evidence is insufficient, return {"status":"needs_info","questions":[...]} and STOP.
|
|
11
|
+
- Output MUST be valid JSON only. No markdown. No commentary.
|
|
12
|
+
|
|
13
|
+
ALLOWED_FILES:
|
|
14
|
+
${JSON.stringify(allowedFiles || [], null, 2)}
|
|
15
|
+
|
|
16
|
+
MISSION TEMPLATE (how to fix):
|
|
17
|
+
${JSON.stringify(template || {}, null, 2)}
|
|
18
|
+
|
|
19
|
+
OUTPUT SCHEMA (strict):
|
|
20
|
+
{
|
|
21
|
+
"status": "ok" | "needs_info" | "cannot_fix",
|
|
22
|
+
"summary": "one sentence",
|
|
23
|
+
"edits": [
|
|
24
|
+
{
|
|
25
|
+
"path": "relative/file/path (must be in ALLOWED_FILES)",
|
|
26
|
+
"diff": "unified diff starting with ---/+++",
|
|
27
|
+
"reason": "why this edit fixes the mission"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"notes": ["optional"],
|
|
31
|
+
"questions": ["only when needs_info"]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
TRUTHPACK SUMMARY:
|
|
35
|
+
${JSON.stringify(truthpackSummary, null, 2)}
|
|
36
|
+
|
|
37
|
+
MISSION:
|
|
38
|
+
${JSON.stringify(mission, null, 2)}
|
|
39
|
+
|
|
40
|
+
TARGET FINDINGS:
|
|
41
|
+
${JSON.stringify(findings, null, 2)}
|
|
42
|
+
|
|
43
|
+
EVIDENCE SNIPPETS:
|
|
44
|
+
${JSON.stringify(fileSnippets, null, 2)}
|
|
45
|
+
|
|
46
|
+
TASK:
|
|
47
|
+
Produce the smallest correct patch(es) to satisfy the mission success criteria.`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { buildRealityFirewall };
|