vettcode-cli 1.0.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.
@@ -0,0 +1,519 @@
1
+ "use strict";
2
+ /**
3
+ * AST-based Code Extraction
4
+ * Intelligently extracts only high-risk code sections to send to AI
5
+ * This reduces token usage by 70-90% while maintaining accuracy
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.extractHighRiskCode = extractHighRiskCode;
45
+ exports.shouldAnalyzeFile = shouldAnalyzeFile;
46
+ const parser = __importStar(require("@babel/parser"));
47
+ const traverse_1 = __importDefault(require("@babel/traverse"));
48
+ // High-risk patterns that indicate code should be analyzed by AI
49
+ const RISK_INDICATORS = {
50
+ // Security-sensitive operations (cross-language)
51
+ userInput: [
52
+ // JavaScript/TypeScript
53
+ "req.body", "req.query", "req.params", "request.body", "params", "searchParams",
54
+ // Python
55
+ "request.form", "request.args", "request.json", "input(", "sys.argv",
56
+ // Java
57
+ "getParameter", "getInputStream", "getReader", "Scanner",
58
+ // PHP
59
+ "$_GET", "$_POST", "$_REQUEST", "$_COOKIE", "$_SERVER",
60
+ // Go
61
+ "r.FormValue", "r.PostFormValue", "r.URL.Query",
62
+ // Ruby
63
+ "params[", "request.params", "gets",
64
+ // C#
65
+ "Request.Form", "Request.QueryString", "Console.ReadLine",
66
+ ],
67
+ database: [
68
+ // JavaScript/TypeScript
69
+ "query", "execute", "findOne", "findMany", "create", "update", "delete", "raw",
70
+ // Python
71
+ "cursor.execute", "session.query", "filter", "get", "all()",
72
+ // Java
73
+ "executeQuery", "executeUpdate", "prepareStatement", "createQuery",
74
+ // PHP
75
+ "mysqli_query", "mysql_query", "PDO", "->query", "->exec",
76
+ // Go
77
+ "db.Query", "db.Exec", "db.QueryRow",
78
+ // Ruby
79
+ "ActiveRecord", ".find", ".where", ".create",
80
+ // C#
81
+ "ExecuteReader", "ExecuteNonQuery", "SqlCommand",
82
+ ],
83
+ fileSystem: [
84
+ // JavaScript/TypeScript
85
+ "readFile", "writeFile", "unlink", "rmdir", "mkdir", "createReadStream",
86
+ // Python
87
+ "open(", "os.remove", "os.rmdir", "shutil", "pathlib",
88
+ // Java
89
+ "FileReader", "FileWriter", "Files.read", "Files.write",
90
+ // PHP
91
+ "fopen", "file_get_contents", "file_put_contents", "unlink",
92
+ // Go
93
+ "os.Open", "os.Create", "ioutil.ReadFile", "os.Remove",
94
+ // Ruby
95
+ "File.open", "File.read", "File.write", "File.delete",
96
+ // C#
97
+ "File.Read", "File.Write", "File.Delete", "StreamReader",
98
+ ],
99
+ network: [
100
+ // JavaScript/TypeScript
101
+ "fetch", "axios", "http.request", "https.request",
102
+ // Python
103
+ "requests.", "urllib", "httplib", "http.client",
104
+ // Java
105
+ "HttpURLConnection", "HttpClient", "RestTemplate",
106
+ // PHP
107
+ "curl_", "file_get_contents", "fopen('http",
108
+ // Go
109
+ "http.Get", "http.Post", "http.Client",
110
+ // Ruby
111
+ "Net::HTTP", "open-uri", "RestClient",
112
+ // C#
113
+ "HttpClient", "WebRequest", "HttpWebRequest",
114
+ ],
115
+ auth: [
116
+ // Cross-language
117
+ "sign", "verify", "hash", "compare", "authenticate", "authorize", "session",
118
+ "password", "token", "jwt", "oauth", "login", "logout",
119
+ // Python
120
+ "hashlib", "bcrypt", "passlib",
121
+ // Java
122
+ "MessageDigest", "BCrypt", "PasswordEncoder",
123
+ // PHP
124
+ "password_hash", "password_verify", "md5", "sha1",
125
+ // Go
126
+ "bcrypt.Generate", "bcrypt.Compare",
127
+ // Ruby
128
+ "BCrypt", "Devise",
129
+ // C#
130
+ "PasswordHasher", "SignInManager",
131
+ ],
132
+ crypto: [
133
+ "createHash", "createCipher", "randomBytes", "pbkdf2", "encrypt", "decrypt",
134
+ "AES", "RSA", "crypto", "cipher",
135
+ ],
136
+ exec: [
137
+ // JavaScript/TypeScript
138
+ "exec", "spawn", "execSync", "spawnSync", "child_process",
139
+ // Python
140
+ "os.system", "subprocess", "exec(", "eval(",
141
+ // Java
142
+ "Runtime.exec", "ProcessBuilder",
143
+ // PHP
144
+ "exec(", "shell_exec", "system(", "passthru",
145
+ // Go
146
+ "exec.Command", "os.StartProcess",
147
+ // Ruby
148
+ "system(", "exec(", "`", "%x",
149
+ // C#
150
+ "Process.Start", "ProcessStartInfo",
151
+ ],
152
+ // Code quality indicators
153
+ complexity: ["if", "else", "switch", "for", "while", "catch"],
154
+ async: ["async", "await", "Promise", ".then", ".catch", "goroutine", "channel"],
155
+ };
156
+ function detectLanguage(filepath) {
157
+ // JavaScript/TypeScript
158
+ if (/\.tsx?$/.test(filepath))
159
+ return "typescript";
160
+ if (/\.jsx?$/.test(filepath))
161
+ return "javascript";
162
+ // Python
163
+ if (/\.pyw?$/.test(filepath))
164
+ return "python";
165
+ // Java
166
+ if (/\.java$/.test(filepath))
167
+ return "java";
168
+ // PHP
169
+ if (/\.php[345]?$/.test(filepath))
170
+ return "php";
171
+ // Go
172
+ if (/\.go$/.test(filepath))
173
+ return "go";
174
+ // Ruby
175
+ if (/\.(rb|rake)$/.test(filepath))
176
+ return "ruby";
177
+ // C#
178
+ if (/\.cs$/.test(filepath))
179
+ return "csharp";
180
+ // C/C++
181
+ if (/\.(c|cpp|cc|cxx|h|hpp|hxx)$/.test(filepath))
182
+ return "cpp";
183
+ // Rust
184
+ if (/\.rs$/.test(filepath))
185
+ return "rust";
186
+ // Kotlin
187
+ if (/\.kts?$/.test(filepath))
188
+ return "kotlin";
189
+ // Swift
190
+ if (/\.swift$/.test(filepath))
191
+ return "swift";
192
+ // Scala
193
+ if (/\.scala$/.test(filepath))
194
+ return "scala";
195
+ return "unknown";
196
+ }
197
+ function calculateRiskScore(code) {
198
+ const factors = [];
199
+ let score = 0;
200
+ // Check for risk indicators
201
+ if (RISK_INDICATORS.userInput.some(p => code.includes(p))) {
202
+ factors.push("handles-user-input");
203
+ score += 3;
204
+ }
205
+ if (RISK_INDICATORS.database.some(p => code.includes(p))) {
206
+ factors.push("database-operation");
207
+ score += 3;
208
+ }
209
+ if (RISK_INDICATORS.fileSystem.some(p => code.includes(p))) {
210
+ factors.push("file-system-access");
211
+ score += 2;
212
+ }
213
+ if (RISK_INDICATORS.auth.some(p => code.includes(p))) {
214
+ factors.push("authentication-logic");
215
+ score += 4;
216
+ }
217
+ if (RISK_INDICATORS.crypto.some(p => code.includes(p))) {
218
+ factors.push("cryptography");
219
+ score += 2;
220
+ }
221
+ if (RISK_INDICATORS.exec.some(p => code.includes(p))) {
222
+ factors.push("command-execution");
223
+ score += 4;
224
+ }
225
+ if (RISK_INDICATORS.network.some(p => code.includes(p))) {
226
+ factors.push("network-request");
227
+ score += 1;
228
+ }
229
+ // Check complexity
230
+ const ifCount = (code.match(/\bif\s*\(/g) || []).length;
231
+ const loopCount = (code.match(/\b(for|while)\s*\(/g) || []).length;
232
+ const tryCount = (code.match(/\btry\s*\{/g) || []).length;
233
+ if (ifCount > 3) {
234
+ factors.push("high-cyclomatic-complexity");
235
+ score += 1;
236
+ }
237
+ if (loopCount > 2) {
238
+ factors.push("nested-loops");
239
+ score += 1;
240
+ }
241
+ if (tryCount === 0 && RISK_INDICATORS.async.some(p => code.includes(p))) {
242
+ // Check if try-catch actually surrounds async operations, not just in comments/strings
243
+ const hasAsyncInTry = /try\s*\{[\s\S]*?(?:await|async|Promise)/.test(code);
244
+ if (!hasAsyncInTry && RISK_INDICATORS.async.some(p => code.includes(p))) {
245
+ factors.push("missing-error-handling");
246
+ score += 2;
247
+ }
248
+ }
249
+ return { score, factors };
250
+ }
251
+ function extractHighRiskCode(filepath, content) {
252
+ const language = detectLanguage(filepath);
253
+ // For non-JS/TS files, use simple pattern matching
254
+ if (language !== "javascript" && language !== "typescript") {
255
+ return extractWithPatterns(filepath, content, language);
256
+ }
257
+ try {
258
+ const sections = [];
259
+ const ast = parser.parse(content, {
260
+ sourceType: "module",
261
+ plugins: [
262
+ "typescript",
263
+ "jsx",
264
+ "decorators-legacy",
265
+ "classProperties",
266
+ "objectRestSpread",
267
+ "asyncGenerators",
268
+ "dynamicImport",
269
+ "optionalChaining",
270
+ "nullishCoalescingOperator",
271
+ ],
272
+ errorRecovery: true,
273
+ });
274
+ const lines = content.split("\n");
275
+ (0, traverse_1.default)(ast, {
276
+ // Extract functions
277
+ FunctionDeclaration(path) {
278
+ const node = path.node;
279
+ if (!node.loc)
280
+ return;
281
+ const funcCode = lines.slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
282
+ const { score, factors } = calculateRiskScore(funcCode);
283
+ if (score >= 2) {
284
+ sections.push({
285
+ type: "function",
286
+ name: node.id?.name || "anonymous",
287
+ code: funcCode,
288
+ startLine: node.loc.start.line,
289
+ endLine: node.loc.end.line,
290
+ riskFactors: factors,
291
+ context: `Function: ${node.id?.name || "anonymous"}`,
292
+ });
293
+ }
294
+ },
295
+ // Extract arrow functions and methods
296
+ ArrowFunctionExpression(path) {
297
+ const node = path.node;
298
+ if (!node.loc)
299
+ return;
300
+ const funcCode = lines.slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
301
+ const { score, factors } = calculateRiskScore(funcCode);
302
+ if (score >= 2) {
303
+ const parent = path.parent;
304
+ let name = "anonymous";
305
+ if (parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
306
+ name = parent.id.name;
307
+ }
308
+ sections.push({
309
+ type: "arrow-function",
310
+ name,
311
+ code: funcCode,
312
+ startLine: node.loc.start.line,
313
+ endLine: node.loc.end.line,
314
+ riskFactors: factors,
315
+ context: `Arrow function: ${name}`,
316
+ });
317
+ }
318
+ },
319
+ // Extract class methods
320
+ ClassMethod(path) {
321
+ const node = path.node;
322
+ if (!node.loc)
323
+ return;
324
+ const methodCode = lines.slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
325
+ const { score, factors } = calculateRiskScore(methodCode);
326
+ if (score >= 2) {
327
+ const className = path.parentPath.parent.type === "ClassDeclaration"
328
+ ? path.parentPath.parent.id?.name
329
+ : "Anonymous";
330
+ const methodName = node.key.type === "Identifier" ? node.key.name : "unknown";
331
+ sections.push({
332
+ type: "class-method",
333
+ name: `${className}.${methodName}`,
334
+ code: methodCode,
335
+ startLine: node.loc.start.line,
336
+ endLine: node.loc.end.line,
337
+ riskFactors: factors,
338
+ context: `Method: ${className}.${methodName}`,
339
+ });
340
+ }
341
+ },
342
+ // Extract API route handlers (Next.js, Express, etc.)
343
+ ExportNamedDeclaration(path) {
344
+ const node = path.node;
345
+ if (!node.loc || !node.declaration)
346
+ return;
347
+ const declCode = lines.slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
348
+ // Check if it's an API handler (GET, POST, etc.)
349
+ if (/export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)/i.test(declCode)) {
350
+ const { score, factors } = calculateRiskScore(declCode);
351
+ factors.push("api-endpoint");
352
+ sections.push({
353
+ type: "api-handler",
354
+ name: declCode.match(/function\s+(\w+)/)?.[1] || "handler",
355
+ code: declCode,
356
+ startLine: node.loc.start.line,
357
+ endLine: node.loc.end.line,
358
+ riskFactors: [...factors, "api-endpoint"],
359
+ context: "API Route Handler",
360
+ });
361
+ }
362
+ },
363
+ });
364
+ // Sort by risk score (highest first)
365
+ sections.sort((a, b) => b.riskFactors.length - a.riskFactors.length);
366
+ // Extract top 15 highest-risk sections per file (increased for better AI analysis)
367
+ const topSections = sections
368
+ .slice(0, 15)
369
+ .map(section => ({
370
+ ...section,
371
+ // Keep full code sections for AI analysis (removed truncation)
372
+ code: section.code
373
+ }));
374
+ if (topSections.length === 0)
375
+ return null;
376
+ return {
377
+ file: filepath,
378
+ language,
379
+ sections: topSections,
380
+ summary: `Extracted ${topSections.length} high-risk code sections from ${filepath}`,
381
+ };
382
+ }
383
+ catch (error) {
384
+ // If AST parsing fails, fall back to pattern matching
385
+ // Log error for debugging but continue with fallback
386
+ if (error instanceof Error) {
387
+ console.error(`[AST Extraction] Failed to parse ${filepath}: ${error.message}`);
388
+ }
389
+ return extractWithPatterns(filepath, content, language);
390
+ }
391
+ }
392
+ function extractWithPatterns(filepath, content, language) {
393
+ const sections = [];
394
+ const lines = content.split("\n");
395
+ // Pattern-based extraction for non-JS languages or when AST fails
396
+ const functionPatterns = [
397
+ /^(async\s+)?function\s+(\w+)/gm,
398
+ /^(export\s+)?(async\s+)?function\s+(\w+)/gm,
399
+ /const\s+(\w+)\s*=\s*(async\s*)?\([^)]*\)\s*=>/gm,
400
+ /def\s+(\w+)\s*\(/gm, // Python
401
+ /func\s+(\w+)\s*\(/gm, // Go
402
+ /public\s+\w+\s+(\w+)\s*\(/gm, // Java
403
+ ];
404
+ for (let i = 0; i < lines.length; i++) {
405
+ const line = lines[i];
406
+ for (const pattern of functionPatterns) {
407
+ // Reset lastIndex for global regex to prevent skipping matches
408
+ pattern.lastIndex = 0;
409
+ const match = pattern.exec(line);
410
+ if (match) {
411
+ // Extract function body (next 30 lines or until next function)
412
+ let endLine = Math.min(i + 30, lines.length);
413
+ for (let j = i + 1; j < lines.length && j < i + 50; j++) {
414
+ if (functionPatterns.some(p => {
415
+ p.lastIndex = 0;
416
+ return p.test(lines[j]);
417
+ })) {
418
+ endLine = j;
419
+ break;
420
+ }
421
+ }
422
+ const funcCode = lines.slice(i, endLine).join("\n");
423
+ const { score, factors } = calculateRiskScore(funcCode);
424
+ if (score >= 2) {
425
+ sections.push({
426
+ type: "function",
427
+ name: match[2] || match[1] || "unknown",
428
+ code: funcCode,
429
+ startLine: i + 1,
430
+ endLine,
431
+ riskFactors: factors,
432
+ context: `Function at line ${i + 1}`,
433
+ });
434
+ }
435
+ }
436
+ }
437
+ }
438
+ if (sections.length === 0)
439
+ return null;
440
+ return {
441
+ file: filepath,
442
+ language,
443
+ sections: sections.slice(0, 15).map(section => ({
444
+ ...section,
445
+ // Keep full code for AI analysis
446
+ code: section.code
447
+ })),
448
+ summary: `Pattern-extracted ${sections.length} sections from ${filepath}`,
449
+ };
450
+ }
451
+ function shouldAnalyzeFile(filepath) {
452
+ // Only analyze source code files
453
+ const analyzePatterns = [
454
+ // JavaScript/TypeScript
455
+ /\.[jt]sx?$/,
456
+ // Python
457
+ /\.py$/,
458
+ /\.pyw$/,
459
+ // Java
460
+ /\.java$/,
461
+ // PHP
462
+ /\.php$/,
463
+ /\.php[345]?$/,
464
+ // Go
465
+ /\.go$/,
466
+ // Ruby
467
+ /\.rb$/,
468
+ /\.rake$/,
469
+ // C#
470
+ /\.cs$/,
471
+ // C/C++
472
+ /\.[ch]$/,
473
+ /\.cpp$/,
474
+ /\.cc$/,
475
+ /\.cxx$/,
476
+ /\.hpp$/,
477
+ /\.hxx$/,
478
+ // Rust
479
+ /\.rs$/,
480
+ // Kotlin
481
+ /\.kt$/,
482
+ /\.kts$/,
483
+ // Swift
484
+ /\.swift$/,
485
+ // Scala
486
+ /\.scala$/,
487
+ ];
488
+ // Skip test files, configs, and non-code files
489
+ const skipPatterns = [
490
+ /\.test\.[jt]sx?$/,
491
+ /\.spec\.[jt]sx?$/,
492
+ /\.stories\.[jt]sx?$/,
493
+ /\.d\.ts$/,
494
+ /\.min\.[jt]s$/,
495
+ /\.bundle\.[jt]s$/,
496
+ /\.config\.[jt]s$/,
497
+ /package-lock\.json$/,
498
+ /yarn\.lock$/,
499
+ /\.lock$/,
500
+ /\.md$/,
501
+ /\.txt$/,
502
+ /\.json$/,
503
+ /\.css$/,
504
+ /\.scss$/,
505
+ /\.sass$/,
506
+ /\.less$/,
507
+ /\.html$/,
508
+ /\.xml$/,
509
+ /\.svg$/,
510
+ /\.yml$/,
511
+ /\.yaml$/,
512
+ ];
513
+ // First check if it should be skipped
514
+ if (skipPatterns.some(pattern => pattern.test(filepath))) {
515
+ return false;
516
+ }
517
+ // Then check if it matches a source code pattern
518
+ return analyzePatterns.some(pattern => pattern.test(filepath));
519
+ }