@vibecheckai/cli 3.2.5 → 3.3.0
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 +192 -5
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
- package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
- package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
- package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
- package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
- package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
- package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
- package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
- package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
- package/bin/runners/lib/agent-firewall/logger.js +141 -0
- package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
- package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
- package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
- package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
- package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
- package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
- package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
- package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
- package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
- package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
- package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
- package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
- package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
- package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
- package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
- package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
- package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
- package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
- package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
- package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
- package/bin/runners/lib/analyzers.js +81 -18
- package/bin/runners/lib/api-client.js +269 -0
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/authority-badge.js +425 -0
- 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 +7 -1
- 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/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/error-handler.js +16 -9
- package/bin/runners/lib/exit-codes.js +275 -0
- 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 +37 -0
- 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/help-formatter.js +413 -0
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/logger.js +38 -0
- 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.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/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/unified-cli-output.js +604 -0
- package/bin/runners/lib/upsell.js +658 -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/runAgent.d.ts +5 -0
- package/bin/runners/runApprove.js +1200 -0
- package/bin/runners/runAuth.js +324 -95
- package/bin/runners/runCheckpoint.js +39 -21
- package/bin/runners/runClassify.js +859 -0
- package/bin/runners/runContext.js +136 -24
- package/bin/runners/runDoctor.js +108 -68
- package/bin/runners/runFirewall.d.ts +5 -0
- package/bin/runners/runFirewallHook.d.ts +5 -0
- package/bin/runners/runFix.js +6 -5
- package/bin/runners/runGuard.js +262 -168
- package/bin/runners/runInit.js +3 -2
- package/bin/runners/runMcp.js +130 -52
- package/bin/runners/runPolish.js +43 -20
- package/bin/runners/runProve.js +1 -2
- package/bin/runners/runReport.js +3 -2
- package/bin/runners/runScan.js +145 -44
- package/bin/runners/runShip.js +3 -4
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/runners/runValidate.js +19 -2
- package/bin/runners/runWatch.js +104 -53
- package/bin/vibecheck.js +106 -19
- package/mcp-server/HARDENING_SUMMARY.md +299 -0
- package/mcp-server/agent-firewall-interceptor.js +367 -31
- package/mcp-server/authority-tools.js +569 -0
- package/mcp-server/conductor/conflict-resolver.js +588 -0
- package/mcp-server/conductor/execution-planner.js +544 -0
- package/mcp-server/conductor/index.js +377 -0
- package/mcp-server/conductor/lock-manager.js +615 -0
- package/mcp-server/conductor/request-queue.js +550 -0
- package/mcp-server/conductor/session-manager.js +500 -0
- package/mcp-server/conductor/tools.js +510 -0
- package/mcp-server/index.js +1199 -208
- package/mcp-server/lib/api-client.cjs +305 -0
- package/mcp-server/lib/logger.cjs +30 -0
- package/mcp-server/logger.js +173 -0
- package/mcp-server/package.json +2 -2
- package/mcp-server/premium-tools.js +2 -2
- package/mcp-server/tier-auth.js +351 -136
- package/mcp-server/tools/index.js +72 -72
- package/mcp-server/truth-firewall-tools.js +145 -15
- package/mcp-server/vibecheck-tools.js +2 -2
- package/package.json +2 -3
- package/mcp-server/index.old.js +0 -4137
- package/mcp-server/package-lock.json +0 -165
|
@@ -128,6 +128,41 @@ function shouldShowBanner(flags = {}) {
|
|
|
128
128
|
return true;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
132
|
+
// OUTPUT SUPPRESSION CHECK
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if non-essential output should be suppressed
|
|
137
|
+
* Use this before any console output that isn't errors or JSON
|
|
138
|
+
*
|
|
139
|
+
* @param {object} flags - Parsed flags object
|
|
140
|
+
* @returns {boolean} true if output should be suppressed
|
|
141
|
+
*/
|
|
142
|
+
function shouldSuppressOutput(flags = {}) {
|
|
143
|
+
// Suppress if quiet or CI mode
|
|
144
|
+
if (flags.quiet || flags.ci) return true;
|
|
145
|
+
|
|
146
|
+
// Suppress if JSON mode (only structured output allowed)
|
|
147
|
+
if (flags.json) return true;
|
|
148
|
+
|
|
149
|
+
// Check environment
|
|
150
|
+
if (process.env.VIBECHECK_QUIET === "true") return true;
|
|
151
|
+
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if we're in JSON output mode
|
|
157
|
+
* Use this to decide between pretty output and structured JSON
|
|
158
|
+
*
|
|
159
|
+
* @param {object} flags - Parsed flags object
|
|
160
|
+
* @returns {boolean} true if JSON output is requested
|
|
161
|
+
*/
|
|
162
|
+
function isJsonMode(flags = {}) {
|
|
163
|
+
return flags.json === true;
|
|
164
|
+
}
|
|
165
|
+
|
|
131
166
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
132
167
|
// CI DETECTION
|
|
133
168
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -204,6 +239,8 @@ function loadConfig(flags = {}) {
|
|
|
204
239
|
module.exports = {
|
|
205
240
|
parseGlobalFlags,
|
|
206
241
|
shouldShowBanner,
|
|
242
|
+
shouldSuppressOutput,
|
|
243
|
+
isJsonMode,
|
|
207
244
|
isCI,
|
|
208
245
|
loadConfig,
|
|
209
246
|
lazy,
|
|
@@ -1,265 +1,265 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Graph Builder
|
|
3
|
-
* Builds the complete ProofGraph from static and runtime edges
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
const crypto = require("crypto");
|
|
9
|
-
|
|
10
|
-
function sha256(text) {
|
|
11
|
-
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Build ProofGraph from extracted nodes and edges
|
|
16
|
-
*/
|
|
17
|
-
function buildProofGraph({ nodes, edges, runtimeEdges = [], meta = {} }) {
|
|
18
|
-
const graph = {
|
|
19
|
-
meta: {
|
|
20
|
-
version: "1.0.0",
|
|
21
|
-
generatedAt: new Date().toISOString(),
|
|
22
|
-
commit: meta.commit || process.env.VIBECHECK_COMMIT_SHA || "unknown",
|
|
23
|
-
...meta
|
|
24
|
-
},
|
|
25
|
-
nodes: [],
|
|
26
|
-
edges: [],
|
|
27
|
-
brokenEdges: [],
|
|
28
|
-
coverage: {
|
|
29
|
-
static: 0,
|
|
30
|
-
runtime: 0,
|
|
31
|
-
total: 0
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Dedupe and add nodes
|
|
36
|
-
const nodeMap = new Map();
|
|
37
|
-
for (const n of nodes) {
|
|
38
|
-
if (!nodeMap.has(n.id)) {
|
|
39
|
-
nodeMap.set(n.id, n);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
graph.nodes = Array.from(nodeMap.values());
|
|
43
|
-
|
|
44
|
-
// Process edges, separate broken ones
|
|
45
|
-
const brokenEdges = [];
|
|
46
|
-
const validEdges = [];
|
|
47
|
-
|
|
48
|
-
for (const edge of edges) {
|
|
49
|
-
if (edge.broken) {
|
|
50
|
-
brokenEdges.push({
|
|
51
|
-
edge: {
|
|
52
|
-
id: edge.id,
|
|
53
|
-
from: edge.from,
|
|
54
|
-
to: edge.to,
|
|
55
|
-
type: edge.type,
|
|
56
|
-
confidence: edge.confidence || "low"
|
|
57
|
-
},
|
|
58
|
-
reason: edge.brokenReason || "Edge target not found",
|
|
59
|
-
severity: determineSeverity(edge),
|
|
60
|
-
route: edge.toRoute || null
|
|
61
|
-
});
|
|
62
|
-
} else {
|
|
63
|
-
validEdges.push({
|
|
64
|
-
id: edge.id,
|
|
65
|
-
from: edge.from,
|
|
66
|
-
to: edge.to,
|
|
67
|
-
type: edge.type,
|
|
68
|
-
confidence: edge.confidence || "med",
|
|
69
|
-
verifiedAt: "static"
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add runtime edges
|
|
75
|
-
for (const re of runtimeEdges) {
|
|
76
|
-
const existing = validEdges.find(e => e.from === re.from && e.to === re.to);
|
|
77
|
-
if (existing) {
|
|
78
|
-
existing.verifiedAt = "runtime";
|
|
79
|
-
existing.runtimeData = re.data;
|
|
80
|
-
} else {
|
|
81
|
-
validEdges.push({
|
|
82
|
-
id: re.id || `runtime_${sha256(re.from + re.to)}`,
|
|
83
|
-
from: re.from,
|
|
84
|
-
to: re.to,
|
|
85
|
-
type: re.type || "runtime",
|
|
86
|
-
confidence: "high",
|
|
87
|
-
verifiedAt: "runtime",
|
|
88
|
-
runtimeData: re.data
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
graph.edges = validEdges;
|
|
94
|
-
graph.brokenEdges = brokenEdges;
|
|
95
|
-
|
|
96
|
-
// Calculate coverage
|
|
97
|
-
const totalPossibleEdges = graph.edges.length + graph.brokenEdges.length;
|
|
98
|
-
graph.coverage = {
|
|
99
|
-
static: graph.edges.filter(e => e.verifiedAt === "static").length,
|
|
100
|
-
runtime: graph.edges.filter(e => e.verifiedAt === "runtime").length,
|
|
101
|
-
broken: graph.brokenEdges.length,
|
|
102
|
-
total: totalPossibleEdges,
|
|
103
|
-
percent: totalPossibleEdges > 0
|
|
104
|
-
? Math.round((graph.edges.length / totalPossibleEdges) * 100)
|
|
105
|
-
: 100
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
return graph;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function determineSeverity(edge) {
|
|
112
|
-
// Missing routes are always BLOCK
|
|
113
|
-
if (edge.toRoute) return "BLOCK";
|
|
114
|
-
|
|
115
|
-
// Unresolved function calls are WARN
|
|
116
|
-
if (edge.to?.startsWith("unresolved_")) return "WARN";
|
|
117
|
-
|
|
118
|
-
return "WARN";
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Merge runtime verification results into graph
|
|
123
|
-
*/
|
|
124
|
-
function mergeRuntimeResults(graph, runtimeResults) {
|
|
125
|
-
const updatedGraph = { ...graph };
|
|
126
|
-
|
|
127
|
-
for (const result of runtimeResults) {
|
|
128
|
-
// Find edge that matches this runtime result
|
|
129
|
-
const edge = updatedGraph.edges.find(e =>
|
|
130
|
-
e.to?.includes(result.path) ||
|
|
131
|
-
(e.type === "calls_route" && e.toRoute?.path === result.path)
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
if (edge) {
|
|
135
|
-
edge.verifiedAt = "runtime";
|
|
136
|
-
edge.runtimeData = {
|
|
137
|
-
status: result.status,
|
|
138
|
-
latencyMs: result.latencyMs,
|
|
139
|
-
error: result.error
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// Check for runtime failures
|
|
143
|
-
if (result.status >= 400) {
|
|
144
|
-
const brokenEdge = {
|
|
145
|
-
edge: { ...edge },
|
|
146
|
-
reason: `Route returns ${result.status} at runtime`,
|
|
147
|
-
severity: result.status >= 500 ? "BLOCK" : "WARN",
|
|
148
|
-
runtimeData: edge.runtimeData
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// Move to broken edges
|
|
152
|
-
updatedGraph.brokenEdges.push(brokenEdge);
|
|
153
|
-
updatedGraph.edges = updatedGraph.edges.filter(e => e.id !== edge.id);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Recalculate coverage
|
|
159
|
-
const totalPossibleEdges = updatedGraph.edges.length + updatedGraph.brokenEdges.length;
|
|
160
|
-
updatedGraph.coverage = {
|
|
161
|
-
static: updatedGraph.edges.filter(e => e.verifiedAt === "static").length,
|
|
162
|
-
runtime: updatedGraph.edges.filter(e => e.verifiedAt === "runtime").length,
|
|
163
|
-
broken: updatedGraph.brokenEdges.length,
|
|
164
|
-
total: totalPossibleEdges,
|
|
165
|
-
percent: totalPossibleEdges > 0
|
|
166
|
-
? Math.round((updatedGraph.edges.length / totalPossibleEdges) * 100)
|
|
167
|
-
: 100
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
return updatedGraph;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Compute graph diff between two graphs
|
|
175
|
-
*/
|
|
176
|
-
function computeGraphDiff(before, after) {
|
|
177
|
-
const diff = {
|
|
178
|
-
nodesAdded: [],
|
|
179
|
-
nodesRemoved: [],
|
|
180
|
-
edgesAdded: [],
|
|
181
|
-
edgesRemoved: [],
|
|
182
|
-
edgesFixed: [],
|
|
183
|
-
edgesBroken: []
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const beforeNodeIds = new Set(before.nodes.map(n => n.id));
|
|
187
|
-
const afterNodeIds = new Set(after.nodes.map(n => n.id));
|
|
188
|
-
|
|
189
|
-
for (const n of after.nodes) {
|
|
190
|
-
if (!beforeNodeIds.has(n.id)) diff.nodesAdded.push(n);
|
|
191
|
-
}
|
|
192
|
-
for (const n of before.nodes) {
|
|
193
|
-
if (!afterNodeIds.has(n.id)) diff.nodesRemoved.push(n);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const beforeEdgeIds = new Set(before.edges.map(e => e.id));
|
|
197
|
-
const afterEdgeIds = new Set(after.edges.map(e => e.id));
|
|
198
|
-
const beforeBrokenIds = new Set(before.brokenEdges.map(e => e.edge.id));
|
|
199
|
-
const afterBrokenIds = new Set(after.brokenEdges.map(e => e.edge.id));
|
|
200
|
-
|
|
201
|
-
for (const e of after.edges) {
|
|
202
|
-
if (!beforeEdgeIds.has(e.id)) {
|
|
203
|
-
if (beforeBrokenIds.has(e.id)) {
|
|
204
|
-
diff.edgesFixed.push(e);
|
|
205
|
-
} else {
|
|
206
|
-
diff.edgesAdded.push(e);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
for (const e of before.edges) {
|
|
212
|
-
if (!afterEdgeIds.has(e.id)) {
|
|
213
|
-
if (afterBrokenIds.has(e.id)) {
|
|
214
|
-
diff.edgesBroken.push(e);
|
|
215
|
-
} else {
|
|
216
|
-
diff.edgesRemoved.push(e);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return diff;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Get findings from broken edges
|
|
226
|
-
*/
|
|
227
|
-
function getFindingsFromGraph(graph) {
|
|
228
|
-
const findings = [];
|
|
229
|
-
|
|
230
|
-
for (const broken of graph.brokenEdges) {
|
|
231
|
-
const finding = {
|
|
232
|
-
id: `GRAPH_${broken.edge.id}`,
|
|
233
|
-
severity: broken.severity,
|
|
234
|
-
category: "BrokenEdge",
|
|
235
|
-
title: broken.reason,
|
|
236
|
-
evidence: [],
|
|
237
|
-
graphEdge: broken.edge
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
if (broken.route) {
|
|
241
|
-
finding.title = `Route ${broken.route.method} ${broken.route.path} referenced but doesn't exist`;
|
|
242
|
-
finding.category = "MissingRoute";
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (broken.runtimeData) {
|
|
246
|
-
finding.title = `Route returns ${broken.runtimeData.status} at runtime`;
|
|
247
|
-
finding.category = "RuntimeFailure";
|
|
248
|
-
if (broken.runtimeData.status === 401) {
|
|
249
|
-
finding.title = "UI shows success but server returned 401 (auth required)";
|
|
250
|
-
finding.category = "CausalContradiction";
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
findings.push(finding);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return findings;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
module.exports = {
|
|
261
|
-
buildProofGraph,
|
|
262
|
-
mergeRuntimeResults,
|
|
263
|
-
computeGraphDiff,
|
|
264
|
-
getFindingsFromGraph
|
|
265
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Graph Builder
|
|
3
|
+
* Builds the complete ProofGraph from static and runtime edges
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const crypto = require("crypto");
|
|
9
|
+
|
|
10
|
+
function sha256(text) {
|
|
11
|
+
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build ProofGraph from extracted nodes and edges
|
|
16
|
+
*/
|
|
17
|
+
function buildProofGraph({ nodes, edges, runtimeEdges = [], meta = {} }) {
|
|
18
|
+
const graph = {
|
|
19
|
+
meta: {
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
generatedAt: new Date().toISOString(),
|
|
22
|
+
commit: meta.commit || process.env.VIBECHECK_COMMIT_SHA || "unknown",
|
|
23
|
+
...meta
|
|
24
|
+
},
|
|
25
|
+
nodes: [],
|
|
26
|
+
edges: [],
|
|
27
|
+
brokenEdges: [],
|
|
28
|
+
coverage: {
|
|
29
|
+
static: 0,
|
|
30
|
+
runtime: 0,
|
|
31
|
+
total: 0
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Dedupe and add nodes
|
|
36
|
+
const nodeMap = new Map();
|
|
37
|
+
for (const n of nodes) {
|
|
38
|
+
if (!nodeMap.has(n.id)) {
|
|
39
|
+
nodeMap.set(n.id, n);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
graph.nodes = Array.from(nodeMap.values());
|
|
43
|
+
|
|
44
|
+
// Process edges, separate broken ones
|
|
45
|
+
const brokenEdges = [];
|
|
46
|
+
const validEdges = [];
|
|
47
|
+
|
|
48
|
+
for (const edge of edges) {
|
|
49
|
+
if (edge.broken) {
|
|
50
|
+
brokenEdges.push({
|
|
51
|
+
edge: {
|
|
52
|
+
id: edge.id,
|
|
53
|
+
from: edge.from,
|
|
54
|
+
to: edge.to,
|
|
55
|
+
type: edge.type,
|
|
56
|
+
confidence: edge.confidence || "low"
|
|
57
|
+
},
|
|
58
|
+
reason: edge.brokenReason || "Edge target not found",
|
|
59
|
+
severity: determineSeverity(edge),
|
|
60
|
+
route: edge.toRoute || null
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
validEdges.push({
|
|
64
|
+
id: edge.id,
|
|
65
|
+
from: edge.from,
|
|
66
|
+
to: edge.to,
|
|
67
|
+
type: edge.type,
|
|
68
|
+
confidence: edge.confidence || "med",
|
|
69
|
+
verifiedAt: "static"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add runtime edges
|
|
75
|
+
for (const re of runtimeEdges) {
|
|
76
|
+
const existing = validEdges.find(e => e.from === re.from && e.to === re.to);
|
|
77
|
+
if (existing) {
|
|
78
|
+
existing.verifiedAt = "runtime";
|
|
79
|
+
existing.runtimeData = re.data;
|
|
80
|
+
} else {
|
|
81
|
+
validEdges.push({
|
|
82
|
+
id: re.id || `runtime_${sha256(re.from + re.to)}`,
|
|
83
|
+
from: re.from,
|
|
84
|
+
to: re.to,
|
|
85
|
+
type: re.type || "runtime",
|
|
86
|
+
confidence: "high",
|
|
87
|
+
verifiedAt: "runtime",
|
|
88
|
+
runtimeData: re.data
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
graph.edges = validEdges;
|
|
94
|
+
graph.brokenEdges = brokenEdges;
|
|
95
|
+
|
|
96
|
+
// Calculate coverage
|
|
97
|
+
const totalPossibleEdges = graph.edges.length + graph.brokenEdges.length;
|
|
98
|
+
graph.coverage = {
|
|
99
|
+
static: graph.edges.filter(e => e.verifiedAt === "static").length,
|
|
100
|
+
runtime: graph.edges.filter(e => e.verifiedAt === "runtime").length,
|
|
101
|
+
broken: graph.brokenEdges.length,
|
|
102
|
+
total: totalPossibleEdges,
|
|
103
|
+
percent: totalPossibleEdges > 0
|
|
104
|
+
? Math.round((graph.edges.length / totalPossibleEdges) * 100)
|
|
105
|
+
: 100
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return graph;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function determineSeverity(edge) {
|
|
112
|
+
// Missing routes are always BLOCK
|
|
113
|
+
if (edge.toRoute) return "BLOCK";
|
|
114
|
+
|
|
115
|
+
// Unresolved function calls are WARN
|
|
116
|
+
if (edge.to?.startsWith("unresolved_")) return "WARN";
|
|
117
|
+
|
|
118
|
+
return "WARN";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Merge runtime verification results into graph
|
|
123
|
+
*/
|
|
124
|
+
function mergeRuntimeResults(graph, runtimeResults) {
|
|
125
|
+
const updatedGraph = { ...graph };
|
|
126
|
+
|
|
127
|
+
for (const result of runtimeResults) {
|
|
128
|
+
// Find edge that matches this runtime result
|
|
129
|
+
const edge = updatedGraph.edges.find(e =>
|
|
130
|
+
e.to?.includes(result.path) ||
|
|
131
|
+
(e.type === "calls_route" && e.toRoute?.path === result.path)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (edge) {
|
|
135
|
+
edge.verifiedAt = "runtime";
|
|
136
|
+
edge.runtimeData = {
|
|
137
|
+
status: result.status,
|
|
138
|
+
latencyMs: result.latencyMs,
|
|
139
|
+
error: result.error
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Check for runtime failures
|
|
143
|
+
if (result.status >= 400) {
|
|
144
|
+
const brokenEdge = {
|
|
145
|
+
edge: { ...edge },
|
|
146
|
+
reason: `Route returns ${result.status} at runtime`,
|
|
147
|
+
severity: result.status >= 500 ? "BLOCK" : "WARN",
|
|
148
|
+
runtimeData: edge.runtimeData
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Move to broken edges
|
|
152
|
+
updatedGraph.brokenEdges.push(brokenEdge);
|
|
153
|
+
updatedGraph.edges = updatedGraph.edges.filter(e => e.id !== edge.id);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Recalculate coverage
|
|
159
|
+
const totalPossibleEdges = updatedGraph.edges.length + updatedGraph.brokenEdges.length;
|
|
160
|
+
updatedGraph.coverage = {
|
|
161
|
+
static: updatedGraph.edges.filter(e => e.verifiedAt === "static").length,
|
|
162
|
+
runtime: updatedGraph.edges.filter(e => e.verifiedAt === "runtime").length,
|
|
163
|
+
broken: updatedGraph.brokenEdges.length,
|
|
164
|
+
total: totalPossibleEdges,
|
|
165
|
+
percent: totalPossibleEdges > 0
|
|
166
|
+
? Math.round((updatedGraph.edges.length / totalPossibleEdges) * 100)
|
|
167
|
+
: 100
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return updatedGraph;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Compute graph diff between two graphs
|
|
175
|
+
*/
|
|
176
|
+
function computeGraphDiff(before, after) {
|
|
177
|
+
const diff = {
|
|
178
|
+
nodesAdded: [],
|
|
179
|
+
nodesRemoved: [],
|
|
180
|
+
edgesAdded: [],
|
|
181
|
+
edgesRemoved: [],
|
|
182
|
+
edgesFixed: [],
|
|
183
|
+
edgesBroken: []
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const beforeNodeIds = new Set(before.nodes.map(n => n.id));
|
|
187
|
+
const afterNodeIds = new Set(after.nodes.map(n => n.id));
|
|
188
|
+
|
|
189
|
+
for (const n of after.nodes) {
|
|
190
|
+
if (!beforeNodeIds.has(n.id)) diff.nodesAdded.push(n);
|
|
191
|
+
}
|
|
192
|
+
for (const n of before.nodes) {
|
|
193
|
+
if (!afterNodeIds.has(n.id)) diff.nodesRemoved.push(n);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const beforeEdgeIds = new Set(before.edges.map(e => e.id));
|
|
197
|
+
const afterEdgeIds = new Set(after.edges.map(e => e.id));
|
|
198
|
+
const beforeBrokenIds = new Set(before.brokenEdges.map(e => e.edge.id));
|
|
199
|
+
const afterBrokenIds = new Set(after.brokenEdges.map(e => e.edge.id));
|
|
200
|
+
|
|
201
|
+
for (const e of after.edges) {
|
|
202
|
+
if (!beforeEdgeIds.has(e.id)) {
|
|
203
|
+
if (beforeBrokenIds.has(e.id)) {
|
|
204
|
+
diff.edgesFixed.push(e);
|
|
205
|
+
} else {
|
|
206
|
+
diff.edgesAdded.push(e);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (const e of before.edges) {
|
|
212
|
+
if (!afterEdgeIds.has(e.id)) {
|
|
213
|
+
if (afterBrokenIds.has(e.id)) {
|
|
214
|
+
diff.edgesBroken.push(e);
|
|
215
|
+
} else {
|
|
216
|
+
diff.edgesRemoved.push(e);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return diff;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get findings from broken edges
|
|
226
|
+
*/
|
|
227
|
+
function getFindingsFromGraph(graph) {
|
|
228
|
+
const findings = [];
|
|
229
|
+
|
|
230
|
+
for (const broken of graph.brokenEdges) {
|
|
231
|
+
const finding = {
|
|
232
|
+
id: `GRAPH_${broken.edge.id}`,
|
|
233
|
+
severity: broken.severity,
|
|
234
|
+
category: "BrokenEdge",
|
|
235
|
+
title: broken.reason,
|
|
236
|
+
evidence: [],
|
|
237
|
+
graphEdge: broken.edge
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
if (broken.route) {
|
|
241
|
+
finding.title = `Route ${broken.route.method} ${broken.route.path} referenced but doesn't exist`;
|
|
242
|
+
finding.category = "MissingRoute";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (broken.runtimeData) {
|
|
246
|
+
finding.title = `Route returns ${broken.runtimeData.status} at runtime`;
|
|
247
|
+
finding.category = "RuntimeFailure";
|
|
248
|
+
if (broken.runtimeData.status === 401) {
|
|
249
|
+
finding.title = "UI shows success but server returned 401 (auth required)";
|
|
250
|
+
finding.category = "CausalContradiction";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
findings.push(finding);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return findings;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
buildProofGraph,
|
|
262
|
+
mergeRuntimeResults,
|
|
263
|
+
computeGraphDiff,
|
|
264
|
+
getFindingsFromGraph
|
|
265
|
+
};
|