@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,363 +1,363 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Extractors v2 - Main Entry Point
|
|
3
|
-
*
|
|
4
|
-
* Unified API for extracting routes from Next.js and Fastify projects.
|
|
5
|
-
* Implements spec-compliant extraction with confidence levels and runtime proof support.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
"use strict";
|
|
9
|
-
|
|
10
|
-
const path = require("path");
|
|
11
|
-
const { extractNextRoutes, detectNextMode, NEXT_HTTP_METHODS } = require("./next-routes");
|
|
12
|
-
const { extractFastifyRoutes, detectFastify, FASTIFY_METHODS } = require("./fastify-routes");
|
|
13
|
-
const {
|
|
14
|
-
normalizeUrl,
|
|
15
|
-
toCanonicalPath,
|
|
16
|
-
matchRoute,
|
|
17
|
-
RouteIndex,
|
|
18
|
-
getMissingRouteSeverity,
|
|
19
|
-
isCriticalPath,
|
|
20
|
-
calculateSpecificity,
|
|
21
|
-
} = require("./route-matcher");
|
|
22
|
-
const {
|
|
23
|
-
extractClientCalls,
|
|
24
|
-
extractWrappers,
|
|
25
|
-
extractUIBindings: extractUIBindingsSimple,
|
|
26
|
-
canonicalizeUrl,
|
|
27
|
-
CALL_KINDS,
|
|
28
|
-
RUNTIMES,
|
|
29
|
-
} = require("./client-calls");
|
|
30
|
-
const {
|
|
31
|
-
buildProofGraph,
|
|
32
|
-
findProofGaps,
|
|
33
|
-
generateProofFindings,
|
|
34
|
-
serializeProofGraph,
|
|
35
|
-
mergeRuntimeProof,
|
|
36
|
-
} = require("./proof-graph");
|
|
37
|
-
const {
|
|
38
|
-
buildTruthpackV2,
|
|
39
|
-
writeTruthpackV2,
|
|
40
|
-
loadTruthpackV2,
|
|
41
|
-
} = require("./truthpack-v2");
|
|
42
|
-
const {
|
|
43
|
-
extractUIBindings,
|
|
44
|
-
linkBindingsToClientCalls,
|
|
45
|
-
UI_EVENT_ATTRIBUTES,
|
|
46
|
-
FORM_ACTION_PATTERNS,
|
|
47
|
-
TRANSITION_HOOKS,
|
|
48
|
-
} = require("./ui-bindings");
|
|
49
|
-
const {
|
|
50
|
-
extractFastifyRoutesWithRuntime,
|
|
51
|
-
findFastifyEntry,
|
|
52
|
-
executeRouteDump,
|
|
53
|
-
mergeWithStaticRoutes,
|
|
54
|
-
applySafetyRules,
|
|
55
|
-
shouldBlockMissingRoute,
|
|
56
|
-
DUMP_ENV_VAR,
|
|
57
|
-
} = require("./fastify-route-dump");
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Extract all server routes from a project
|
|
61
|
-
* Detects framework(s) and extracts routes with confidence levels
|
|
62
|
-
*/
|
|
63
|
-
function extractServerRoutes(projectRoot, options = {}) {
|
|
64
|
-
const { fastifyEntry = null } = options;
|
|
65
|
-
|
|
66
|
-
const result = {
|
|
67
|
-
stack: {
|
|
68
|
-
next: { present: false, router: "unknown" },
|
|
69
|
-
fastify: { present: false, entryFile: null },
|
|
70
|
-
},
|
|
71
|
-
routes: [],
|
|
72
|
-
gaps: [],
|
|
73
|
-
middleware: { matchers: [], protectedHints: [] },
|
|
74
|
-
rewrites: [],
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Extract Next.js routes
|
|
78
|
-
const nextResult = extractNextRoutes(projectRoot);
|
|
79
|
-
if (nextResult.present) {
|
|
80
|
-
result.stack.next = {
|
|
81
|
-
present: true,
|
|
82
|
-
router: nextResult.router,
|
|
83
|
-
appDir: nextResult.appDir,
|
|
84
|
-
pagesDir: nextResult.pagesDir,
|
|
85
|
-
middlewareFile: nextResult.middlewareFile,
|
|
86
|
-
};
|
|
87
|
-
result.routes.push(...nextResult.routes);
|
|
88
|
-
result.middleware = nextResult.middleware;
|
|
89
|
-
result.rewrites = nextResult.rewrites || [];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Extract Fastify routes
|
|
93
|
-
const fastifyResult = extractFastifyRoutes(projectRoot, fastifyEntry);
|
|
94
|
-
if (fastifyResult.present) {
|
|
95
|
-
result.stack.fastify = {
|
|
96
|
-
present: true,
|
|
97
|
-
entryFile: fastifyResult.entryFile,
|
|
98
|
-
prefixes: fastifyResult.prefixes,
|
|
99
|
-
};
|
|
100
|
-
result.routes.push(...fastifyResult.routes);
|
|
101
|
-
result.gaps.push(...(fastifyResult.gaps || []));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Dedupe routes by canonical path + method
|
|
105
|
-
result.routes = dedupeRoutes(result.routes);
|
|
106
|
-
|
|
107
|
-
return result;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Deduplicate routes, keeping highest confidence
|
|
112
|
-
*/
|
|
113
|
-
function dedupeRoutes(routes) {
|
|
114
|
-
const seen = new Map();
|
|
115
|
-
|
|
116
|
-
for (const route of routes) {
|
|
117
|
-
const key = `${route.methods.sort().join(",")}:${route.canonicalPath}`;
|
|
118
|
-
const existing = seen.get(key);
|
|
119
|
-
|
|
120
|
-
if (!existing) {
|
|
121
|
-
seen.set(key, route);
|
|
122
|
-
} else {
|
|
123
|
-
// Keep higher confidence
|
|
124
|
-
const confOrder = { high: 3, medium: 2, low: 1 };
|
|
125
|
-
if ((confOrder[route.confidence] || 0) > (confOrder[existing.confidence] || 0)) {
|
|
126
|
-
seen.set(key, route);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return [...seen.values()];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Match client calls against server routes and produce findings
|
|
136
|
-
*/
|
|
137
|
-
function matchClientCalls(clientCalls, serverRoutes, options = {}) {
|
|
138
|
-
const { basePath = "", trailingSlash = false, rewrites = [] } = options;
|
|
139
|
-
|
|
140
|
-
const index = new RouteIndex(serverRoutes);
|
|
141
|
-
const results = [];
|
|
142
|
-
|
|
143
|
-
for (const call of clientCalls) {
|
|
144
|
-
const normalizedPath = normalizeUrl(call.resolvedPath || call.urlTemplate, { basePath, trailingSlash });
|
|
145
|
-
const method = call.method || "UNKNOWN";
|
|
146
|
-
|
|
147
|
-
const match = index.match(normalizedPath, method, {
|
|
148
|
-
allowMethodMismatch: true,
|
|
149
|
-
considerRewrites: rewrites,
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
results.push({
|
|
153
|
-
call,
|
|
154
|
-
normalizedPath,
|
|
155
|
-
method,
|
|
156
|
-
match,
|
|
157
|
-
isCritical: isCriticalPath(normalizedPath),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return results;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Apply runtime proof to upgrade/downgrade findings
|
|
166
|
-
*
|
|
167
|
-
* Runtime beats static:
|
|
168
|
-
* - 404/405 at runtime → BLOCK (confirmed missing)
|
|
169
|
-
* - 2xx at runtime → route exists (even if static missed it)
|
|
170
|
-
*/
|
|
171
|
-
function applyRuntimeProof(matchResults, runtimeRequests) {
|
|
172
|
-
const requestMap = new Map();
|
|
173
|
-
|
|
174
|
-
// Index runtime requests by normalized path
|
|
175
|
-
for (const req of runtimeRequests) {
|
|
176
|
-
const normalizedPath = normalizeUrl(req.url);
|
|
177
|
-
if (!requestMap.has(normalizedPath)) {
|
|
178
|
-
requestMap.set(normalizedPath, []);
|
|
179
|
-
}
|
|
180
|
-
requestMap.get(normalizedPath).push(req);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Apply proof to each match result
|
|
184
|
-
for (const result of matchResults) {
|
|
185
|
-
const runtimeReqs = requestMap.get(result.normalizedPath) || [];
|
|
186
|
-
|
|
187
|
-
if (runtimeReqs.length > 0) {
|
|
188
|
-
result.hasRuntimeProof = true;
|
|
189
|
-
|
|
190
|
-
// Check for failures
|
|
191
|
-
const failures = runtimeReqs.filter(r => r.status === 404 || r.status === 405 || r.failed);
|
|
192
|
-
const successes = runtimeReqs.filter(r => r.status >= 200 && r.status < 300);
|
|
193
|
-
|
|
194
|
-
if (failures.length > 0 && successes.length === 0) {
|
|
195
|
-
result.runtimeResult = failures[0].status === 405 ? "405" : "404";
|
|
196
|
-
result.runtimeEvidence = failures.slice(0, 3);
|
|
197
|
-
} else if (successes.length > 0) {
|
|
198
|
-
result.runtimeResult = "2xx";
|
|
199
|
-
result.runtimeEvidence = successes.slice(0, 3);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return matchResults;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Generate route findings from match results
|
|
209
|
-
*/
|
|
210
|
-
function generateRouteFindings(matchResults, options = {}) {
|
|
211
|
-
const { framework = "unknown" } = options;
|
|
212
|
-
const findings = [];
|
|
213
|
-
|
|
214
|
-
for (const result of matchResults) {
|
|
215
|
-
// Route missing
|
|
216
|
-
if (!result.match.matched || result.match.matchType === "none") {
|
|
217
|
-
const severity = getMissingRouteSeverity({
|
|
218
|
-
framework,
|
|
219
|
-
staticConfidence: result.match.confidence,
|
|
220
|
-
hasRuntimeProof: result.hasRuntimeProof,
|
|
221
|
-
runtimeResult: result.runtimeResult,
|
|
222
|
-
isRewriteTarget: result.match.matchType === "rewrite",
|
|
223
|
-
isCriticalPath: result.isCritical,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Skip if runtime confirmed it exists
|
|
227
|
-
if (result.runtimeResult === "2xx") continue;
|
|
228
|
-
|
|
229
|
-
findings.push({
|
|
230
|
-
detectorId: "ROUTE_MISSING",
|
|
231
|
-
severity,
|
|
232
|
-
category: "Routes",
|
|
233
|
-
scope: "client",
|
|
234
|
-
title: `Client calls ${result.method} ${result.normalizedPath} but no server route exists`,
|
|
235
|
-
why: severity === "BLOCK"
|
|
236
|
-
? "Route confirmed missing. This will cause errors in production."
|
|
237
|
-
: "Route may be missing. Could be dynamically registered or proxied.",
|
|
238
|
-
confidence: result.hasRuntimeProof ? "high" : result.match.confidence,
|
|
239
|
-
evidence: result.call.evidence || [],
|
|
240
|
-
runtimeEvidence: result.runtimeEvidence,
|
|
241
|
-
call: result.call,
|
|
242
|
-
matchResult: result.match,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Method mismatch
|
|
247
|
-
if (result.match.matched && result.match.methodMismatch) {
|
|
248
|
-
const severity = result.isCritical ? "BLOCK" : "WARN";
|
|
249
|
-
|
|
250
|
-
findings.push({
|
|
251
|
-
detectorId: "ROUTE_METHOD_MISMATCH",
|
|
252
|
-
severity,
|
|
253
|
-
category: "Routes",
|
|
254
|
-
scope: "client",
|
|
255
|
-
title: `Method mismatch: client uses ${result.method} but server route ${result.match.route?.canonicalPath} handles ${result.match.route?.methods?.join("/")}`,
|
|
256
|
-
why: "Method mismatch will cause 405 errors.",
|
|
257
|
-
confidence: "high",
|
|
258
|
-
evidence: [
|
|
259
|
-
...(result.call.evidence || []),
|
|
260
|
-
...(result.match.route?.evidence || []),
|
|
261
|
-
],
|
|
262
|
-
call: result.call,
|
|
263
|
-
matchResult: result.match,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return findings;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Build a truthpack-compatible routes structure
|
|
273
|
-
*/
|
|
274
|
-
function buildRoutesForTruthpack(projectRoot, options = {}) {
|
|
275
|
-
const extraction = extractServerRoutes(projectRoot, options);
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
server: extraction.routes.map(r => ({
|
|
279
|
-
id: r.id,
|
|
280
|
-
kind: r.kind,
|
|
281
|
-
methods: r.methods,
|
|
282
|
-
path: r.canonicalPath,
|
|
283
|
-
rawPath: r.rawPath,
|
|
284
|
-
authRequired: r.authRequired,
|
|
285
|
-
confidence: r.confidence,
|
|
286
|
-
handler: r.handler,
|
|
287
|
-
evidence: r.evidence,
|
|
288
|
-
})),
|
|
289
|
-
stack: extraction.stack,
|
|
290
|
-
gaps: extraction.gaps,
|
|
291
|
-
middleware: extraction.middleware,
|
|
292
|
-
rewrites: extraction.rewrites,
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
module.exports = {
|
|
297
|
-
// Main extraction
|
|
298
|
-
extractServerRoutes,
|
|
299
|
-
extractNextRoutes,
|
|
300
|
-
extractFastifyRoutes,
|
|
301
|
-
extractClientCalls,
|
|
302
|
-
|
|
303
|
-
// Detection
|
|
304
|
-
detectNextMode,
|
|
305
|
-
detectFastify,
|
|
306
|
-
|
|
307
|
-
// Matching
|
|
308
|
-
matchClientCalls,
|
|
309
|
-
matchRoute,
|
|
310
|
-
RouteIndex,
|
|
311
|
-
normalizeUrl,
|
|
312
|
-
toCanonicalPath,
|
|
313
|
-
canonicalizeUrl,
|
|
314
|
-
calculateSpecificity,
|
|
315
|
-
|
|
316
|
-
// Runtime proof
|
|
317
|
-
applyRuntimeProof,
|
|
318
|
-
|
|
319
|
-
// Findings
|
|
320
|
-
generateRouteFindings,
|
|
321
|
-
getMissingRouteSeverity,
|
|
322
|
-
isCriticalPath,
|
|
323
|
-
|
|
324
|
-
// Truthpack integration
|
|
325
|
-
buildRoutesForTruthpack,
|
|
326
|
-
|
|
327
|
-
// Wrappers
|
|
328
|
-
extractWrappers,
|
|
329
|
-
|
|
330
|
-
// Proof Graph
|
|
331
|
-
buildProofGraph,
|
|
332
|
-
findProofGaps,
|
|
333
|
-
generateProofFindings,
|
|
334
|
-
serializeProofGraph,
|
|
335
|
-
mergeRuntimeProof,
|
|
336
|
-
|
|
337
|
-
// Truthpack v2
|
|
338
|
-
buildTruthpackV2,
|
|
339
|
-
writeTruthpackV2,
|
|
340
|
-
loadTruthpackV2,
|
|
341
|
-
|
|
342
|
-
// UI Bindings
|
|
343
|
-
extractUIBindings,
|
|
344
|
-
linkBindingsToClientCalls,
|
|
345
|
-
UI_EVENT_ATTRIBUTES,
|
|
346
|
-
FORM_ACTION_PATTERNS,
|
|
347
|
-
TRANSITION_HOOKS,
|
|
348
|
-
|
|
349
|
-
// Fastify Runtime Dump
|
|
350
|
-
extractFastifyRoutesWithRuntime,
|
|
351
|
-
findFastifyEntry,
|
|
352
|
-
executeRouteDump,
|
|
353
|
-
mergeWithStaticRoutes,
|
|
354
|
-
applySafetyRules,
|
|
355
|
-
shouldBlockMissingRoute,
|
|
356
|
-
DUMP_ENV_VAR,
|
|
357
|
-
|
|
358
|
-
// Constants
|
|
359
|
-
NEXT_HTTP_METHODS,
|
|
360
|
-
FASTIFY_METHODS,
|
|
361
|
-
CALL_KINDS,
|
|
362
|
-
RUNTIMES,
|
|
363
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Route Extractors v2 - Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Unified API for extracting routes from Next.js and Fastify projects.
|
|
5
|
+
* Implements spec-compliant extraction with confidence levels and runtime proof support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const { extractNextRoutes, detectNextMode, NEXT_HTTP_METHODS } = require("./next-routes");
|
|
12
|
+
const { extractFastifyRoutes, detectFastify, FASTIFY_METHODS } = require("./fastify-routes");
|
|
13
|
+
const {
|
|
14
|
+
normalizeUrl,
|
|
15
|
+
toCanonicalPath,
|
|
16
|
+
matchRoute,
|
|
17
|
+
RouteIndex,
|
|
18
|
+
getMissingRouteSeverity,
|
|
19
|
+
isCriticalPath,
|
|
20
|
+
calculateSpecificity,
|
|
21
|
+
} = require("./route-matcher");
|
|
22
|
+
const {
|
|
23
|
+
extractClientCalls,
|
|
24
|
+
extractWrappers,
|
|
25
|
+
extractUIBindings: extractUIBindingsSimple,
|
|
26
|
+
canonicalizeUrl,
|
|
27
|
+
CALL_KINDS,
|
|
28
|
+
RUNTIMES,
|
|
29
|
+
} = require("./client-calls");
|
|
30
|
+
const {
|
|
31
|
+
buildProofGraph,
|
|
32
|
+
findProofGaps,
|
|
33
|
+
generateProofFindings,
|
|
34
|
+
serializeProofGraph,
|
|
35
|
+
mergeRuntimeProof,
|
|
36
|
+
} = require("./proof-graph");
|
|
37
|
+
const {
|
|
38
|
+
buildTruthpackV2,
|
|
39
|
+
writeTruthpackV2,
|
|
40
|
+
loadTruthpackV2,
|
|
41
|
+
} = require("./truthpack-v2");
|
|
42
|
+
const {
|
|
43
|
+
extractUIBindings,
|
|
44
|
+
linkBindingsToClientCalls,
|
|
45
|
+
UI_EVENT_ATTRIBUTES,
|
|
46
|
+
FORM_ACTION_PATTERNS,
|
|
47
|
+
TRANSITION_HOOKS,
|
|
48
|
+
} = require("./ui-bindings");
|
|
49
|
+
const {
|
|
50
|
+
extractFastifyRoutesWithRuntime,
|
|
51
|
+
findFastifyEntry,
|
|
52
|
+
executeRouteDump,
|
|
53
|
+
mergeWithStaticRoutes,
|
|
54
|
+
applySafetyRules,
|
|
55
|
+
shouldBlockMissingRoute,
|
|
56
|
+
DUMP_ENV_VAR,
|
|
57
|
+
} = require("./fastify-route-dump");
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract all server routes from a project
|
|
61
|
+
* Detects framework(s) and extracts routes with confidence levels
|
|
62
|
+
*/
|
|
63
|
+
function extractServerRoutes(projectRoot, options = {}) {
|
|
64
|
+
const { fastifyEntry = null } = options;
|
|
65
|
+
|
|
66
|
+
const result = {
|
|
67
|
+
stack: {
|
|
68
|
+
next: { present: false, router: "unknown" },
|
|
69
|
+
fastify: { present: false, entryFile: null },
|
|
70
|
+
},
|
|
71
|
+
routes: [],
|
|
72
|
+
gaps: [],
|
|
73
|
+
middleware: { matchers: [], protectedHints: [] },
|
|
74
|
+
rewrites: [],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Extract Next.js routes
|
|
78
|
+
const nextResult = extractNextRoutes(projectRoot);
|
|
79
|
+
if (nextResult.present) {
|
|
80
|
+
result.stack.next = {
|
|
81
|
+
present: true,
|
|
82
|
+
router: nextResult.router,
|
|
83
|
+
appDir: nextResult.appDir,
|
|
84
|
+
pagesDir: nextResult.pagesDir,
|
|
85
|
+
middlewareFile: nextResult.middlewareFile,
|
|
86
|
+
};
|
|
87
|
+
result.routes.push(...nextResult.routes);
|
|
88
|
+
result.middleware = nextResult.middleware;
|
|
89
|
+
result.rewrites = nextResult.rewrites || [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Extract Fastify routes
|
|
93
|
+
const fastifyResult = extractFastifyRoutes(projectRoot, fastifyEntry);
|
|
94
|
+
if (fastifyResult.present) {
|
|
95
|
+
result.stack.fastify = {
|
|
96
|
+
present: true,
|
|
97
|
+
entryFile: fastifyResult.entryFile,
|
|
98
|
+
prefixes: fastifyResult.prefixes,
|
|
99
|
+
};
|
|
100
|
+
result.routes.push(...fastifyResult.routes);
|
|
101
|
+
result.gaps.push(...(fastifyResult.gaps || []));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Dedupe routes by canonical path + method
|
|
105
|
+
result.routes = dedupeRoutes(result.routes);
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Deduplicate routes, keeping highest confidence
|
|
112
|
+
*/
|
|
113
|
+
function dedupeRoutes(routes) {
|
|
114
|
+
const seen = new Map();
|
|
115
|
+
|
|
116
|
+
for (const route of routes) {
|
|
117
|
+
const key = `${route.methods.sort().join(",")}:${route.canonicalPath}`;
|
|
118
|
+
const existing = seen.get(key);
|
|
119
|
+
|
|
120
|
+
if (!existing) {
|
|
121
|
+
seen.set(key, route);
|
|
122
|
+
} else {
|
|
123
|
+
// Keep higher confidence
|
|
124
|
+
const confOrder = { high: 3, medium: 2, low: 1 };
|
|
125
|
+
if ((confOrder[route.confidence] || 0) > (confOrder[existing.confidence] || 0)) {
|
|
126
|
+
seen.set(key, route);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return [...seen.values()];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Match client calls against server routes and produce findings
|
|
136
|
+
*/
|
|
137
|
+
function matchClientCalls(clientCalls, serverRoutes, options = {}) {
|
|
138
|
+
const { basePath = "", trailingSlash = false, rewrites = [] } = options;
|
|
139
|
+
|
|
140
|
+
const index = new RouteIndex(serverRoutes);
|
|
141
|
+
const results = [];
|
|
142
|
+
|
|
143
|
+
for (const call of clientCalls) {
|
|
144
|
+
const normalizedPath = normalizeUrl(call.resolvedPath || call.urlTemplate, { basePath, trailingSlash });
|
|
145
|
+
const method = call.method || "UNKNOWN";
|
|
146
|
+
|
|
147
|
+
const match = index.match(normalizedPath, method, {
|
|
148
|
+
allowMethodMismatch: true,
|
|
149
|
+
considerRewrites: rewrites,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
results.push({
|
|
153
|
+
call,
|
|
154
|
+
normalizedPath,
|
|
155
|
+
method,
|
|
156
|
+
match,
|
|
157
|
+
isCritical: isCriticalPath(normalizedPath),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return results;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Apply runtime proof to upgrade/downgrade findings
|
|
166
|
+
*
|
|
167
|
+
* Runtime beats static:
|
|
168
|
+
* - 404/405 at runtime → BLOCK (confirmed missing)
|
|
169
|
+
* - 2xx at runtime → route exists (even if static missed it)
|
|
170
|
+
*/
|
|
171
|
+
function applyRuntimeProof(matchResults, runtimeRequests) {
|
|
172
|
+
const requestMap = new Map();
|
|
173
|
+
|
|
174
|
+
// Index runtime requests by normalized path
|
|
175
|
+
for (const req of runtimeRequests) {
|
|
176
|
+
const normalizedPath = normalizeUrl(req.url);
|
|
177
|
+
if (!requestMap.has(normalizedPath)) {
|
|
178
|
+
requestMap.set(normalizedPath, []);
|
|
179
|
+
}
|
|
180
|
+
requestMap.get(normalizedPath).push(req);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Apply proof to each match result
|
|
184
|
+
for (const result of matchResults) {
|
|
185
|
+
const runtimeReqs = requestMap.get(result.normalizedPath) || [];
|
|
186
|
+
|
|
187
|
+
if (runtimeReqs.length > 0) {
|
|
188
|
+
result.hasRuntimeProof = true;
|
|
189
|
+
|
|
190
|
+
// Check for failures
|
|
191
|
+
const failures = runtimeReqs.filter(r => r.status === 404 || r.status === 405 || r.failed);
|
|
192
|
+
const successes = runtimeReqs.filter(r => r.status >= 200 && r.status < 300);
|
|
193
|
+
|
|
194
|
+
if (failures.length > 0 && successes.length === 0) {
|
|
195
|
+
result.runtimeResult = failures[0].status === 405 ? "405" : "404";
|
|
196
|
+
result.runtimeEvidence = failures.slice(0, 3);
|
|
197
|
+
} else if (successes.length > 0) {
|
|
198
|
+
result.runtimeResult = "2xx";
|
|
199
|
+
result.runtimeEvidence = successes.slice(0, 3);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return matchResults;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Generate route findings from match results
|
|
209
|
+
*/
|
|
210
|
+
function generateRouteFindings(matchResults, options = {}) {
|
|
211
|
+
const { framework = "unknown" } = options;
|
|
212
|
+
const findings = [];
|
|
213
|
+
|
|
214
|
+
for (const result of matchResults) {
|
|
215
|
+
// Route missing
|
|
216
|
+
if (!result.match.matched || result.match.matchType === "none") {
|
|
217
|
+
const severity = getMissingRouteSeverity({
|
|
218
|
+
framework,
|
|
219
|
+
staticConfidence: result.match.confidence,
|
|
220
|
+
hasRuntimeProof: result.hasRuntimeProof,
|
|
221
|
+
runtimeResult: result.runtimeResult,
|
|
222
|
+
isRewriteTarget: result.match.matchType === "rewrite",
|
|
223
|
+
isCriticalPath: result.isCritical,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Skip if runtime confirmed it exists
|
|
227
|
+
if (result.runtimeResult === "2xx") continue;
|
|
228
|
+
|
|
229
|
+
findings.push({
|
|
230
|
+
detectorId: "ROUTE_MISSING",
|
|
231
|
+
severity,
|
|
232
|
+
category: "Routes",
|
|
233
|
+
scope: "client",
|
|
234
|
+
title: `Client calls ${result.method} ${result.normalizedPath} but no server route exists`,
|
|
235
|
+
why: severity === "BLOCK"
|
|
236
|
+
? "Route confirmed missing. This will cause errors in production."
|
|
237
|
+
: "Route may be missing. Could be dynamically registered or proxied.",
|
|
238
|
+
confidence: result.hasRuntimeProof ? "high" : result.match.confidence,
|
|
239
|
+
evidence: result.call.evidence || [],
|
|
240
|
+
runtimeEvidence: result.runtimeEvidence,
|
|
241
|
+
call: result.call,
|
|
242
|
+
matchResult: result.match,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Method mismatch
|
|
247
|
+
if (result.match.matched && result.match.methodMismatch) {
|
|
248
|
+
const severity = result.isCritical ? "BLOCK" : "WARN";
|
|
249
|
+
|
|
250
|
+
findings.push({
|
|
251
|
+
detectorId: "ROUTE_METHOD_MISMATCH",
|
|
252
|
+
severity,
|
|
253
|
+
category: "Routes",
|
|
254
|
+
scope: "client",
|
|
255
|
+
title: `Method mismatch: client uses ${result.method} but server route ${result.match.route?.canonicalPath} handles ${result.match.route?.methods?.join("/")}`,
|
|
256
|
+
why: "Method mismatch will cause 405 errors.",
|
|
257
|
+
confidence: "high",
|
|
258
|
+
evidence: [
|
|
259
|
+
...(result.call.evidence || []),
|
|
260
|
+
...(result.match.route?.evidence || []),
|
|
261
|
+
],
|
|
262
|
+
call: result.call,
|
|
263
|
+
matchResult: result.match,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return findings;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Build a truthpack-compatible routes structure
|
|
273
|
+
*/
|
|
274
|
+
function buildRoutesForTruthpack(projectRoot, options = {}) {
|
|
275
|
+
const extraction = extractServerRoutes(projectRoot, options);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
server: extraction.routes.map(r => ({
|
|
279
|
+
id: r.id,
|
|
280
|
+
kind: r.kind,
|
|
281
|
+
methods: r.methods,
|
|
282
|
+
path: r.canonicalPath,
|
|
283
|
+
rawPath: r.rawPath,
|
|
284
|
+
authRequired: r.authRequired,
|
|
285
|
+
confidence: r.confidence,
|
|
286
|
+
handler: r.handler,
|
|
287
|
+
evidence: r.evidence,
|
|
288
|
+
})),
|
|
289
|
+
stack: extraction.stack,
|
|
290
|
+
gaps: extraction.gaps,
|
|
291
|
+
middleware: extraction.middleware,
|
|
292
|
+
rewrites: extraction.rewrites,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = {
|
|
297
|
+
// Main extraction
|
|
298
|
+
extractServerRoutes,
|
|
299
|
+
extractNextRoutes,
|
|
300
|
+
extractFastifyRoutes,
|
|
301
|
+
extractClientCalls,
|
|
302
|
+
|
|
303
|
+
// Detection
|
|
304
|
+
detectNextMode,
|
|
305
|
+
detectFastify,
|
|
306
|
+
|
|
307
|
+
// Matching
|
|
308
|
+
matchClientCalls,
|
|
309
|
+
matchRoute,
|
|
310
|
+
RouteIndex,
|
|
311
|
+
normalizeUrl,
|
|
312
|
+
toCanonicalPath,
|
|
313
|
+
canonicalizeUrl,
|
|
314
|
+
calculateSpecificity,
|
|
315
|
+
|
|
316
|
+
// Runtime proof
|
|
317
|
+
applyRuntimeProof,
|
|
318
|
+
|
|
319
|
+
// Findings
|
|
320
|
+
generateRouteFindings,
|
|
321
|
+
getMissingRouteSeverity,
|
|
322
|
+
isCriticalPath,
|
|
323
|
+
|
|
324
|
+
// Truthpack integration
|
|
325
|
+
buildRoutesForTruthpack,
|
|
326
|
+
|
|
327
|
+
// Wrappers
|
|
328
|
+
extractWrappers,
|
|
329
|
+
|
|
330
|
+
// Proof Graph
|
|
331
|
+
buildProofGraph,
|
|
332
|
+
findProofGaps,
|
|
333
|
+
generateProofFindings,
|
|
334
|
+
serializeProofGraph,
|
|
335
|
+
mergeRuntimeProof,
|
|
336
|
+
|
|
337
|
+
// Truthpack v2
|
|
338
|
+
buildTruthpackV2,
|
|
339
|
+
writeTruthpackV2,
|
|
340
|
+
loadTruthpackV2,
|
|
341
|
+
|
|
342
|
+
// UI Bindings
|
|
343
|
+
extractUIBindings,
|
|
344
|
+
linkBindingsToClientCalls,
|
|
345
|
+
UI_EVENT_ATTRIBUTES,
|
|
346
|
+
FORM_ACTION_PATTERNS,
|
|
347
|
+
TRANSITION_HOOKS,
|
|
348
|
+
|
|
349
|
+
// Fastify Runtime Dump
|
|
350
|
+
extractFastifyRoutesWithRuntime,
|
|
351
|
+
findFastifyEntry,
|
|
352
|
+
executeRouteDump,
|
|
353
|
+
mergeWithStaticRoutes,
|
|
354
|
+
applySafetyRules,
|
|
355
|
+
shouldBlockMissingRoute,
|
|
356
|
+
DUMP_ENV_VAR,
|
|
357
|
+
|
|
358
|
+
// Constants
|
|
359
|
+
NEXT_HTTP_METHODS,
|
|
360
|
+
FASTIFY_METHODS,
|
|
361
|
+
CALL_KINDS,
|
|
362
|
+
RUNTIMES,
|
|
363
|
+
};
|