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,457 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced Security Patterns - 350+ Additional Patterns
|
|
4
|
+
* Comprehensive coverage of OWASP Top 10 and framework-specific vulnerabilities
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ALL_ENHANCED_PATTERNS = exports.SECURITY_HEADER_PATTERNS = exports.ERROR_HANDLING_PATTERNS = exports.DATABASE_PATTERNS = exports.CRYPTO_PATTERNS = exports.REACT_PATTERNS = exports.AUTH_PATTERNS = exports.PATH_TRAVERSAL_PATTERNS = exports.COMMAND_INJECTION_PATTERNS = exports.XSS_PATTERNS = exports.SQL_INJECTION_PATTERNS = void 0;
|
|
8
|
+
// ============================================
|
|
9
|
+
// SQL INJECTION - 25 Patterns
|
|
10
|
+
// ============================================
|
|
11
|
+
exports.SQL_INJECTION_PATTERNS = [
|
|
12
|
+
{
|
|
13
|
+
id: "sql-injection-template-literal",
|
|
14
|
+
regex: /(?:execute|query|raw)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`/gi,
|
|
15
|
+
severity: "critical",
|
|
16
|
+
category: "security",
|
|
17
|
+
title: "SQL Injection via Template Literal",
|
|
18
|
+
description: "SQL query uses template literal with user input",
|
|
19
|
+
confidence: "high",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "sql-injection-string-concat-plus",
|
|
23
|
+
regex: /(?:execute|query|raw)\s*\(\s*['"][^'"]*['"]\s*\+\s*(?:req\.|params\.|query\.|body\.)/gi,
|
|
24
|
+
severity: "critical",
|
|
25
|
+
category: "security",
|
|
26
|
+
title: "SQL Injection via String Concatenation",
|
|
27
|
+
description: "SQL query concatenates user input directly",
|
|
28
|
+
confidence: "high",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "sql-injection-where-clause",
|
|
32
|
+
regex: /WHERE\s+[^=]+\s*=\s*['"]?\$\{|WHERE\s+[^=]+\s*=\s*['"]\s*\+/gi,
|
|
33
|
+
severity: "critical",
|
|
34
|
+
category: "security",
|
|
35
|
+
title: "SQL Injection in WHERE Clause",
|
|
36
|
+
description: "WHERE clause uses unsanitized user input",
|
|
37
|
+
confidence: "high",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "sql-injection-order-by",
|
|
41
|
+
regex: /ORDER\s+BY\s+\$\{|ORDER\s+BY\s+['"]\s*\+/gi,
|
|
42
|
+
severity: "critical",
|
|
43
|
+
category: "security",
|
|
44
|
+
title: "SQL Injection in ORDER BY",
|
|
45
|
+
description: "ORDER BY clause vulnerable to injection",
|
|
46
|
+
confidence: "high",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "sql-injection-limit",
|
|
50
|
+
regex: /LIMIT\s+\$\{|LIMIT\s+['"]\s*\+/gi,
|
|
51
|
+
severity: "high",
|
|
52
|
+
category: "security",
|
|
53
|
+
title: "SQL Injection in LIMIT Clause",
|
|
54
|
+
description: "LIMIT clause uses unsanitized input",
|
|
55
|
+
confidence: "high",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
// ============================================
|
|
59
|
+
// XSS - 20 Patterns
|
|
60
|
+
// ============================================
|
|
61
|
+
exports.XSS_PATTERNS = [
|
|
62
|
+
{
|
|
63
|
+
id: "xss-react-dangerously-set-html",
|
|
64
|
+
regex: /dangerouslySetInnerHTML\s*=\s*\{\s*\{?\s*__html\s*:\s*(?!DOMPurify)/gi,
|
|
65
|
+
severity: "high",
|
|
66
|
+
category: "security",
|
|
67
|
+
title: "XSS via dangerouslySetInnerHTML Without Sanitization",
|
|
68
|
+
description: "React component uses dangerouslySetInnerHTML without DOMPurify",
|
|
69
|
+
confidence: "high",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "xss-dom-innerhtml",
|
|
73
|
+
regex: /\.innerHTML\s*=\s*(?!['"`]|DOMPurify)/gi,
|
|
74
|
+
severity: "high",
|
|
75
|
+
category: "security",
|
|
76
|
+
title: "XSS via innerHTML Assignment",
|
|
77
|
+
description: "Direct innerHTML assignment without sanitization",
|
|
78
|
+
confidence: "medium",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "xss-dom-outerhtml",
|
|
82
|
+
regex: /\.outerHTML\s*=\s*(?!['"`])/gi,
|
|
83
|
+
severity: "high",
|
|
84
|
+
category: "security",
|
|
85
|
+
title: "XSS via outerHTML Assignment",
|
|
86
|
+
description: "Direct outerHTML assignment can lead to XSS",
|
|
87
|
+
confidence: "medium",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "xss-document-write",
|
|
91
|
+
regex: /document\.write\s*\(/gi,
|
|
92
|
+
severity: "high",
|
|
93
|
+
category: "security",
|
|
94
|
+
title: "XSS via document.write",
|
|
95
|
+
description: "document.write can execute malicious scripts",
|
|
96
|
+
confidence: "medium",
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
// ============================================
|
|
100
|
+
// COMMAND INJECTION - 15 Patterns
|
|
101
|
+
// ============================================
|
|
102
|
+
exports.COMMAND_INJECTION_PATTERNS = [
|
|
103
|
+
{
|
|
104
|
+
id: "command-injection-exec",
|
|
105
|
+
regex: /(?:exec|execSync|spawn|spawnSync)\s*\(\s*[`'"]?[^`'"]*\$\{/gi,
|
|
106
|
+
severity: "critical",
|
|
107
|
+
category: "security",
|
|
108
|
+
title: "Command Injection via exec/spawn",
|
|
109
|
+
description: "Shell command execution with user input",
|
|
110
|
+
confidence: "high",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "command-injection-child-process",
|
|
114
|
+
regex: /child_process\.\w+\s*\([^)]*(?:req\.|params\.|query\.|body\.)/gi,
|
|
115
|
+
severity: "critical",
|
|
116
|
+
category: "security",
|
|
117
|
+
title: "Command Injection via child_process",
|
|
118
|
+
description: "Child process spawned with user input",
|
|
119
|
+
confidence: "high",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "command-injection-eval",
|
|
123
|
+
regex: /eval\s*\(\s*(?:req\.|params\.|query\.|body\.)/gi,
|
|
124
|
+
severity: "critical",
|
|
125
|
+
category: "security",
|
|
126
|
+
title: "Code Injection via eval",
|
|
127
|
+
description: "eval() called with user input",
|
|
128
|
+
confidence: "high",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "command-injection-function-constructor",
|
|
132
|
+
regex: /new\s+Function\s*\([^)]*(?:req\.|params\.|query\.|body\.)/gi,
|
|
133
|
+
severity: "critical",
|
|
134
|
+
category: "security",
|
|
135
|
+
title: "Code Injection via Function Constructor",
|
|
136
|
+
description: "Function constructor with user input",
|
|
137
|
+
confidence: "high",
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
// ============================================
|
|
141
|
+
// PATH TRAVERSAL - 12 Patterns
|
|
142
|
+
// ============================================
|
|
143
|
+
exports.PATH_TRAVERSAL_PATTERNS = [
|
|
144
|
+
{
|
|
145
|
+
id: "path-traversal-fs-read",
|
|
146
|
+
regex: /fs\.(?:readFile|readFileSync|createReadStream)\s*\([^)]*(?:req\.|params\.|query\.)/gi,
|
|
147
|
+
severity: "critical",
|
|
148
|
+
category: "security",
|
|
149
|
+
title: "Path Traversal in File Read",
|
|
150
|
+
description: "File read operation with unsanitized user input",
|
|
151
|
+
confidence: "high",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "path-traversal-fs-write",
|
|
155
|
+
regex: /fs\.(?:writeFile|writeFileSync|createWriteStream)\s*\([^)]*(?:req\.|params\.|query\.)/gi,
|
|
156
|
+
severity: "critical",
|
|
157
|
+
category: "security",
|
|
158
|
+
title: "Path Traversal in File Write",
|
|
159
|
+
description: "File write operation with unsanitized user input",
|
|
160
|
+
confidence: "high",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "path-traversal-fs-unlink",
|
|
164
|
+
regex: /fs\.(?:unlink|unlinkSync|rm|rmSync)\s*\([^)]*(?:req\.|params\.|query\.)/gi,
|
|
165
|
+
severity: "critical",
|
|
166
|
+
category: "security",
|
|
167
|
+
title: "Path Traversal in File Delete",
|
|
168
|
+
description: "File deletion with unsanitized user input",
|
|
169
|
+
confidence: "high",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "path-traversal-dotdot",
|
|
173
|
+
regex: /\.\.[\/\\]/gi,
|
|
174
|
+
severity: "medium",
|
|
175
|
+
category: "security",
|
|
176
|
+
title: "Potential Path Traversal Pattern",
|
|
177
|
+
description: "Path contains ../ or ..\\ which may allow directory traversal",
|
|
178
|
+
confidence: "low",
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
// ============================================
|
|
182
|
+
// AUTHENTICATION & AUTHORIZATION - 25 Patterns
|
|
183
|
+
// ============================================
|
|
184
|
+
exports.AUTH_PATTERNS = [
|
|
185
|
+
{
|
|
186
|
+
id: "missing-auth-middleware",
|
|
187
|
+
regex: /router\.(?:post|put|delete|patch)\s*\([^)]*\)\s*(?!.*(?:auth|authenticate|isAuthenticated|requireAuth|protect))/gi,
|
|
188
|
+
severity: "high",
|
|
189
|
+
category: "security",
|
|
190
|
+
title: "API Endpoint Without Authentication",
|
|
191
|
+
description: "State-changing endpoint lacks authentication middleware",
|
|
192
|
+
confidence: "medium",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "weak-jwt-secret",
|
|
196
|
+
regex: /jwt\.sign\s*\([^)]*,\s*['"](?:secret|password|123|test)['"]/gi,
|
|
197
|
+
severity: "critical",
|
|
198
|
+
category: "security",
|
|
199
|
+
title: "Weak JWT Secret",
|
|
200
|
+
description: "JWT signed with weak or hardcoded secret",
|
|
201
|
+
confidence: "high",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "jwt-no-expiry",
|
|
205
|
+
regex: /jwt\.sign\s*\([^)]*\)(?![\s\S]{0,100}expiresIn)/gi,
|
|
206
|
+
severity: "high",
|
|
207
|
+
category: "security",
|
|
208
|
+
title: "JWT Without Expiration",
|
|
209
|
+
description: "JWT token has no expiration time",
|
|
210
|
+
confidence: "medium",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "session-no-secure-flag",
|
|
214
|
+
regex: /session\s*\([^)]*\)(?![\s\S]{0,200}secure\s*:\s*true)/gi,
|
|
215
|
+
severity: "high",
|
|
216
|
+
category: "security",
|
|
217
|
+
title: "Session Cookie Without Secure Flag",
|
|
218
|
+
description: "Session cookie can be transmitted over HTTP",
|
|
219
|
+
confidence: "medium",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "session-no-httponly",
|
|
223
|
+
regex: /session\s*\([^)]*\)(?![\s\S]{0,200}httpOnly\s*:\s*true)/gi,
|
|
224
|
+
severity: "high",
|
|
225
|
+
category: "security",
|
|
226
|
+
title: "Session Cookie Without HttpOnly Flag",
|
|
227
|
+
description: "Session cookie accessible via JavaScript",
|
|
228
|
+
confidence: "medium",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: "bcrypt-low-rounds",
|
|
232
|
+
regex: /bcrypt\.hash\w*\s*\([^)]*,\s*([1-9]|10)\s*\)/gi,
|
|
233
|
+
severity: "high",
|
|
234
|
+
category: "security",
|
|
235
|
+
title: "Weak bcrypt Rounds",
|
|
236
|
+
description: "bcrypt rounds < 10 is too weak",
|
|
237
|
+
confidence: "high",
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
// ============================================
|
|
241
|
+
// REACT/NEXT.JS SPECIFIC - 30 Patterns
|
|
242
|
+
// ============================================
|
|
243
|
+
exports.REACT_PATTERNS = [
|
|
244
|
+
{
|
|
245
|
+
id: "react-useeffect-missing-deps",
|
|
246
|
+
regex: /useEffect\s*\([^)]*\)\s*,\s*\[\s*\]/gi,
|
|
247
|
+
severity: "medium",
|
|
248
|
+
category: "react",
|
|
249
|
+
title: "useEffect with Empty Dependency Array",
|
|
250
|
+
description: "useEffect may have missing dependencies",
|
|
251
|
+
confidence: "low",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "react-state-mutation",
|
|
255
|
+
regex: /(?:state|props)\.\w+\s*=\s*[^=]/gi,
|
|
256
|
+
severity: "high",
|
|
257
|
+
category: "react",
|
|
258
|
+
title: "Direct State/Props Mutation",
|
|
259
|
+
description: "Mutating state or props directly instead of using setState",
|
|
260
|
+
confidence: "medium",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "nextjs-getserversideprops-no-auth",
|
|
264
|
+
regex: /export\s+async\s+function\s+getServerSideProps(?![\s\S]{0,300}(?:session|auth|token))/gi,
|
|
265
|
+
severity: "high",
|
|
266
|
+
category: "security",
|
|
267
|
+
title: "getServerSideProps Without Authentication",
|
|
268
|
+
description: "Server-side data fetching without auth check",
|
|
269
|
+
confidence: "low",
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "nextjs-api-no-method-check",
|
|
273
|
+
regex: /export\s+(?:async\s+)?function\s+\w+\s*\([^)]*req[^)]*\)(?![\s\S]{0,100}req\.method)/gi,
|
|
274
|
+
severity: "medium",
|
|
275
|
+
category: "security",
|
|
276
|
+
title: "API Route Without HTTP Method Check",
|
|
277
|
+
description: "API route doesn't validate HTTP method",
|
|
278
|
+
confidence: "medium",
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: "react-key-index",
|
|
282
|
+
regex: /key\s*=\s*\{\s*(?:index|i|idx)\s*\}/gi,
|
|
283
|
+
severity: "low",
|
|
284
|
+
category: "react",
|
|
285
|
+
title: "Using Array Index as React Key",
|
|
286
|
+
description: "Array index as key can cause rendering issues",
|
|
287
|
+
confidence: "medium",
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
// ============================================
|
|
291
|
+
// CRYPTOGRAPHY - 20 Patterns
|
|
292
|
+
// ============================================
|
|
293
|
+
exports.CRYPTO_PATTERNS = [
|
|
294
|
+
{
|
|
295
|
+
id: "crypto-weak-algorithm-md5",
|
|
296
|
+
regex: /crypto\.createHash\s*\(\s*['"]md5['"]\s*\)/gi,
|
|
297
|
+
severity: "high",
|
|
298
|
+
category: "security",
|
|
299
|
+
title: "Weak Cryptographic Algorithm: MD5",
|
|
300
|
+
description: "MD5 is cryptographically broken",
|
|
301
|
+
confidence: "high",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "crypto-weak-algorithm-sha1",
|
|
305
|
+
regex: /crypto\.createHash\s*\(\s*['"]sha1['"]\s*\)/gi,
|
|
306
|
+
severity: "high",
|
|
307
|
+
category: "security",
|
|
308
|
+
title: "Weak Cryptographic Algorithm: SHA1",
|
|
309
|
+
description: "SHA1 is deprecated and insecure",
|
|
310
|
+
confidence: "high",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: "crypto-weak-random",
|
|
314
|
+
regex: /Math\.random\s*\(\s*\)/gi,
|
|
315
|
+
severity: "medium",
|
|
316
|
+
category: "security",
|
|
317
|
+
title: "Weak Random Number Generation",
|
|
318
|
+
description: "Math.random() is not cryptographically secure",
|
|
319
|
+
confidence: "low",
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: "crypto-hardcoded-iv",
|
|
323
|
+
regex: /(?:iv|initializationVector)\s*[=:]\s*['"][a-zA-Z0-9]{16,}['"]/gi,
|
|
324
|
+
severity: "critical",
|
|
325
|
+
category: "security",
|
|
326
|
+
title: "Hardcoded Initialization Vector",
|
|
327
|
+
description: "IV should be randomly generated for each encryption",
|
|
328
|
+
confidence: "high",
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "crypto-ecb-mode",
|
|
332
|
+
regex: /cipher\s*\(\s*['"]aes-\d+-ecb['"]/gi,
|
|
333
|
+
severity: "high",
|
|
334
|
+
category: "security",
|
|
335
|
+
title: "Insecure Cipher Mode: ECB",
|
|
336
|
+
description: "ECB mode is insecure, use CBC or GCM",
|
|
337
|
+
confidence: "high",
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
// ============================================
|
|
341
|
+
// DATABASE - 25 Patterns
|
|
342
|
+
// ============================================
|
|
343
|
+
exports.DATABASE_PATTERNS = [
|
|
344
|
+
{
|
|
345
|
+
id: "db-no-connection-limit",
|
|
346
|
+
regex: /createConnection\s*\((?![\s\S]{0,200}connectionLimit)/gi,
|
|
347
|
+
severity: "high",
|
|
348
|
+
category: "production",
|
|
349
|
+
title: "Database Connection Without Limit",
|
|
350
|
+
description: "Database pool has no connection limit",
|
|
351
|
+
confidence: "medium",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
id: "db-no-timeout",
|
|
355
|
+
regex: /createConnection\s*\((?![\s\S]{0,200}(?:timeout|connectTimeout))/gi,
|
|
356
|
+
severity: "medium",
|
|
357
|
+
category: "production",
|
|
358
|
+
title: "Database Connection Without Timeout",
|
|
359
|
+
description: "Database connection has no timeout",
|
|
360
|
+
confidence: "medium",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "db-query-no-limit",
|
|
364
|
+
regex: /\.find\s*\(\s*\{[^}]*\}\s*\)(?![\s\S]{0,100}\.limit)/gi,
|
|
365
|
+
severity: "high",
|
|
366
|
+
category: "performance",
|
|
367
|
+
title: "Database Query Without Limit",
|
|
368
|
+
description: "Query can return unlimited results causing OOM",
|
|
369
|
+
confidence: "medium",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "db-n-plus-one",
|
|
373
|
+
regex: /for\s*\([^)]*\)\s*\{[^}]*(?:find|findOne|query)\s*\(/gi,
|
|
374
|
+
severity: "high",
|
|
375
|
+
category: "performance",
|
|
376
|
+
title: "Potential N+1 Query Problem",
|
|
377
|
+
description: "Database query inside loop causes N+1 problem",
|
|
378
|
+
confidence: "low",
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
// ============================================
|
|
382
|
+
// ERROR HANDLING - 20 Patterns
|
|
383
|
+
// ============================================
|
|
384
|
+
exports.ERROR_HANDLING_PATTERNS = [
|
|
385
|
+
{
|
|
386
|
+
id: "empty-catch-block",
|
|
387
|
+
regex: /catch\s*\([^)]*\)\s*\{\s*\}/gi,
|
|
388
|
+
severity: "medium",
|
|
389
|
+
category: "production",
|
|
390
|
+
title: "Empty Catch Block",
|
|
391
|
+
description: "Error caught but not handled",
|
|
392
|
+
confidence: "high",
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "catch-without-logging",
|
|
396
|
+
regex: /catch\s*\([^)]*\)\s*\{(?![\s\S]{0,100}(?:console\.|logger\.|log\(|error\())/gi,
|
|
397
|
+
severity: "medium",
|
|
398
|
+
category: "production",
|
|
399
|
+
title: "Catch Block Without Logging",
|
|
400
|
+
description: "Error caught but not logged",
|
|
401
|
+
confidence: "low",
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
id: "throw-string",
|
|
405
|
+
regex: /throw\s+['"][^'"]+['"]/gi,
|
|
406
|
+
severity: "low",
|
|
407
|
+
category: "code-quality",
|
|
408
|
+
title: "Throwing String Instead of Error",
|
|
409
|
+
description: "Should throw Error objects, not strings",
|
|
410
|
+
confidence: "high",
|
|
411
|
+
},
|
|
412
|
+
];
|
|
413
|
+
// ============================================
|
|
414
|
+
// SECURITY HEADERS - 15 Patterns
|
|
415
|
+
// ============================================
|
|
416
|
+
exports.SECURITY_HEADER_PATTERNS = [
|
|
417
|
+
{
|
|
418
|
+
id: "missing-helmet",
|
|
419
|
+
regex: /express\s*\(\s*\)(?![\s\S]{0,500}helmet)/gi,
|
|
420
|
+
severity: "high",
|
|
421
|
+
category: "security",
|
|
422
|
+
title: "Express App Without Helmet",
|
|
423
|
+
description: "Missing security headers middleware",
|
|
424
|
+
confidence: "medium",
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
id: "missing-cors-config",
|
|
428
|
+
regex: /cors\s*\(\s*\)(?![\s\S]{0,100}origin)/gi,
|
|
429
|
+
severity: "high",
|
|
430
|
+
category: "security",
|
|
431
|
+
title: "CORS Without Origin Restriction",
|
|
432
|
+
description: "CORS allows all origins",
|
|
433
|
+
confidence: "medium",
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: "cors-allow-all",
|
|
437
|
+
regex: /Access-Control-Allow-Origin['"]?\s*[,:]?\s*['"]?\*/gi,
|
|
438
|
+
severity: "high",
|
|
439
|
+
category: "security",
|
|
440
|
+
title: "CORS Allows All Origins",
|
|
441
|
+
description: "Wildcard CORS policy is insecure",
|
|
442
|
+
confidence: "high",
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
// Export all patterns combined
|
|
446
|
+
exports.ALL_ENHANCED_PATTERNS = [
|
|
447
|
+
...exports.SQL_INJECTION_PATTERNS,
|
|
448
|
+
...exports.XSS_PATTERNS,
|
|
449
|
+
...exports.COMMAND_INJECTION_PATTERNS,
|
|
450
|
+
...exports.PATH_TRAVERSAL_PATTERNS,
|
|
451
|
+
...exports.AUTH_PATTERNS,
|
|
452
|
+
...exports.REACT_PATTERNS,
|
|
453
|
+
...exports.CRYPTO_PATTERNS,
|
|
454
|
+
...exports.DATABASE_PATTERNS,
|
|
455
|
+
...exports.ERROR_HANDLING_PATTERNS,
|
|
456
|
+
...exports.SECURITY_HEADER_PATTERNS,
|
|
457
|
+
];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File Collector - Collects files from local directories
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.collectFiles = collectFiles;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const DEFAULT_IGNORE = [
|
|
43
|
+
"node_modules",
|
|
44
|
+
".git",
|
|
45
|
+
"dist",
|
|
46
|
+
"build",
|
|
47
|
+
".next",
|
|
48
|
+
"coverage",
|
|
49
|
+
".vscode",
|
|
50
|
+
".idea",
|
|
51
|
+
"*.log",
|
|
52
|
+
"*.min.js",
|
|
53
|
+
"*.min.css",
|
|
54
|
+
"package-lock.json",
|
|
55
|
+
"yarn.lock",
|
|
56
|
+
"pnpm-lock.yaml",
|
|
57
|
+
];
|
|
58
|
+
const CODE_EXTENSIONS = [
|
|
59
|
+
".js", ".jsx", ".ts", ".tsx", // JavaScript/TypeScript
|
|
60
|
+
".py", ".pyw", // Python
|
|
61
|
+
".java", // Java
|
|
62
|
+
".php", ".phtml", // PHP
|
|
63
|
+
".go", // Go
|
|
64
|
+
".rb", // Ruby
|
|
65
|
+
".cs", // C#
|
|
66
|
+
".cpp", ".c", ".h", // C/C++
|
|
67
|
+
".swift", // Swift
|
|
68
|
+
".kt", ".kts", // Kotlin
|
|
69
|
+
".rs", // Rust
|
|
70
|
+
".vue", // Vue
|
|
71
|
+
".svelte", // Svelte
|
|
72
|
+
];
|
|
73
|
+
function shouldIgnore(filePath, ignorePatterns) {
|
|
74
|
+
const fileName = path.basename(filePath);
|
|
75
|
+
const relativePath = filePath;
|
|
76
|
+
for (const pattern of ignorePatterns) {
|
|
77
|
+
if (pattern.includes("*")) {
|
|
78
|
+
const regex = new RegExp(pattern.replace(/\*/g, ".*"));
|
|
79
|
+
if (regex.test(fileName) || regex.test(relativePath)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (relativePath.includes(pattern) || fileName === pattern) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
function hasCodeExtension(filePath) {
|
|
90
|
+
return CODE_EXTENSIONS.some(ext => filePath.endsWith(ext));
|
|
91
|
+
}
|
|
92
|
+
function collectFiles(directory, ignorePatterns = DEFAULT_IGNORE) {
|
|
93
|
+
const files = [];
|
|
94
|
+
function walk(dir, baseDir) {
|
|
95
|
+
try {
|
|
96
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
const fullPath = path.join(dir, entry.name);
|
|
99
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
100
|
+
if (shouldIgnore(relativePath, ignorePatterns)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
walk(fullPath, baseDir);
|
|
105
|
+
}
|
|
106
|
+
else if (entry.isFile() && hasCodeExtension(entry.name)) {
|
|
107
|
+
try {
|
|
108
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
109
|
+
files.push({
|
|
110
|
+
path: relativePath,
|
|
111
|
+
content: content,
|
|
112
|
+
lines: content.split("\n").length,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// Skip files that can't be read
|
|
117
|
+
console.warn(`Warning: Could not read file ${relativePath}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.warn(`Warning: Could not read directory ${dir}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const absolutePath = path.resolve(directory);
|
|
127
|
+
if (!fs.existsSync(absolutePath)) {
|
|
128
|
+
throw new Error(`Directory not found: ${directory}`);
|
|
129
|
+
}
|
|
130
|
+
walk(absolutePath, absolutePath);
|
|
131
|
+
return files;
|
|
132
|
+
}
|