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.
- package/.env.example +20 -0
- package/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/ast-extractor.js +519 -0
- package/dist/cli-scan-orchestrator.js +336 -0
- package/dist/cli.js +208 -0
- package/dist/control-flow-analyzer.js +184 -0
- package/dist/data-flow-analyzer.js +197 -0
- package/dist/enhanced-patterns.js +457 -0
- package/dist/file-collector.js +132 -0
- package/dist/ignore-patterns.js +225 -0
- package/dist/openrouter.js +311 -0
- package/dist/patterns.js +248 -0
- package/dist/prompts.js +144 -0
- package/dist/reference-graph.js +415 -0
- package/dist/report-generator.js +128 -0
- package/dist/scan-priority.js +49 -0
- package/dist/smart-scan-orchestrator.js +878 -0
- package/dist/static-analyzer.js +1681 -0
- package/dist/types.js +2 -0
- package/dist/verification-layer.js +525 -0
- package/package.json +61 -0
|
@@ -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
|
+
}
|