@vibecheckai/cli 3.3.0 → 3.5.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/registry.js +389 -269
- package/bin/runners/cli-utils.js +2 -33
- package/bin/runners/context/generators/cursor.js +49 -2
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
- package/bin/runners/lib/analyzers.js +599 -142
- package/bin/runners/lib/audit-logger.js +532 -0
- package/bin/runners/lib/authority/authorities/architecture.js +364 -0
- package/bin/runners/lib/authority/authorities/compliance.js +341 -0
- package/bin/runners/lib/authority/authorities/human.js +343 -0
- package/bin/runners/lib/authority/authorities/quality.js +420 -0
- package/bin/runners/lib/authority/authorities/security.js +228 -0
- package/bin/runners/lib/authority/index.js +293 -0
- package/bin/runners/lib/authority-badge.js +425 -425
- package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
- package/bin/runners/lib/cli-charts.js +368 -0
- package/bin/runners/lib/cli-config-display.js +405 -0
- package/bin/runners/lib/cli-demo.js +275 -0
- package/bin/runners/lib/cli-errors.js +438 -0
- package/bin/runners/lib/cli-help-formatter.js +439 -0
- package/bin/runners/lib/cli-interactive-menu.js +509 -0
- package/bin/runners/lib/cli-prompts.js +441 -0
- package/bin/runners/lib/cli-scan-cards.js +362 -0
- package/bin/runners/lib/compliance-reporter.js +710 -0
- package/bin/runners/lib/conductor/index.js +671 -0
- package/bin/runners/lib/easy/README.md +123 -0
- package/bin/runners/lib/easy/index.js +140 -0
- package/bin/runners/lib/easy/interactive-wizard.js +788 -0
- package/bin/runners/lib/easy/one-click-firewall.js +564 -0
- package/bin/runners/lib/easy/zero-config-reality.js +714 -0
- package/bin/runners/lib/engines/accessibility-engine.js +218 -18
- package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
- package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
- package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
- package/bin/runners/lib/engines/confidence-scoring.js +276 -0
- package/bin/runners/lib/engines/context-detection.js +264 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
- package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
- package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
- package/bin/runners/lib/engines/env-variables-engine.js +458 -0
- package/bin/runners/lib/engines/error-handling-engine.js +437 -0
- package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
- package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
- package/bin/runners/lib/engines/framework-detection.js +508 -0
- package/bin/runners/lib/engines/import-order-engine.js +429 -0
- package/bin/runners/lib/engines/mock-data-engine.js +53 -10
- package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
- package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
- package/bin/runners/lib/engines/orchestrator.js +334 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
- package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
- package/bin/runners/lib/engines/type-aware-engine.js +263 -39
- package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
- package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
- package/bin/runners/lib/enhanced-features/index.js +305 -0
- package/bin/runners/lib/enhanced-output.js +631 -0
- package/bin/runners/lib/enterprise.js +300 -0
- package/bin/runners/lib/entitlements-v2.js +161 -478
- package/bin/runners/lib/firewall/command-validator.js +351 -0
- package/bin/runners/lib/firewall/config.js +341 -0
- package/bin/runners/lib/firewall/content-validator.js +519 -0
- package/bin/runners/lib/firewall/index.js +101 -0
- package/bin/runners/lib/firewall/path-validator.js +256 -0
- package/bin/runners/lib/html-proof-report.js +350 -700
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
- package/bin/runners/lib/mcp-utils.js +425 -0
- package/bin/runners/lib/missions/plan.js +46 -6
- package/bin/runners/lib/missions/templates.js +232 -0
- package/bin/runners/lib/output/index.js +1022 -0
- package/bin/runners/lib/policy-engine.js +652 -0
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
- package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
- package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
- package/bin/runners/lib/polish/autofix/index.js +200 -0
- package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
- package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
- package/bin/runners/lib/polish/backend-checks.js +148 -0
- package/bin/runners/lib/polish/documentation-checks.js +111 -0
- package/bin/runners/lib/polish/frontend-checks.js +168 -0
- package/bin/runners/lib/polish/index.js +71 -0
- package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
- package/bin/runners/lib/polish/library-detection.js +175 -0
- package/bin/runners/lib/polish/performance-checks.js +100 -0
- package/bin/runners/lib/polish/security-checks.js +148 -0
- package/bin/runners/lib/polish/utils.js +203 -0
- package/bin/runners/lib/prompt-builder.js +540 -0
- package/bin/runners/lib/proof-certificate.js +634 -0
- package/bin/runners/lib/reality/accessibility-audit.js +946 -0
- package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
- package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
- package/bin/runners/lib/reality/performance-tracker.js +1077 -0
- package/bin/runners/lib/reality/scenario-generator.js +1404 -0
- package/bin/runners/lib/reality/visual-regression.js +852 -0
- package/bin/runners/lib/reality-profiler.js +717 -0
- package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
- package/bin/runners/lib/review/ai-code-review.js +832 -0
- package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
- package/bin/runners/lib/sbom-generator.js +641 -0
- package/bin/runners/lib/scan-output-enhanced.js +512 -0
- package/bin/runners/lib/scan-output.js +65 -19
- package/bin/runners/lib/security/owasp-scanner.js +939 -0
- package/bin/runners/lib/ship-output.js +18 -25
- package/bin/runners/lib/terminal-ui.js +113 -1
- package/bin/runners/lib/unified-cli-output.js +603 -430
- package/bin/runners/lib/upsell.js +90 -338
- package/bin/runners/lib/validators/contract-validator.js +283 -0
- package/bin/runners/lib/validators/dead-export-detector.js +279 -0
- package/bin/runners/lib/validators/dep-audit.js +245 -0
- package/bin/runners/lib/validators/env-validator.js +319 -0
- package/bin/runners/lib/validators/index.js +120 -0
- package/bin/runners/lib/validators/license-checker.js +252 -0
- package/bin/runners/lib/validators/route-validator.js +290 -0
- package/bin/runners/runAIAgent.js +5 -10
- package/bin/runners/runAgent.js +3 -0
- package/bin/runners/runApprove.js +1233 -1200
- package/bin/runners/runAuth.js +22 -1
- package/bin/runners/runAuthority.js +528 -0
- package/bin/runners/runCheckpoint.js +4 -24
- package/bin/runners/runClassify.js +862 -859
- package/bin/runners/runConductor.js +772 -0
- package/bin/runners/runContainer.js +366 -0
- package/bin/runners/runContext.js +3 -0
- package/bin/runners/runDoctor.js +28 -41
- package/bin/runners/runEasy.js +410 -0
- package/bin/runners/runFirewall.js +3 -0
- package/bin/runners/runFirewallHook.js +3 -0
- package/bin/runners/runFix.js +76 -66
- package/bin/runners/runGuard.js +411 -18
- package/bin/runners/runIaC.js +372 -0
- package/bin/runners/runInit.js +10 -60
- package/bin/runners/runMcp.js +11 -12
- package/bin/runners/runPolish.js +240 -64
- package/bin/runners/runPromptFirewall.js +5 -12
- package/bin/runners/runProve.js +20 -55
- package/bin/runners/runReality.js +68 -59
- package/bin/runners/runReport.js +31 -5
- package/bin/runners/runRuntime.js +5 -8
- package/bin/runners/runScan.js +194 -1273
- package/bin/runners/runShip.js +695 -47
- package/bin/runners/runTruth.js +3 -0
- package/bin/runners/runValidate.js +7 -11
- package/bin/runners/runVibe.js +791 -0
- package/bin/runners/runWatch.js +14 -23
- package/bin/vibecheck.js +179 -65
- package/mcp-server/index.js +202 -636
- package/mcp-server/lib/api-client.cjs +7 -299
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +175 -574
- package/mcp-server/tools-v3.js +800 -505
- package/mcp-server/tools.js +495 -0
- package/package.json +1 -1
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
- package/mcp-server/index-v1.js +0 -698
|
@@ -1,12 +1,161 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* API Consistency Engine
|
|
3
|
-
*
|
|
3
|
+
* Enhanced with:
|
|
4
|
+
* - REST convention checking (HTTP methods, response codes)
|
|
5
|
+
* - Response shape consistency
|
|
6
|
+
* - Input validation detection
|
|
7
|
+
* - Error response format standardization
|
|
8
|
+
* - CORS and security header detection
|
|
9
|
+
* - Rate limiting indicators
|
|
10
|
+
* - API versioning patterns
|
|
4
11
|
*/
|
|
5
12
|
|
|
6
13
|
const { getAST } = require("./ast-cache");
|
|
7
14
|
const traverse = require("@babel/traverse").default;
|
|
8
15
|
const t = require("@babel/types");
|
|
9
16
|
|
|
17
|
+
/**
|
|
18
|
+
* HTTP method best practices
|
|
19
|
+
*/
|
|
20
|
+
const HTTP_METHOD_EXPECTATIONS = {
|
|
21
|
+
GET: {
|
|
22
|
+
shouldHaveBody: false,
|
|
23
|
+
typicalStatusCodes: [200, 404],
|
|
24
|
+
description: "Retrieve data",
|
|
25
|
+
},
|
|
26
|
+
POST: {
|
|
27
|
+
shouldHaveBody: true,
|
|
28
|
+
typicalStatusCodes: [201, 400, 409],
|
|
29
|
+
description: "Create resource",
|
|
30
|
+
},
|
|
31
|
+
PUT: {
|
|
32
|
+
shouldHaveBody: true,
|
|
33
|
+
typicalStatusCodes: [200, 204, 404],
|
|
34
|
+
description: "Replace resource",
|
|
35
|
+
},
|
|
36
|
+
PATCH: {
|
|
37
|
+
shouldHaveBody: true,
|
|
38
|
+
typicalStatusCodes: [200, 204, 404],
|
|
39
|
+
description: "Update resource partially",
|
|
40
|
+
},
|
|
41
|
+
DELETE: {
|
|
42
|
+
shouldHaveBody: false,
|
|
43
|
+
typicalStatusCodes: [204, 404],
|
|
44
|
+
description: "Remove resource",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Common response wrapper patterns
|
|
50
|
+
*/
|
|
51
|
+
const RESPONSE_WRAPPER_PATTERNS = [
|
|
52
|
+
{ pattern: /\{\s*data\s*:/, name: "data" },
|
|
53
|
+
{ pattern: /\{\s*result\s*:/, name: "result" },
|
|
54
|
+
{ pattern: /\{\s*payload\s*:/, name: "payload" },
|
|
55
|
+
{ pattern: /\{\s*response\s*:/, name: "response" },
|
|
56
|
+
{ pattern: /\{\s*body\s*:/, name: "body" },
|
|
57
|
+
{ pattern: /\{\s*message\s*:/, name: "message" },
|
|
58
|
+
{ pattern: /\{\s*error\s*:/, name: "error" },
|
|
59
|
+
{ pattern: /\{\s*success\s*:/, name: "success" },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect HTTP method from file or code
|
|
64
|
+
*/
|
|
65
|
+
function detectHTTPMethod(code, filePath) {
|
|
66
|
+
const methods = new Set();
|
|
67
|
+
|
|
68
|
+
// Next.js App Router pattern: export function GET/POST/etc
|
|
69
|
+
const appRouterMatch = code.match(/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g);
|
|
70
|
+
if (appRouterMatch) {
|
|
71
|
+
appRouterMatch.forEach(m => {
|
|
72
|
+
const method = m.match(/(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/)[1];
|
|
73
|
+
methods.add(method);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Express pattern: app.get/post/etc or router.get/post/etc
|
|
78
|
+
const expressMatch = code.match(/\.(get|post|put|patch|delete|head|options)\s*\(/gi);
|
|
79
|
+
if (expressMatch) {
|
|
80
|
+
expressMatch.forEach(m => {
|
|
81
|
+
const method = m.match(/\.(get|post|put|patch|delete|head|options)/i)[1].toUpperCase();
|
|
82
|
+
methods.add(method);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Next.js Pages Router pattern
|
|
87
|
+
if (/req\.method\s*===?\s*['"](\w+)['"]/g.test(code)) {
|
|
88
|
+
const methodMatch = code.match(/req\.method\s*===?\s*['"](\w+)['"]/g);
|
|
89
|
+
methodMatch?.forEach(m => {
|
|
90
|
+
const method = m.match(/['"](\w+)['"]/)[1].toUpperCase();
|
|
91
|
+
methods.add(method);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return methods;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check for input validation
|
|
100
|
+
*/
|
|
101
|
+
function hasInputValidation(code) {
|
|
102
|
+
const validationPatterns = [
|
|
103
|
+
/\bzod\b/,
|
|
104
|
+
/\byup\b/,
|
|
105
|
+
/\bjoi\b/,
|
|
106
|
+
/\bvalidate\s*\(/i,
|
|
107
|
+
/\bschema\s*\.\s*parse/,
|
|
108
|
+
/\bschema\s*\.\s*safeParse/,
|
|
109
|
+
/\.input\s*\(/, // tRPC input
|
|
110
|
+
/\bz\s*\.\s*object/,
|
|
111
|
+
/\bz\s*\.\s*string/,
|
|
112
|
+
/\bvalidateBody/i,
|
|
113
|
+
/\brequestSchema/i,
|
|
114
|
+
/\bbodySchema/i,
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
return validationPatterns.some(p => p.test(code));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check for authentication
|
|
122
|
+
*/
|
|
123
|
+
function hasAuthentication(code) {
|
|
124
|
+
const authPatterns = [
|
|
125
|
+
/getServerSession/,
|
|
126
|
+
/getSession/,
|
|
127
|
+
/auth\s*\(\)/,
|
|
128
|
+
/verifyToken/,
|
|
129
|
+
/authenticate/i,
|
|
130
|
+
/authorization/i,
|
|
131
|
+
/protectedProcedure/,
|
|
132
|
+
/withAuth/i,
|
|
133
|
+
/requireAuth/i,
|
|
134
|
+
/isAuthenticated/i,
|
|
135
|
+
/bearer/i,
|
|
136
|
+
/jwt/i,
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
return authPatterns.some(p => p.test(code));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check for rate limiting
|
|
144
|
+
*/
|
|
145
|
+
function hasRateLimiting(code) {
|
|
146
|
+
const rateLimitPatterns = [
|
|
147
|
+
/rateLimit/i,
|
|
148
|
+
/rateLimiter/i,
|
|
149
|
+
/throttle/i,
|
|
150
|
+
/X-RateLimit/i,
|
|
151
|
+
/upstash.*ratelimit/i,
|
|
152
|
+
/@upstash\/ratelimit/,
|
|
153
|
+
/limiter\./i,
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
return rateLimitPatterns.some(p => p.test(code));
|
|
157
|
+
}
|
|
158
|
+
|
|
10
159
|
/**
|
|
11
160
|
* Analyze API consistency issues
|
|
12
161
|
*/
|
|
@@ -16,13 +165,23 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
16
165
|
if (!ast) return findings;
|
|
17
166
|
|
|
18
167
|
const lines = code.split("\n");
|
|
19
|
-
const
|
|
168
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
169
|
+
const isAPIRoute = normalizedPath.includes("/api/") ||
|
|
170
|
+
normalizedPath.includes("/routes/") ||
|
|
171
|
+
normalizedPath.match(/\/route\.(ts|js)$/);
|
|
20
172
|
|
|
21
173
|
if (!isAPIRoute) return findings;
|
|
22
174
|
|
|
23
175
|
const responseFormats = new Set();
|
|
24
176
|
const errorHandlingPatterns = new Set();
|
|
177
|
+
const statusCodesUsed = new Set();
|
|
178
|
+
const responseWrappers = new Set();
|
|
25
179
|
let hasErrorHandler = false;
|
|
180
|
+
let readsRequestBody = false;
|
|
181
|
+
let hasContentTypeCheck = false;
|
|
182
|
+
|
|
183
|
+
// Detect HTTP methods
|
|
184
|
+
const httpMethods = detectHTTPMethod(code, filePath);
|
|
26
185
|
|
|
27
186
|
traverse(ast, {
|
|
28
187
|
// Check response formats
|
|
@@ -32,18 +191,39 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
32
191
|
// Next.js API routes
|
|
33
192
|
if (t.isMemberExpression(node.callee) &&
|
|
34
193
|
t.isIdentifier(node.callee.object, { name: "NextResponse" })) {
|
|
35
|
-
const method = node.callee.property
|
|
194
|
+
const method = node.callee.property?.name;
|
|
36
195
|
if (["json", "redirect", "next"].includes(method)) {
|
|
37
196
|
responseFormats.add(`NextResponse.${method}`);
|
|
38
197
|
}
|
|
198
|
+
|
|
199
|
+
// Check response wrapper in NextResponse.json()
|
|
200
|
+
if (method === "json" && node.arguments[0]) {
|
|
201
|
+
const arg = node.arguments[0];
|
|
202
|
+
if (t.isObjectExpression(arg)) {
|
|
203
|
+
arg.properties.forEach(prop => {
|
|
204
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
|
|
205
|
+
responseWrappers.add(prop.key.name);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
39
210
|
}
|
|
40
211
|
|
|
41
212
|
// Express-style responses
|
|
42
213
|
if (t.isMemberExpression(node.callee)) {
|
|
43
214
|
const prop = node.callee.property;
|
|
44
|
-
if (t.isIdentifier(prop)
|
|
45
|
-
|
|
46
|
-
|
|
215
|
+
if (t.isIdentifier(prop)) {
|
|
216
|
+
if (["json", "send", "status", "redirect"].includes(prop.name)) {
|
|
217
|
+
responseFormats.add(`res.${prop.name}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Track status codes
|
|
221
|
+
if (prop.name === "status" && node.arguments[0]) {
|
|
222
|
+
const statusArg = node.arguments[0];
|
|
223
|
+
if (t.isNumericLiteral(statusArg)) {
|
|
224
|
+
statusCodesUsed.add(statusArg.value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
47
227
|
}
|
|
48
228
|
}
|
|
49
229
|
|
|
@@ -53,6 +233,43 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
53
233
|
hasErrorHandler = true;
|
|
54
234
|
errorHandlingPatterns.add("promise.catch");
|
|
55
235
|
}
|
|
236
|
+
|
|
237
|
+
// Request body access
|
|
238
|
+
if (t.isMemberExpression(node.callee)) {
|
|
239
|
+
const obj = node.callee.object;
|
|
240
|
+
const prop = node.callee.property;
|
|
241
|
+
|
|
242
|
+
// req.body, request.json(), etc.
|
|
243
|
+
if ((t.isIdentifier(obj, { name: "req" }) || t.isIdentifier(obj, { name: "request" })) &&
|
|
244
|
+
t.isIdentifier(prop) && ["body", "json"].includes(prop.name)) {
|
|
245
|
+
readsRequestBody = true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Content-Type check
|
|
250
|
+
if (t.isMemberExpression(node.callee)) {
|
|
251
|
+
const callee = node.callee;
|
|
252
|
+
if (t.isIdentifier(callee.property, { name: "get" }) &&
|
|
253
|
+
node.arguments[0] &&
|
|
254
|
+
t.isStringLiteral(node.arguments[0]) &&
|
|
255
|
+
node.arguments[0].value.toLowerCase() === "content-type") {
|
|
256
|
+
hasContentTypeCheck = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Await request.json() also indicates body reading
|
|
262
|
+
AwaitExpression(path) {
|
|
263
|
+
const arg = path.node.argument;
|
|
264
|
+
if (t.isCallExpression(arg) && t.isMemberExpression(arg.callee)) {
|
|
265
|
+
const obj = arg.callee.object;
|
|
266
|
+
const prop = arg.callee.property;
|
|
267
|
+
if (t.isIdentifier(prop, { name: "json" }) ||
|
|
268
|
+
t.isIdentifier(prop, { name: "formData" }) ||
|
|
269
|
+
t.isIdentifier(prop, { name: "text" })) {
|
|
270
|
+
readsRequestBody = true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
56
273
|
},
|
|
57
274
|
|
|
58
275
|
// Try-catch blocks
|
|
@@ -67,7 +284,6 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
67
284
|
if (t.isBinaryExpression(test) &&
|
|
68
285
|
(test.operator === "===" || test.operator === "==")) {
|
|
69
286
|
const left = test.left;
|
|
70
|
-
const right = test.right;
|
|
71
287
|
|
|
72
288
|
// Check for error status checks
|
|
73
289
|
if ((t.isMemberExpression(left) &&
|
|
@@ -98,18 +314,59 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
98
314
|
},
|
|
99
315
|
});
|
|
100
316
|
|
|
101
|
-
//
|
|
102
|
-
|
|
317
|
+
// REST convention checks
|
|
318
|
+
for (const method of httpMethods) {
|
|
319
|
+
const expectations = HTTP_METHOD_EXPECTATIONS[method];
|
|
320
|
+
if (!expectations) continue;
|
|
321
|
+
|
|
322
|
+
// Check GET/DELETE with body
|
|
323
|
+
if (!expectations.shouldHaveBody && readsRequestBody) {
|
|
324
|
+
findings.push({
|
|
325
|
+
type: "rest_convention_violation",
|
|
326
|
+
severity: "WARN",
|
|
327
|
+
category: "APIConsistency",
|
|
328
|
+
file: filePath,
|
|
329
|
+
line: 1,
|
|
330
|
+
column: 0,
|
|
331
|
+
title: `${method} request should not have body`,
|
|
332
|
+
message: `REST convention: ${method} requests typically don't have a request body. Consider using query parameters instead.`,
|
|
333
|
+
confidence: "med",
|
|
334
|
+
fixHint: "Use query parameters (searchParams) instead of request body for GET requests",
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check POST/PUT/PATCH without body
|
|
339
|
+
if (expectations.shouldHaveBody && !readsRequestBody && method !== "DELETE") {
|
|
340
|
+
findings.push({
|
|
341
|
+
type: "rest_convention_violation",
|
|
342
|
+
severity: "INFO",
|
|
343
|
+
category: "APIConsistency",
|
|
344
|
+
file: filePath,
|
|
345
|
+
line: 1,
|
|
346
|
+
column: 0,
|
|
347
|
+
title: `${method} request without body`,
|
|
348
|
+
message: `${method} requests typically include a request body. This might be intentional for some endpoints.`,
|
|
349
|
+
confidence: "low",
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check for inconsistent response wrappers
|
|
355
|
+
const dataWrappers = Array.from(responseWrappers).filter(w =>
|
|
356
|
+
["data", "result", "payload", "response", "body"].includes(w)
|
|
357
|
+
);
|
|
358
|
+
if (dataWrappers.length > 1) {
|
|
103
359
|
findings.push({
|
|
104
|
-
type: "
|
|
360
|
+
type: "inconsistent_response_wrapper",
|
|
105
361
|
severity: "WARN",
|
|
106
362
|
category: "APIConsistency",
|
|
107
363
|
file: filePath,
|
|
108
364
|
line: 1,
|
|
109
365
|
column: 0,
|
|
110
|
-
title: "Inconsistent
|
|
111
|
-
message: `Multiple response
|
|
112
|
-
confidence: "
|
|
366
|
+
title: "Inconsistent response wrapper keys",
|
|
367
|
+
message: `Multiple response wrapper keys used: ${dataWrappers.join(", ")}. Standardize on one format.`,
|
|
368
|
+
confidence: "med",
|
|
369
|
+
fixHint: "Use a consistent wrapper like { data: ... } or { result: ... } across all endpoints",
|
|
113
370
|
});
|
|
114
371
|
}
|
|
115
372
|
|
|
@@ -123,34 +380,78 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
123
380
|
line: 1,
|
|
124
381
|
column: 0,
|
|
125
382
|
title: "API route missing error handling",
|
|
126
|
-
message: "API route should have try-catch or promise error handling",
|
|
383
|
+
message: "API route should have try-catch or promise error handling to prevent unhandled exceptions",
|
|
127
384
|
confidence: "med",
|
|
385
|
+
fixHint: "Wrap async logic in try-catch and return appropriate error responses",
|
|
128
386
|
});
|
|
129
387
|
}
|
|
130
388
|
|
|
131
|
-
// Check for missing
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (t.isMemberExpression(node.callee) &&
|
|
137
|
-
t.isIdentifier(node.callee.property, { name: "status" })) {
|
|
138
|
-
hasStatusCode = true;
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
if (!hasStatusCode && responseFormats.size > 0) {
|
|
389
|
+
// Check for missing input validation on POST/PUT/PATCH
|
|
390
|
+
const mutationMethods = ["POST", "PUT", "PATCH"];
|
|
391
|
+
const hasMutationMethod = mutationMethods.some(m => httpMethods.has(m));
|
|
392
|
+
|
|
393
|
+
if (hasMutationMethod && readsRequestBody && !hasInputValidation(code)) {
|
|
144
394
|
findings.push({
|
|
145
|
-
type: "
|
|
395
|
+
type: "missing_input_validation",
|
|
146
396
|
severity: "WARN",
|
|
147
397
|
category: "APIConsistency",
|
|
148
398
|
file: filePath,
|
|
149
399
|
line: 1,
|
|
150
400
|
column: 0,
|
|
151
|
-
title: "API
|
|
152
|
-
message: "
|
|
401
|
+
title: "API endpoint missing input validation",
|
|
402
|
+
message: "Mutation endpoints should validate request body. Consider using Zod, Yup, or similar.",
|
|
153
403
|
confidence: "med",
|
|
404
|
+
fixHint: "Add schema validation: const body = schema.parse(await request.json())",
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Check for missing authentication on sensitive endpoints
|
|
409
|
+
const isSensitiveEndpoint = /\/(admin|user|account|settings|billing|payment)/i.test(normalizedPath);
|
|
410
|
+
if (isSensitiveEndpoint && !hasAuthentication(code)) {
|
|
411
|
+
findings.push({
|
|
412
|
+
type: "missing_authentication",
|
|
413
|
+
severity: "WARN",
|
|
414
|
+
category: "APIConsistency",
|
|
415
|
+
file: filePath,
|
|
416
|
+
line: 1,
|
|
417
|
+
column: 0,
|
|
418
|
+
title: "Sensitive endpoint may need authentication",
|
|
419
|
+
message: `Endpoint path suggests sensitive data (${normalizedPath}). Consider adding authentication.`,
|
|
420
|
+
confidence: "low",
|
|
421
|
+
fixHint: "Add authentication check: const session = await getServerSession()",
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Check for missing rate limiting on public endpoints
|
|
426
|
+
const isPublicEndpoint = /(public|webhook|auth\/.*login|auth\/.*register)/i.test(normalizedPath);
|
|
427
|
+
if (isPublicEndpoint && !hasRateLimiting(code)) {
|
|
428
|
+
findings.push({
|
|
429
|
+
type: "missing_rate_limiting",
|
|
430
|
+
severity: "INFO",
|
|
431
|
+
category: "APIConsistency",
|
|
432
|
+
file: filePath,
|
|
433
|
+
line: 1,
|
|
434
|
+
column: 0,
|
|
435
|
+
title: "Public endpoint without rate limiting",
|
|
436
|
+
message: "Public endpoints should consider rate limiting to prevent abuse",
|
|
437
|
+
confidence: "low",
|
|
438
|
+
fixHint: "Consider using @upstash/ratelimit or similar rate limiting solution",
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Check Content-Type validation for POST/PUT/PATCH
|
|
443
|
+
if (hasMutationMethod && readsRequestBody && !hasContentTypeCheck) {
|
|
444
|
+
// This is a low-priority check - most frameworks handle this
|
|
445
|
+
findings.push({
|
|
446
|
+
type: "missing_content_type_check",
|
|
447
|
+
severity: "INFO",
|
|
448
|
+
category: "APIConsistency",
|
|
449
|
+
file: filePath,
|
|
450
|
+
line: 1,
|
|
451
|
+
column: 0,
|
|
452
|
+
title: "No explicit Content-Type validation",
|
|
453
|
+
message: "Consider validating Content-Type header for request body endpoints",
|
|
454
|
+
confidence: "low",
|
|
154
455
|
});
|
|
155
456
|
}
|
|
156
457
|
|
|
@@ -159,4 +460,8 @@ function analyzeAPIConsistency(code, filePath) {
|
|
|
159
460
|
|
|
160
461
|
module.exports = {
|
|
161
462
|
analyzeAPIConsistency,
|
|
463
|
+
detectHTTPMethod,
|
|
464
|
+
hasInputValidation,
|
|
465
|
+
hasAuthentication,
|
|
466
|
+
hasRateLimiting,
|
|
162
467
|
};
|