milens 0.6.7 → 0.6.9
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/README.md +18 -13
- package/adapters/claude-code/.claude/mcp.json +2 -2
- package/adapters/claude-code/CLAUDE.md +1 -1
- package/adapters/gemini/.gemini/context.md +2 -2
- package/adapters/opencode/.opencode/config.json +2 -3
- package/adapters/zed/.zed/settings.json +1 -1
- package/dist/agents-md.d.ts +1 -0
- package/dist/agents-md.d.ts.map +1 -1
- package/dist/agents-md.js +50 -1
- package/dist/agents-md.js.map +1 -1
- package/dist/analyzer/engine.d.ts +2 -0
- package/dist/analyzer/engine.d.ts.map +1 -1
- package/dist/analyzer/engine.js +56 -24
- package/dist/analyzer/engine.js.map +1 -1
- package/dist/analyzer/resolver.js +5 -2
- package/dist/analyzer/resolver.js.map +1 -1
- package/dist/analyzer/review.d.ts.map +1 -1
- package/dist/analyzer/review.js +5 -1
- package/dist/analyzer/review.js.map +1 -1
- package/dist/analyzer/scope-resolver.d.ts.map +1 -1
- package/dist/analyzer/scope-resolver.js +45 -4
- package/dist/analyzer/scope-resolver.js.map +1 -1
- package/dist/cli.js +33 -15
- package/dist/cli.js.map +1 -1
- package/dist/security/rules.d.ts +2 -1
- package/dist/security/rules.d.ts.map +1 -1
- package/dist/security/rules.js +276 -61
- package/dist/security/rules.js.map +1 -1
- package/dist/server/mcp.d.ts.map +1 -1
- package/dist/server/mcp.js +84 -19
- package/dist/server/mcp.js.map +1 -1
- package/dist/server/test-plan.d.ts +1 -1
- package/dist/server/test-plan.d.ts.map +1 -1
- package/dist/server/test-plan.js +48 -4
- package/dist/server/test-plan.js.map +1 -1
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +18 -16
- package/dist/store/db.js.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/progress.d.ts +6 -18
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +40 -94
- package/dist/ui/progress.js.map +1 -1
- package/package.json +3 -2
package/dist/security/rules.js
CHANGED
|
@@ -11,8 +11,18 @@ export const OWASP_CATEGORIES = {
|
|
|
11
11
|
'A09:2021': 'Security Logging and Monitoring Failures',
|
|
12
12
|
'A10:2021': 'Server-Side Request Forgery (SSRF)',
|
|
13
13
|
};
|
|
14
|
-
const DEFAULT_EXCLUDE = '**/*.test.*,**/*.spec.*,**/node_modules
|
|
15
|
-
// ──
|
|
14
|
+
const DEFAULT_EXCLUDE = '**/*.test.*,**/*.spec.*,**/node_modules/**,**/security/rules.ts,**/metric-milens-tool*.md,**/docs/**,**/*.html';
|
|
15
|
+
// ── Language-specific fileGlobs for cross-language coverage ──
|
|
16
|
+
const JS_TS = '**/*.{js,jsx,ts,tsx,mjs,cjs}';
|
|
17
|
+
const JS_TS_PY = '**/*.{js,jsx,ts,tsx,mjs,cjs,py}';
|
|
18
|
+
const ALL_CODE = '**/*.{js,jsx,ts,tsx,mjs,cjs,py,java,go,rs,rb,php}';
|
|
19
|
+
const PY_ONLY = '**/*.py';
|
|
20
|
+
const JAVA_ONLY = '**/*.java';
|
|
21
|
+
const PHP_ONLY = '**/*.php';
|
|
22
|
+
const DOCKER_FILES = '**/{Dockerfile,docker-compose.yml,docker-compose.yaml,.dockerignore}';
|
|
23
|
+
const K8S_FILES = '**/{deployment,service,pod,ingress,statefulset,daemonset,configmap,secret,role,rolebinding,clusterrole,clusterrolebinding}*.{yaml,yml}';
|
|
24
|
+
const TF_FILES = '**/*.tf';
|
|
25
|
+
// ── 190+ built-in security rules across 25 categories ──
|
|
16
26
|
export const ALL_RULES = [
|
|
17
27
|
// ═══════════════════════════════════════════════════════════════
|
|
18
28
|
// SEC-001 – SEC-010 : Secrets (A02:2021)
|
|
@@ -26,12 +36,12 @@ export const ALL_RULES = [
|
|
|
26
36
|
description: 'Password value appears to be hardcoded in source code. Use environment variables or a secrets manager instead.',
|
|
27
37
|
patterns: [
|
|
28
38
|
/(?:password|passwd|pwd)\s*[:=]\s*['"`][^'"`\n]{4,}['"`]/i,
|
|
29
|
-
/(?:password|passwd|pwd)\s*=\s*[^'"`\s;]{4,}/i,
|
|
39
|
+
/(?:password|passwd|pwd)\s*=\s*(?!process\.env\.)[^'"`\s;]{4,}/i,
|
|
30
40
|
],
|
|
31
41
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
32
42
|
fix: 'Replace hardcoded password with process.env.DB_PASSWORD or a secrets manager.',
|
|
33
43
|
confidence: 0.92,
|
|
34
|
-
enabled: true,
|
|
44
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
35
45
|
},
|
|
36
46
|
{
|
|
37
47
|
id: 'SEC-002',
|
|
@@ -47,7 +57,7 @@ export const ALL_RULES = [
|
|
|
47
57
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
48
58
|
fix: 'Store secrets in a vault (HashiCorp Vault, AWS Secrets Manager, etc.) or environment variables.',
|
|
49
59
|
confidence: 0.90,
|
|
50
|
-
enabled: true,
|
|
60
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
51
61
|
},
|
|
52
62
|
{
|
|
53
63
|
id: 'SEC-003',
|
|
@@ -63,7 +73,7 @@ export const ALL_RULES = [
|
|
|
63
73
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
64
74
|
fix: 'Move the API key to process.env.API_KEY and never commit credentials.',
|
|
65
75
|
confidence: 0.93,
|
|
66
|
-
enabled: true,
|
|
76
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
67
77
|
},
|
|
68
78
|
{
|
|
69
79
|
id: 'SEC-004',
|
|
@@ -79,7 +89,7 @@ export const ALL_RULES = [
|
|
|
79
89
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
80
90
|
fix: 'Use IAM roles, instance profiles, or AWS Secrets Manager. If the key is leaked, deactivate it in the AWS console immediately.',
|
|
81
91
|
confidence: 0.95,
|
|
82
|
-
enabled: true,
|
|
92
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
83
93
|
},
|
|
84
94
|
{
|
|
85
95
|
id: 'SEC-005',
|
|
@@ -95,7 +105,7 @@ export const ALL_RULES = [
|
|
|
95
105
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
96
106
|
fix: 'Use environment variables (STRIPE_SECRET_KEY, OPENAI_API_KEY) and never commit sk- keys.',
|
|
97
107
|
confidence: 0.95,
|
|
98
|
-
enabled: true,
|
|
108
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
99
109
|
},
|
|
100
110
|
{
|
|
101
111
|
id: 'SEC-006',
|
|
@@ -111,7 +121,7 @@ export const ALL_RULES = [
|
|
|
111
121
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
112
122
|
fix: 'Use GITHUB_TOKEN in Actions workflows, or store PATs in repository secrets.',
|
|
113
123
|
confidence: 0.95,
|
|
114
|
-
enabled: true,
|
|
124
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
115
125
|
},
|
|
116
126
|
{
|
|
117
127
|
id: 'SEC-007',
|
|
@@ -127,7 +137,7 @@ export const ALL_RULES = [
|
|
|
127
137
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
128
138
|
fix: 'Store private keys outside the repository; use a secrets manager or PKI infrastructure.',
|
|
129
139
|
confidence: 0.95,
|
|
130
|
-
enabled: true,
|
|
140
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
131
141
|
},
|
|
132
142
|
{
|
|
133
143
|
id: 'SEC-008',
|
|
@@ -143,7 +153,7 @@ export const ALL_RULES = [
|
|
|
143
153
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
144
154
|
fix: 'Store private keys outside the repository; use a secrets manager or PKI infrastructure.',
|
|
145
155
|
confidence: 0.95,
|
|
146
|
-
enabled: true,
|
|
156
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
147
157
|
},
|
|
148
158
|
{
|
|
149
159
|
id: 'SEC-009',
|
|
@@ -159,7 +169,7 @@ export const ALL_RULES = [
|
|
|
159
169
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
160
170
|
fix: 'Use environment variables or a secure token store. Rotate the exposed token.',
|
|
161
171
|
confidence: 0.88,
|
|
162
|
-
enabled: true,
|
|
172
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
163
173
|
},
|
|
164
174
|
{
|
|
165
175
|
id: 'SEC-010',
|
|
@@ -176,7 +186,7 @@ export const ALL_RULES = [
|
|
|
176
186
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
177
187
|
fix: 'Never assign secret values to process.env in code. Use external .env files (gitignored) or a secrets manager.',
|
|
178
188
|
confidence: 0.85,
|
|
179
|
-
enabled: true,
|
|
189
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
180
190
|
},
|
|
181
191
|
// ═══════════════════════════════════════════════════════════════
|
|
182
192
|
// SEC-011 – SEC-019 : Injection (A03:2021)
|
|
@@ -196,7 +206,7 @@ export const ALL_RULES = [
|
|
|
196
206
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
197
207
|
fix: 'Replace eval() with safer alternatives: JSON.parse() for data, or design a non-eval code path.',
|
|
198
208
|
confidence: 0.92,
|
|
199
|
-
enabled: true,
|
|
209
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
200
210
|
},
|
|
201
211
|
{
|
|
202
212
|
id: 'SEC-012',
|
|
@@ -213,7 +223,7 @@ export const ALL_RULES = [
|
|
|
213
223
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
214
224
|
fix: 'Avoid exec(). Use getattr(), importlib, or restructure logic to not require dynamic execution.',
|
|
215
225
|
confidence: 0.90,
|
|
216
|
-
enabled: true,
|
|
226
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
217
227
|
},
|
|
218
228
|
{
|
|
219
229
|
id: 'SEC-013',
|
|
@@ -231,7 +241,7 @@ export const ALL_RULES = [
|
|
|
231
241
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
232
242
|
fix: 'Use child_process.execFile() or child_process.spawn() with argument arrays instead of string commands.',
|
|
233
243
|
confidence: 0.88,
|
|
234
|
-
enabled: true,
|
|
244
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
235
245
|
},
|
|
236
246
|
{
|
|
237
247
|
id: 'SEC-014',
|
|
@@ -249,7 +259,7 @@ export const ALL_RULES = [
|
|
|
249
259
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
250
260
|
fix: 'Use parameterized queries or an ORM with safe query builders (e.g., $1 placeholders, Prisma, SQLAlchemy ORM).',
|
|
251
261
|
confidence: 0.91,
|
|
252
|
-
enabled: true,
|
|
262
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
253
263
|
},
|
|
254
264
|
{
|
|
255
265
|
id: 'SEC-015',
|
|
@@ -266,7 +276,7 @@ export const ALL_RULES = [
|
|
|
266
276
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
267
277
|
fix: 'Avoid the Function constructor. Use closures, higher-order functions, or refactor logic.',
|
|
268
278
|
confidence: 0.92,
|
|
269
|
-
enabled: true,
|
|
279
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
270
280
|
},
|
|
271
281
|
{
|
|
272
282
|
id: 'SEC-016',
|
|
@@ -284,7 +294,7 @@ export const ALL_RULES = [
|
|
|
284
294
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
285
295
|
fix: 'Use textContent, createElement, or a sanitization library like DOMPurify before setting innerHTML.',
|
|
286
296
|
confidence: 0.87,
|
|
287
|
-
enabled: true,
|
|
297
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
288
298
|
},
|
|
289
299
|
{
|
|
290
300
|
id: 'SEC-017',
|
|
@@ -302,7 +312,7 @@ export const ALL_RULES = [
|
|
|
302
312
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
303
313
|
fix: 'Sanitize HTML content with DOMPurify before passing to dangerouslySetInnerHTML, or use React elements instead.',
|
|
304
314
|
confidence: 0.90,
|
|
305
|
-
enabled: true,
|
|
315
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
306
316
|
},
|
|
307
317
|
{
|
|
308
318
|
id: 'SEC-018',
|
|
@@ -320,7 +330,7 @@ export const ALL_RULES = [
|
|
|
320
330
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
321
331
|
fix: 'Use DOM manipulation APIs (createElement, appendChild) or framework-specific rendering.',
|
|
322
332
|
confidence: 0.88,
|
|
323
|
-
enabled: true,
|
|
333
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
324
334
|
},
|
|
325
335
|
{
|
|
326
336
|
id: 'SEC-019',
|
|
@@ -339,7 +349,7 @@ export const ALL_RULES = [
|
|
|
339
349
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
340
350
|
fix: 'Pass a function reference instead of a string to setTimeout/setInterval.',
|
|
341
351
|
confidence: 0.85,
|
|
342
|
-
enabled: true,
|
|
352
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
343
353
|
},
|
|
344
354
|
// ═══════════════════════════════════════════════════════════════
|
|
345
355
|
// SEC-020 – SEC-023 : Unicode (A03:2021 / various)
|
|
@@ -358,7 +368,7 @@ export const ALL_RULES = [
|
|
|
358
368
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
359
369
|
fix: 'Remove bidi override characters. Use explicit markup for bidirectional text if needed.',
|
|
360
370
|
confidence: 0.95,
|
|
361
|
-
enabled: true,
|
|
371
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
362
372
|
},
|
|
363
373
|
{
|
|
364
374
|
id: 'SEC-021',
|
|
@@ -374,7 +384,7 @@ export const ALL_RULES = [
|
|
|
374
384
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
375
385
|
fix: 'Remove invisible/zero-width characters. These are often introduced accidentally or maliciously.',
|
|
376
386
|
confidence: 0.93,
|
|
377
|
-
enabled: true,
|
|
387
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
378
388
|
},
|
|
379
389
|
{
|
|
380
390
|
id: 'SEC-022',
|
|
@@ -392,7 +402,7 @@ export const ALL_RULES = [
|
|
|
392
402
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
393
403
|
fix: 'Replace homoglyph characters with their intended ASCII/Latin equivalents. Review for malicious intent.',
|
|
394
404
|
confidence: 0.80,
|
|
395
|
-
enabled: true,
|
|
405
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
396
406
|
},
|
|
397
407
|
{
|
|
398
408
|
id: 'SEC-023',
|
|
@@ -409,7 +419,7 @@ export const ALL_RULES = [
|
|
|
409
419
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
410
420
|
fix: 'Remove hidden Unicode characters from comments during code review.',
|
|
411
421
|
confidence: 0.78,
|
|
412
|
-
enabled: true,
|
|
422
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
413
423
|
},
|
|
414
424
|
// ═══════════════════════════════════════════════════════════════
|
|
415
425
|
// SEC-024 – SEC-030 : Dangerous (A03:2021 / A08:2021)
|
|
@@ -430,7 +440,7 @@ export const ALL_RULES = [
|
|
|
430
440
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
431
441
|
fix: 'Use subprocess.run() with a list of arguments and shell=False (the default).',
|
|
432
442
|
confidence: 0.90,
|
|
433
|
-
enabled: true,
|
|
443
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
434
444
|
},
|
|
435
445
|
{
|
|
436
446
|
id: 'SEC-025',
|
|
@@ -447,7 +457,7 @@ export const ALL_RULES = [
|
|
|
447
457
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
448
458
|
fix: 'Use shell=False (default) and pass command arguments as a list. Validate all user input.',
|
|
449
459
|
confidence: 0.90,
|
|
450
|
-
enabled: true,
|
|
460
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
451
461
|
},
|
|
452
462
|
{
|
|
453
463
|
id: 'SEC-026',
|
|
@@ -464,7 +474,7 @@ export const ALL_RULES = [
|
|
|
464
474
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
465
475
|
fix: 'Use ProcessBuilder with a List<String> of arguments instead of Runtime.exec() with a single string.',
|
|
466
476
|
confidence: 0.85,
|
|
467
|
-
enabled: true,
|
|
477
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
468
478
|
},
|
|
469
479
|
{
|
|
470
480
|
id: 'SEC-027',
|
|
@@ -482,7 +492,7 @@ export const ALL_RULES = [
|
|
|
482
492
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
483
493
|
fix: 'Set shell:false (default) and pass arguments as an array. Sanitize all user-supplied input.',
|
|
484
494
|
confidence: 0.86,
|
|
485
|
-
enabled: true,
|
|
495
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
486
496
|
},
|
|
487
497
|
{
|
|
488
498
|
id: 'SEC-028',
|
|
@@ -500,7 +510,7 @@ export const ALL_RULES = [
|
|
|
500
510
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
501
511
|
fix: 'Use json.loads() or a safe serialization format. Never unpickle data from untrusted sources.',
|
|
502
512
|
confidence: 0.92,
|
|
503
|
-
enabled: true,
|
|
513
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
504
514
|
},
|
|
505
515
|
{
|
|
506
516
|
id: 'SEC-029',
|
|
@@ -517,7 +527,7 @@ export const ALL_RULES = [
|
|
|
517
527
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
518
528
|
fix: 'Use json_decode() instead of unserialize(). If unserialize() is required, restrict allowed_classes.',
|
|
519
529
|
confidence: 0.90,
|
|
520
|
-
enabled: true,
|
|
530
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
521
531
|
},
|
|
522
532
|
{
|
|
523
533
|
id: 'SEC-030',
|
|
@@ -536,7 +546,7 @@ export const ALL_RULES = [
|
|
|
536
546
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
537
547
|
fix: 'Avoid dynamic module loading. Use static imports or maintain an allowlist of safe module paths.',
|
|
538
548
|
confidence: 0.84,
|
|
539
|
-
enabled: true,
|
|
549
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
540
550
|
},
|
|
541
551
|
// ═══════════════════════════════════════════════════════════════
|
|
542
552
|
// SEC-031 – SEC-035 : Config (A05:2021 / A04:2021)
|
|
@@ -558,7 +568,7 @@ export const ALL_RULES = [
|
|
|
558
568
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
559
569
|
fix: 'Ensure middleware matcher config explicitly lists protected routes. Do not use wildcard exclusions.',
|
|
560
570
|
confidence: 0.82,
|
|
561
|
-
enabled: true,
|
|
571
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
562
572
|
},
|
|
563
573
|
{
|
|
564
574
|
id: 'SEC-032',
|
|
@@ -577,7 +587,7 @@ export const ALL_RULES = [
|
|
|
577
587
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
578
588
|
fix: 'Replace "*" with an explicit allowlist of trusted origins. Never pair "*" with credentials:true.',
|
|
579
589
|
confidence: 0.88,
|
|
580
|
-
enabled: true,
|
|
590
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
581
591
|
},
|
|
582
592
|
{
|
|
583
593
|
id: 'SEC-033',
|
|
@@ -590,13 +600,13 @@ export const ALL_RULES = [
|
|
|
590
600
|
/secure\s*:\s*false/,
|
|
591
601
|
/\bcookie\s*\(\s*[^)]*secure\s*:\s*false[^)]*\)/,
|
|
592
602
|
/Set-Cookie\s*[=:].*;\s*Secure\s*=\s*false/i,
|
|
593
|
-
/res\.cookie\s*\(
|
|
603
|
+
/res\.cookie\s*\((?!.*secure\s*:\s*true)/,
|
|
594
604
|
],
|
|
595
605
|
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py}',
|
|
596
606
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
597
607
|
fix: 'Set secure:true on all cookies in production. Use HTTPS everywhere.',
|
|
598
608
|
confidence: 0.85,
|
|
599
|
-
enabled: true,
|
|
609
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
600
610
|
},
|
|
601
611
|
{
|
|
602
612
|
id: 'SEC-034',
|
|
@@ -609,13 +619,13 @@ export const ALL_RULES = [
|
|
|
609
619
|
/httpOnly\s*:\s*false/,
|
|
610
620
|
/\bcookie\s*\(\s*[^)]*httpOnly\s*:\s*false[^)]*\)/,
|
|
611
621
|
/Set-Cookie\s*[=:].*;\s*HttpOnly\s*=\s*false/i,
|
|
612
|
-
/res\.cookie\s*\(
|
|
622
|
+
/res\.cookie\s*\((?!.*httpOnly\s*:\s*true)/,
|
|
613
623
|
],
|
|
614
624
|
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py}',
|
|
615
625
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
616
626
|
fix: 'Set httpOnly:true on all session/authentication cookies to prevent JavaScript access.',
|
|
617
627
|
confidence: 0.85,
|
|
618
|
-
enabled: true,
|
|
628
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
619
629
|
},
|
|
620
630
|
{
|
|
621
631
|
id: 'SEC-035',
|
|
@@ -635,7 +645,7 @@ export const ALL_RULES = [
|
|
|
635
645
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
636
646
|
fix: 'Disable debug mode in production. Use environment-specific config files.',
|
|
637
647
|
confidence: 0.78,
|
|
638
|
-
enabled: true,
|
|
648
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
639
649
|
},
|
|
640
650
|
// ═══════════════════════════════════════════════════════════════
|
|
641
651
|
// SEC-036 – SEC-040 : Data Leak (A09:2021)
|
|
@@ -656,7 +666,7 @@ export const ALL_RULES = [
|
|
|
656
666
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
657
667
|
fix: 'Remove or redact password values from log output. Use a logging library with automatic PII redaction.',
|
|
658
668
|
confidence: 0.82,
|
|
659
|
-
enabled: true,
|
|
669
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
660
670
|
},
|
|
661
671
|
{
|
|
662
672
|
id: 'SEC-037',
|
|
@@ -673,7 +683,7 @@ export const ALL_RULES = [
|
|
|
673
683
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
674
684
|
fix: 'Never log tokens or secrets. Mask sensitive values before logging (e.g., log only first 4 chars).',
|
|
675
685
|
confidence: 0.83,
|
|
676
|
-
enabled: true,
|
|
686
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
677
687
|
},
|
|
678
688
|
{
|
|
679
689
|
id: 'SEC-038',
|
|
@@ -691,7 +701,7 @@ export const ALL_RULES = [
|
|
|
691
701
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
692
702
|
fix: 'Store connection strings without credentials in code; inject credentials from environment variables at runtime.',
|
|
693
703
|
confidence: 0.89,
|
|
694
|
-
enabled: true,
|
|
704
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
695
705
|
},
|
|
696
706
|
{
|
|
697
707
|
id: 'SEC-039',
|
|
@@ -710,7 +720,7 @@ export const ALL_RULES = [
|
|
|
710
720
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
711
721
|
fix: 'Use HTTPS URLs from environment variables. Never hardcode internal IPs.',
|
|
712
722
|
confidence: 0.75,
|
|
713
|
-
enabled: true,
|
|
723
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
714
724
|
},
|
|
715
725
|
{
|
|
716
726
|
id: 'SEC-040',
|
|
@@ -720,15 +730,15 @@ export const ALL_RULES = [
|
|
|
720
730
|
name: 'General console.log left in production code',
|
|
721
731
|
description: 'Unconditional console.log statements may leak internal state to the browser console in production.',
|
|
722
732
|
patterns: [
|
|
723
|
-
/^\s*console\.log\s*\(/m,
|
|
724
|
-
/^\s*console\.warn\s*\(/m,
|
|
725
|
-
/^\s*console\.debug\s*\(/m,
|
|
733
|
+
/^\s*console\.log\s*\([^)]*(?:\$\{|req\.|res\.|err|error|user|token|secret)/m,
|
|
734
|
+
/^\s*console\.warn\s*\([^)]*(?:\$\{|req\.|res\.|err|error)/m,
|
|
735
|
+
/^\s*console\.debug\s*\([^)]*(?:\$\{|req\.|res\.|token|secret)/m,
|
|
726
736
|
],
|
|
727
737
|
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
728
738
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
729
739
|
fix: 'Replace console.log with a proper logging framework that supports log levels and can be silenced in production.',
|
|
730
740
|
confidence: 0.70,
|
|
731
|
-
enabled: true,
|
|
741
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
732
742
|
},
|
|
733
743
|
// ═══════════════════════════════════════════════════════════════
|
|
734
744
|
// SEC-041 – SEC-044 : Crypto (A02:2021)
|
|
@@ -752,7 +762,7 @@ export const ALL_RULES = [
|
|
|
752
762
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
753
763
|
fix: 'Replace MD5 with SHA-256 or SHA-3 for security-sensitive hashing. MD5 is only acceptable for non-cryptographic checksums.',
|
|
754
764
|
confidence: 0.93,
|
|
755
|
-
enabled: true,
|
|
765
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
756
766
|
},
|
|
757
767
|
{
|
|
758
768
|
id: 'SEC-042',
|
|
@@ -772,7 +782,7 @@ export const ALL_RULES = [
|
|
|
772
782
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
773
783
|
fix: 'Use SHA-256 or SHA-3 instead of SHA-1 for any security-sensitive operation.',
|
|
774
784
|
confidence: 0.90,
|
|
775
|
-
enabled: true,
|
|
785
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
776
786
|
},
|
|
777
787
|
{
|
|
778
788
|
id: 'SEC-043',
|
|
@@ -789,7 +799,7 @@ export const ALL_RULES = [
|
|
|
789
799
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
790
800
|
fix: 'Use crypto.randomBytes(), crypto.randomUUID(), or crypto.getRandomValues() for cryptographically secure randomness.',
|
|
791
801
|
confidence: 0.85,
|
|
792
|
-
enabled: true,
|
|
802
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
793
803
|
},
|
|
794
804
|
{
|
|
795
805
|
id: 'SEC-044',
|
|
@@ -808,7 +818,7 @@ export const ALL_RULES = [
|
|
|
808
818
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
809
819
|
fix: 'Generate a unique, cryptographically random salt/IV per operation using crypto.randomBytes(). Never hardcode.',
|
|
810
820
|
confidence: 0.86,
|
|
811
|
-
enabled: true,
|
|
821
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
812
822
|
},
|
|
813
823
|
// ═══════════════════════════════════════════════════════════════
|
|
814
824
|
// SEC-045 – SEC-048 : Auth (A01:2021 / A07:2021)
|
|
@@ -828,7 +838,7 @@ export const ALL_RULES = [
|
|
|
828
838
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
829
839
|
fix: 'Use a role hierarchy checker or a dedicated authorization library (e.g., CASL, Oso). Validate RBAC claims from a trusted source.',
|
|
830
840
|
confidence: 0.80,
|
|
831
|
-
enabled: true,
|
|
841
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
832
842
|
},
|
|
833
843
|
{
|
|
834
844
|
id: 'SEC-046',
|
|
@@ -845,7 +855,7 @@ export const ALL_RULES = [
|
|
|
845
855
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
846
856
|
fix: 'Apply authentication middleware to all protected routes. Use a route grouping pattern to apply middleware consistently.',
|
|
847
857
|
confidence: 0.78,
|
|
848
|
-
enabled: true,
|
|
858
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
849
859
|
},
|
|
850
860
|
{
|
|
851
861
|
id: 'SEC-047',
|
|
@@ -855,15 +865,14 @@ export const ALL_RULES = [
|
|
|
855
865
|
name: 'JWT created without expiration',
|
|
856
866
|
description: 'JWTs without an exp claim are valid indefinitely, increasing the blast radius if a token is leaked.',
|
|
857
867
|
patterns: [
|
|
858
|
-
/jwt\.sign\s*\(\s*
|
|
859
|
-
/
|
|
860
|
-
/sign\s*\(\s*payload[^)]*\)(?!.*expiresIn)/i,
|
|
868
|
+
/jwt\.sign\s*\(\s*(?!.*\b(?:expiresIn|expires|exp)\b)[^)]*\)/i,
|
|
869
|
+
/sign\s*\(\s*payload(?!.*\b(?:expiresIn|expires|exp)\b)[^)]*\)/i,
|
|
861
870
|
],
|
|
862
871
|
fileGlob: '**/*.{js,jsx,ts,tsx,py}',
|
|
863
872
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
864
873
|
fix: 'Always set an expiresIn or exp claim when signing JWTs. Use short-lived tokens with refresh token rotation.',
|
|
865
874
|
confidence: 0.82,
|
|
866
|
-
enabled: true,
|
|
875
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
867
876
|
},
|
|
868
877
|
{
|
|
869
878
|
id: 'SEC-048',
|
|
@@ -884,7 +893,7 @@ export const ALL_RULES = [
|
|
|
884
893
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
885
894
|
fix: 'Use HttpOnly, Secure cookies for session tokens. Never pass session IDs in URL query parameters.',
|
|
886
895
|
confidence: 0.86,
|
|
887
|
-
enabled: true,
|
|
896
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
888
897
|
},
|
|
889
898
|
// ═══════════════════════════════════════════════════════════════
|
|
890
899
|
// SEC-049 – SEC-050 : File Access (A01:2021)
|
|
@@ -906,7 +915,7 @@ export const ALL_RULES = [
|
|
|
906
915
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
907
916
|
fix: 'Validate and sanitize user input. Use path.resolve() and check that the resolved path is within the allowed directory.',
|
|
908
917
|
confidence: 0.88,
|
|
909
|
-
enabled: true,
|
|
918
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
910
919
|
},
|
|
911
920
|
{
|
|
912
921
|
id: 'SEC-050',
|
|
@@ -924,8 +933,214 @@ export const ALL_RULES = [
|
|
|
924
933
|
excludeGlob: DEFAULT_EXCLUDE,
|
|
925
934
|
fix: 'Sanitize file paths. Use an allowlist of permitted paths. Validate that resolved paths stay within an allowed root directory.',
|
|
926
935
|
confidence: 0.90,
|
|
927
|
-
enabled: true,
|
|
936
|
+
falsePositiveRisk: 'low', enabled: true,
|
|
928
937
|
},
|
|
938
|
+
{ id: 'SEC-051', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Hardcoded JWT secret', description: 'JWT signing secret hardcoded in source code.', patterns: [/\b(?:jwtSecret|JWT_SECRET|jwt_secret)\s*[:=]\s*['"`][A-Za-z0-9_\-]{16,}['"`]/, /secret\s*[:=]\s*['"`][A-Za-z0-9_\-]{16,}['"`].*jwt/i], excludeGlob: DEFAULT_EXCLUDE, fix: 'Use process.env.JWT_SECRET from environment variables.', confidence: 0.95, falsePositiveRisk: 'medium', enabled: true },
|
|
939
|
+
{ id: 'SEC-052', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Hardcoded encryption key', description: 'Encryption key hardcoded in source code.', patterns: [/\b(?:encrypt(?:ion)?Key|ENCRYPT(?:ION)?_KEY|secretKey|SECRET_KEY)\s*[:=]\s*['"`][A-Za-z0-9_\-]{16,}['"`]/i], fix: 'Use process.env.ENCRYPTION_KEY or a key management service.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
940
|
+
{ id: 'SEC-053', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Hardcoded AWS Secret Access Key', description: 'AWS secret access key exposed.', patterns: [/aws_secret_access_key\s*[:=]\s*['"`][A-Za-z0-9+\/=]{40}['"`]/i, /secretAccessKey\s*[:=]\s*['"`][A-Za-z0-9+\/=]{40}['"`]/], fix: 'Use AWS IAM roles or environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
941
|
+
{ id: 'SEC-054', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Hardcoded AWS Session Token', description: 'AWS session token exposed.', patterns: [/aws_session_token\s*[:=]\s*['"`][A-Za-z0-9+\/=]{100,}['"`]/i], fix: 'Use temporary credentials via IAM roles.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
942
|
+
{ id: 'SEC-055', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'GCP Service Account Key', description: 'Google Cloud service account JSON key exposed.', patterns: [/"type"\s*:\s*"service_account"/, /"private_key_id"\s*:\s*"[a-f0-9]+"/], fileGlob: '**/*.{json,js,ts}', fix: 'Use GCP IAM roles or Workload Identity.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
943
|
+
{ id: 'SEC-056', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Azure Storage Key', description: 'Azure storage account key exposed.', patterns: [/AccountKey\s*=\s*[A-Za-z0-9+\/=]{88}/, /DefaultEndpointsProtocol.*AccountKey/], fix: 'Use Azure Managed Identity.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
944
|
+
{ id: 'SEC-057', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Slack Token', description: 'Slack bot or user token exposed.', patterns: [/xox[baprs]\-[0-9A-Za-z\-]{10,}/], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
945
|
+
{ id: 'SEC-058', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Discord Token', description: 'Discord bot token exposed.', patterns: [/[MNO][A-Za-z\d\-_]{23,25}\.[A-Za-z\d\-_]{6}\.[A-Za-z\d\-_]{27,38}/], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
946
|
+
{ id: 'SEC-059', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Twilio Token', description: 'Twilio auth token or API key exposed.', patterns: [/SK[0-9a-fA-F]{32}/, /twilio.*token\s*[:=]\s*['"`][A-Za-z0-9]{32,}['"`]/i], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
947
|
+
{ id: 'SEC-060', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'SendGrid API Key', description: 'SendGrid API key exposed.', patterns: [/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/, /sendgrid.*(?:api[_-]?key|key)\s*[:=]\s*['"`][A-Za-z0-9_\-]{20,}['"`]/i], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
948
|
+
{ id: 'SEC-061', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Firebase Admin Key', description: 'Firebase service account key or config exposed.', patterns: [/"project_id"\s*:\s*"[^"]+".*"private_key"/, /firebase.*config.*apiKey/i], fileGlob: '**/*.{json,js,ts}', fix: 'Use Firebase Admin SDK with environment-based credentials.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
949
|
+
{ id: 'SEC-062', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'DSA Private Key', description: 'DSA private key exposed.', patterns: [/-----BEGIN DSA PRIVATE KEY-----/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Store outside the repository.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
950
|
+
{ id: 'SEC-063', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'PGP Private Key', description: 'PGP/GPG private key exposed.', patterns: [/-----BEGIN PGP PRIVATE KEY BLOCK-----/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Store outside the repository.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
951
|
+
{ id: 'SEC-064', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'SSH Private Key', description: 'SSH private key exposed.', patterns: [/-----BEGIN (?:RSA|DSA|EC|OPENSSH) PRIVATE KEY-----/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Store outside the repository.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
952
|
+
{ id: 'SEC-065', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Generic Refresh Token', description: 'OAuth refresh token hardcoded.', patterns: [/refresh[_-]?token\s*[:=]\s*['"`][A-Za-z0-9_\-.]{16,}['"`]/i], fix: 'Store refresh tokens in secure storage.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
953
|
+
{ id: 'SEC-066', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Credential in Dockerfile', description: 'Credentials hardcoded in Dockerfile.', patterns: [/ENV\s+(?:PASSWORD|SECRET|TOKEN|KEY)\s*=\s*\S+/i, /ARG\s+(?:PASSWORD|SECRET|TOKEN|KEY)\s*=\s*\S+/i], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use Docker secrets or build args without default values.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
954
|
+
{ id: 'SEC-067', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Credential in Terraform', description: 'Secrets hardcoded in Terraform files.', patterns: [/(?:password|secret|token|key)\s*=\s*"[^"]{4,}"/i], fileGlob: TF_FILES, fix: 'Use Terraform variables with sensitive=true.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
955
|
+
{ id: 'SEC-068', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Credential in Kubernetes Manifest', description: 'Secrets hardcoded in K8s manifests.', patterns: [/(?:password|secret|token)\s*:\s*[A-Za-z0-9_\-]{8,}/], fileGlob: K8S_FILES, fix: 'Use Kubernetes Secrets and reference via secretKeyRef.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
956
|
+
{ id: 'SEC-069', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'GitLab Token', description: 'GitLab PAT exposed.', patterns: [/glpat\-[A-Za-z0-9_\-]{20,}/, /gitlab.*token\s*[:=]\s*['"`][A-Za-z0-9_\-]{20,}['"`]/i], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
957
|
+
{ id: 'SEC-070', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Hardcoded OAuth client secret', description: 'OAuth client secret hardcoded.', patterns: [/client[_-]?secret\s*[:=]\s*['"`][A-Za-z0-9_\-]{16,}['"`]/i, /CLIENT_SECRET\s*=\s*['"`][A-Za-z0-9_\-]{16,}['"`]/], fix: 'Use environment variables or OAuth secret management.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
958
|
+
// ═══════════════════════════════════════════════════════════════
|
|
959
|
+
// SEC-071 – SEC-087 : SQLi/NoSQLi/Template/Log Injection (A03:2021)
|
|
960
|
+
// ═══════════════════════════════════════════════════════════════
|
|
961
|
+
{ id: 'SEC-071', category: 'injection', owasp: 'A03:2021', severity: 'CRITICAL', name: 'NoSQL Injection', description: 'Unsanitized user input in NoSQL query.', patterns: [/\{\s*\$where\s*:\s*req\./, /\{\s*\$regex\s*:\s*req\./, /\.find\(\s*\{\s*[^}]*req\./, /\{\s*(?:\$(?:where|gt|lt|ne|in|nin|regex|eq|expr))\s*:/], fileGlob: JS_TS, fix: 'Sanitize all user input used in NoSQL queries.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
962
|
+
{ id: 'SEC-072', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'LDAP Injection', description: 'Unsanitized input in LDAP query filters.', patterns: [/\(.*=\s*(?:req\.|request\.|params\.|query\.|body\.)/, /ldap.*search.*filter.*req\./i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Escape LDAP filter special characters.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
963
|
+
{ id: 'SEC-073', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'XPath Injection', description: 'Unsanitized input in XPath expressions.', patterns: [/\.evaluate\s*\(\s*[`'"].*req\./, /xpath.*compile.*req\./i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use parameterized XPath queries.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
964
|
+
{ id: 'SEC-074', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'CRLF Injection', description: 'CRLF injection in HTTP response headers.', patterns: [/setHeader\s*\(\s*[`'"][^`'"]*\\r\\n/, /header\s*\(\s*[`'"][^`'"]*\\r\\n/, /response\.write.*%0d%0a/i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Strip CR/LF characters from user input before inserting into headers.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
965
|
+
{ id: 'SEC-075', category: 'injection', owasp: 'A03:2021', severity: 'CRITICAL', name: 'Server-Side Template Injection', description: 'User input rendered in template without sanitization.', patterns: [/render_template_string\s*\(.*req\./i, /\.render\s*\(\s*\{.*req\./, /\{\{.*req\.(?:params|query|body)/, /\$\{.*req\.(?:params|query|body)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Never pass raw user input to template render functions.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
966
|
+
{ id: 'SEC-076', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'EL/OGNL Injection', description: 'Expression Language injection in Java.', patterns: [/\$\{[^}]*request\.getParameter/, /#\{[^}]*request\.getParameter/], fileGlob: JAVA_ONLY, fix: 'Do not evaluate user input as EL/OGNL.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
967
|
+
{ id: 'SEC-077', category: 'injection', owasp: 'A03:2021', severity: 'MEDIUM', name: 'CSV Formula Injection', description: 'CSV export vulnerable to formula injection.', patterns: [/\.write\s*\(\s*[`'"].*=[`'"]/, /csv.*write.*=/i, /to_csv.*=/i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Prefix cells starting with =, +, -, @ with a single quote.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
968
|
+
{ id: 'SEC-078', category: 'injection', owasp: 'A03:2021', severity: 'MEDIUM', name: 'Log Injection', description: 'User input written to logs without sanitization.', patterns: [/logger\s*\.\s*(?:info|error|warn|debug|log)\s*\(\s*[^)]*req\./, /console\.log\s*\(\s*[^)]*req\.(?:params|query|body)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Sanitize user input before logging. Strip newlines.', confidence: 0.75, falsePositiveRisk: 'low', enabled: true },
|
|
969
|
+
{ id: 'SEC-079', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'Mongo Operator Injection', description: 'User-controlled $ operators in MongoDB.', patterns: [/\$\s*(?:where|regex|gt|lt|ne|in|nin|expr)\s*:\s*(?:req\.|request\.|params\.|query\.|body\.)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Whitelist allowed operators and sanitize values.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
970
|
+
{ id: 'SEC-080', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'GraphQL Injection', description: 'Raw GraphQL query built with user input.', patterns: [/graphql\s*\(\s*[`'].*req\./, /gql\s*\(\s*[`'].*req\./, /execute\s*\(\s*[`'].*req\./i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use parameterized queries and input validation.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
971
|
+
{ id: 'SEC-081', category: 'injection', owasp: 'A03:2021', severity: 'MEDIUM', name: 'Host Header Injection', description: 'Request host header used unsafely.', patterns: [/req\.(?:headers|get)\(?['"]host['"]/, /request\.getHeader\s*\(\s*['"]Host['"]/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate host header against a trusted domain list.', confidence: 0.75, falsePositiveRisk: 'low', enabled: true },
|
|
972
|
+
{ id: 'SEC-082', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'Email Header Injection', description: 'User input in email headers.', patterns: [/mail\s*\(\s*\{[^}]*subject\s*:\s*(?:req\.|request\.|params\.)/, /sendmail.*(?:req\.|request\.|params\.)/i, /transport\.sendMail.*req\./i], fileGlob: JS_TS_PY, fix: 'Sanitize user input before inserting into email headers.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
973
|
+
{ id: 'SEC-083', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'HTTP Header Injection', description: 'User input inserted into HTTP response headers.', patterns: [/res\.setHeader\s*\(\s*(?:req\.|request\.|params\.)/, /response\.headers\.set\s*\(\s*[`'][^`']*\$\{/, /header\s*\(\s*[`'][^`']*\$\{.*req\./], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Strip newline characters from user input before setting headers.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
974
|
+
{ id: 'SEC-084', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'XML Injection', description: 'User input inserted into XML without encoding.', patterns: [/\.innerHTML\s*=\s*['"`]<\?xml/, /xml\s*\+\s*(?:req\.|request\.|params\.)/, /xmldom.*parse.*req\./i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Encode special XML characters before insertion.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
975
|
+
{ id: 'SEC-085', category: 'injection', owasp: 'A03:2021', severity: 'MEDIUM', name: 'Argument Injection', description: 'Command arguments built from user input.', patterns: [/\.spawn\s*\(\s*['"][^'"]+['"]\s*,\s*\[.*(?:req\.|request\.|params\.)/, /execFile\s*\(\s*['"][^'"]+['"]\s*,\s*\[.*(?:req\.|request\.|params\.)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate and sanitize arguments. Do not use user input in command arguments.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
976
|
+
{ id: 'SEC-086', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'Shell Injection', description: 'Shell command with user-supplied input.', patterns: [/exec\s*\(\s*[`'].*\$\{.*req\./, /exec\s*\(\s*['"][^'"]*['"]\s*\+\s*(?:req\.|request\.|params\.)/, /`[^`]*\$[({]\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use execFile instead of exec. Avoid shell interpretation.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
977
|
+
{ id: 'SEC-087', category: 'injection', owasp: 'A03:2021', severity: 'HIGH', name: 'Dynamic Code Generation', description: 'User input used to generate executable code.', patterns: [/new Function\s*\(\s*(?:req\.|request\.|params\.)/, /compile\s*\(\s*(?:req\.|request\.|params\.)/, /vm\.runInNewContext.*req\./], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Never generate executable code from user input.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
978
|
+
// ═══════════════════════════════════════════════════════════════
|
|
979
|
+
// SEC-088 – SEC-095 : RCE (A03:2021)
|
|
980
|
+
// ═══════════════════════════════════════════════════════════════
|
|
981
|
+
{ id: 'SEC-088', category: 'rce', owasp: 'A03:2021', severity: 'CRITICAL', name: 'execSync() dynamic input', description: 'execSync with user-controlled input.', patterns: [/execSync\s*\(\s*[^)]*(?:req\.|request\.|params\.|query\.|body\.)/, /execSync\s*\(\s*`[^`]*\$\{[^}]*\}[^`]*`/], fileGlob: JS_TS, fix: 'Avoid execSync with user input.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
982
|
+
{ id: 'SEC-089', category: 'rce', owasp: 'A03:2021', severity: 'CRITICAL', name: 'PowerShell Invoke-Expression', description: 'Invoke-Expression with dynamic input.', patterns: [/Invoke-Expression\s+\$/, /iex\s+\$/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Never use Invoke-Expression with variable input.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
983
|
+
{ id: 'SEC-090', category: 'rce', owasp: 'A03:2021', severity: 'HIGH', name: 'Dynamic Class Loading', description: 'Dynamic class loading with user-controlled name.', patterns: [/Class\.forName\s*\(\s*req\./, /classLoader\.loadClass\s*\(\s*req\./i, /importlib\.import_module\s*\(\s*req\./i, /__import__\s*\(\s*req\./i], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use an allowlist for dynamically loaded classes.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
984
|
+
{ id: 'SEC-091', category: 'rce', owasp: 'A03:2021', severity: 'HIGH', name: 'Reflection Execute', description: 'Reflection-based invocation with user input.', patterns: [/\.getMethod\s*\(\s*req\./, /\.invoke\s*\([^)]*req\./, /method\.call\s*\([^)]*req\./], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use an allowlist of permitted methods.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
985
|
+
{ id: 'SEC-092', category: 'rce', owasp: 'A03:2021', severity: 'HIGH', name: 'Unsafe Script Engine', description: 'Script engine evaluating user input.', patterns: [/scriptEngine\.eval\s*\(\s*req\./i, /ScriptEngine.*eval.*request/i], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Never pass user input to script engine eval.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
986
|
+
{ id: 'SEC-093', category: 'rce', owasp: 'A03:2021', severity: 'HIGH', name: 'Bash Command Substitution', description: 'Shell command substitution with user input.', patterns: [/`\s*\$[({]\s*(?:req\.|request\.|params\.|query\.)/, /\$\(\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use execFile instead of shell commands with substitution.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
987
|
+
// ═══════════════════════════════════════════════════════════════
|
|
988
|
+
// SEC-094 – SEC-100 : XSS (A03:2021)
|
|
989
|
+
// ═══════════════════════════════════════════════════════════════
|
|
990
|
+
{ id: 'SEC-094', category: 'xss', owasp: 'A03:2021', severity: 'HIGH', name: 'outerHTML dynamic assignment', description: 'outerHTML set with dynamic content.', patterns: [/\.outerHTML\s*=\s*(?:req\.|request\.|params\.|query\.)/, /\.outerHTML\s*=\s*[`'].*\$\{/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use textContent or DOM manipulation instead.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
991
|
+
{ id: 'SEC-095', category: 'xss', owasp: 'A03:2021', severity: 'HIGH', name: 'insertAdjacentHTML dynamic', description: 'insertAdjacentHTML called with user input.', patterns: [/\.insertAdjacentHTML\s*\(\s*[^,]*,\s*(?:req\.|request\.|params\.)/, /\.insertAdjacentHTML\s*\(\s*[^,]*,\s*`[^`]*\$/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use insertAdjacentElement or textContent instead.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
992
|
+
{ id: 'SEC-096', category: 'xss', owasp: 'A03:2021', severity: 'HIGH', name: 'jQuery html() dynamic', description: 'jQuery .html() called with user input.', patterns: [/\$\([^)]*\)\s*\.\s*html\s*\(\s*(?:req\.|request\.|params\.)/, /\.html\s*\(\s*[`'].*\$\{[^}]*req\./], fileGlob: JS_TS, fix: 'Use .text() instead of .html() for user content.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
993
|
+
{ id: 'SEC-097', category: 'xss', owasp: 'A03:2021', severity: 'HIGH', name: 'DOM XSS Sink', description: 'User input flowing into DOM XSS sink.', patterns: [/\.(?:innerHTML|outerHTML)\s*=\s*(?:location|document\.)/, /eval\s*\(\s*(?:location\.hash|location\.search)/, /document\.write\s*\(\s*(?:location\.hash|location\.search)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Sanitize all user input before DOM insertion.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
994
|
+
{ id: 'SEC-098', category: 'xss', owasp: 'A03:2021', severity: 'MEDIUM', name: 'SVG Script Injection', description: 'Inline SVG with script tag.', patterns: [/<svg[^>]*>.*<script/i, /<svg[^>]*onload\s*=/i], fileGlob: '**/*.{jsx,tsx}', fix: 'Sanitize SVG content before rendering.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
995
|
+
{ id: 'SEC-099', category: 'xss', owasp: 'A03:2021', severity: 'HIGH', name: 'Inline Event Handler Injection', description: 'Inline event handlers with user data.', patterns: [/on(?:click|load|error|mouseover|focus|blur|submit)\s*=\s*[`'].*\$\{/, /on\w+\s*=\s*\{.*req\./], fileGlob: '**/*.{jsx,tsx}', fix: 'Use addEventListener instead of inline handlers.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
996
|
+
{ id: 'SEC-100', category: 'xss', owasp: 'A03:2021', severity: 'MEDIUM', name: 'Unsafe Markdown Rendering', description: 'Markdown rendered without sanitization.', patterns: [/dangerouslySetInnerHTML.*marked/, /\.innerHTML\s*=.*markdown/i, /\.innerHTML\s*=.*md\s*\(/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use DOMPurify to sanitize rendered HTML.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
997
|
+
// ═══════════════════════════════════════════════════════════════
|
|
998
|
+
// SEC-101 – SEC-110 : Deserialization (A08:2021)
|
|
999
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1000
|
+
{ id: 'SEC-101', category: 'deserialization', owasp: 'A08:2021', severity: 'CRITICAL', name: 'marshal.loads() untrusted', description: 'Python marshal.loads on untrusted data.', patterns: [/marshal\.loads?\s*\(/, /marshal\.load\s*\(/], fileGlob: PY_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use json.loads instead.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1001
|
+
{ id: 'SEC-102', category: 'deserialization', owasp: 'A08:2021', severity: 'CRITICAL', name: 'yaml.load() unsafe', description: 'PyYAML yaml.load without SafeLoader.', patterns: [/yaml\.load\s*\(/, /yaml\.load\s*\([^,)]*\)/, /yaml\.load_all\s*\(/], fileGlob: PY_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use yaml.safe_load() instead.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1002
|
+
{ id: 'SEC-103', category: 'deserialization', owasp: 'A08:2021', severity: 'CRITICAL', name: 'Java readObject() untrusted', description: 'ObjectInputStream.readObject on untrusted data.', patterns: [/ObjectInputStream.*readObject/, /\.readObject\s*\(\s*\)/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate serialized objects. Use a whitelist.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1003
|
+
{ id: 'SEC-104', category: 'deserialization', owasp: 'A08:2021', severity: 'CRITICAL', name: 'BinaryFormatter Deserialize', description: 'BinaryFormatter.Deserialize.', patterns: [/BinaryFormatter.*Deserialize/, /IFormatter.*Deserialize/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Use a safer serializer or whitelist allowed types.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1004
|
+
{ id: 'SEC-105', category: 'deserialization', owasp: 'A08:2021', severity: 'HIGH', name: 'Jackson Default Typing', description: 'Jackson ObjectMapper with default typing.', patterns: [/enableDefaultTyping\s*\(/, /ObjectMapper.*enableDefaultTyping/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable default typing or use a strict type whitelist.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1005
|
+
{ id: 'SEC-106', category: 'deserialization', owasp: 'A08:2021', severity: 'HIGH', name: 'Fastjson AutoType', description: 'Fastjson with autoType enabled.', patterns: [/JSON\.parse.*autoType/, /ParserConfig.*AutoTypeSupport/, /@type\s*:/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable autoType in Fastjson.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1006
|
+
{ id: 'SEC-107', category: 'deserialization', owasp: 'A08:2021', severity: 'HIGH', name: 'PHP unserialize() untrusted', description: 'unserialize on untrusted data.', patterns: [/unserialize\s*\(\s*(?:\$_(?:GET|POST|REQUEST|COOKIE)|req\.|request\.)/i, /unserialize\s*\(\s*[^)]*base64_decode/], fileGlob: PHP_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use json_decode instead. Never unserialize user input.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1007
|
+
{ id: 'SEC-108', category: 'deserialization', owasp: 'A08:2021', severity: 'HIGH', name: 'Kryo Unsafe Deserialize', description: 'Kryo deserialization without registration.', patterns: [/new Kryo\s*\(/, /kryo\.readClassAndObject/, /kryo\.readObject/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Enable registration required.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1008
|
+
{ id: 'SEC-109', category: 'deserialization', owasp: 'A08:2021', severity: 'HIGH', name: 'XMLDecoder Deserialization', description: 'XMLDecoder used for deserialization.', patterns: [/XMLDecoder/, /xmlDecoder\.readObject/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Avoid XMLDecoder. Use a safer data format.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1009
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1010
|
+
// SEC-110 – SEC-115 : SSRF (A10:2021)
|
|
1011
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1012
|
+
{ id: 'SEC-110', category: 'ssrf', owasp: 'A10:2021', severity: 'CRITICAL', name: 'User Controlled URL Fetch', description: 'User-supplied URL in HTTP request.', patterns: [/fetch\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, /axios\s*\(\s*\{[^}]*url\s*:\s*(?:req\.|request\.)/, /requests\.(?:get|post|put)\s*\(\s*(?:req\.|request\.|params\.)/i, /http\.(?:get|request)\s*\(\s*(?:req\.|request\.|params\.)/, /got\s*\(\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, fix: 'Validate user-supplied URLs against a whitelist of allowed domains.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1013
|
+
{ id: 'SEC-111', category: 'ssrf', owasp: 'A10:2021', severity: 'HIGH', name: 'SSRF via File URL', description: 'file:// URL used with user input.', patterns: [/['"`]file:\/\/['"`]\s*\+/, /url\.startsWith\s*\(\s*['"`]file:/, /['"`]file:\/\/['"`]\s*\+\s*(?:req\.|request\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Block file:// URLs from user input.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1014
|
+
{ id: 'SEC-112', category: 'ssrf', owasp: 'A10:2021', severity: 'MEDIUM', name: 'SSRF via Gopher Protocol', description: 'gopher:// URL with user input.', patterns: [/['"`]gopher:\/\/['"`]/, /url\.includes\s*\(\s*['"`]gopher:/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Block gopher:// URLs from user input.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1015
|
+
{ id: 'SEC-113', category: 'ssrf', owasp: 'A10:2021', severity: 'HIGH', name: 'SSRF Cloud Metadata', description: 'Requests to cloud metadata endpoints.', patterns: [/169\.254\.169\.254/, /metadata\.google\.internal/, /instance-data/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Block requests to internal IPs and metadata endpoints.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1016
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1017
|
+
// SEC-114 – SEC-116 : XXE (A05:2021)
|
|
1018
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1019
|
+
{ id: 'SEC-114', category: 'xxe', owasp: 'A05:2021', severity: 'CRITICAL', name: 'XML External Entity', description: 'XML parser with external entities enabled.', patterns: [/DocumentBuilderFactory.*setExpandEntityReferences\s*\(\s*true/, /SAXParserFactory.*external-general-entities.*true/, /XMLInputFactory.*IS_SUPPORTING_EXTERNAL_ENTITIES.*true/], fileGlob: JAVA_ONLY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable external entities.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1020
|
+
{ id: 'SEC-115', category: 'xxe', owasp: 'A05:2021', severity: 'HIGH', name: 'DTD Processing Enabled', description: 'XML DTD processing enabled.', patterns: [/DOCTYPE/, /<!ENTITY/, /xml\.etree\.ElementTree.*DOCTYPE/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable DTD processing in XML parsers.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1021
|
+
{ id: 'SEC-116', category: 'xxe', owasp: 'A05:2021', severity: 'HIGH', name: 'External Schema Loading', description: 'XML schema loaded from external URL.', patterns: [/schemaLocation\s*=\s*['"`]http/, /xsi:schemaLocation\s*=\s*['"`]http/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable loading of external schemas.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1022
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1023
|
+
// SEC-117 – SEC-122 : Path Traversal & File Operations (A01:2021)
|
|
1024
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1025
|
+
{ id: 'SEC-117', category: 'path-traversal', owasp: 'A01:2021', severity: 'HIGH', name: 'Absolute Path Injection', description: 'User input used as absolute file path.', patterns: [/\/\s*\+\s*(?:req\.|request\.|params\.|query\.|body\.)/, /path\.join\s*\(\s*['"`]\/['"`]\s*,\s*(?:req\.|request\.|params\.)/, /os\.path\.join\s*\(\s*['"`]\/['"`]\s*,\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Restrict file access to an allowed directory.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1026
|
+
{ id: 'SEC-118', category: 'path-traversal', owasp: 'A01:2021', severity: 'HIGH', name: 'Zip Slip', description: 'Zip extraction with path traversal.', patterns: [/\.extract\s*\(\s*(?:req\.|request\.)/, /unzip.*\.\./i, /extractall\s*\(/, /tar\.extract/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate each entry path before extraction.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1027
|
+
{ id: 'SEC-119', category: 'file-access', owasp: 'A01:2021', severity: 'HIGH', name: 'fs.writeFile user path', description: 'fs.writeFile with user-controlled path.', patterns: [/fs\.writeFile(?:Sync)?\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, /fs\.writeFile(?:Sync)?\s*\(\s*`[^`]*\$\{/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Restrict write paths to an allowed directory.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1028
|
+
{ id: 'SEC-120', category: 'file-upload', owasp: 'A01:2021', severity: 'CRITICAL', name: 'Unrestricted File Upload', description: 'File upload without type/size validation.', patterns: [/\.upload\s*\(/, /multer\s*\(\s*\{[^}]*\}\s*\)/, /req\.file\s*\./, /request\.files\s*\./], fileGlob: JS_TS_PY, fix: 'Validate file type, size, and scan for malware.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1029
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1030
|
+
// SEC-121 – SEC-128 : Authentication & Authorization (A07:2021 + A01:2021)
|
|
1031
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1032
|
+
{ id: 'SEC-121', category: 'auth', owasp: 'A07:2021', severity: 'CRITICAL', name: 'Missing Authentication', description: 'Route handler without authentication check.', patterns: [/router\.(?:get|post|put|delete|patch)\s*\(\s*['"`]\/(?:admin|api|dashboard)['"`]\s*,\s*(?!\s*(?:auth|middleware|guard|authenticate|isAuth|requireAuth|withAuth)\b)/i, /@(?:Get|Post|Put|Delete)\s*\(\s*['"].*(?:admin|api)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Add authentication middleware.', confidence: 0.65, falsePositiveRisk: 'high', enabled: true },
|
|
1033
|
+
{ id: 'SEC-122', category: 'auth', owasp: 'A07:2021', severity: 'HIGH', name: 'Default Credentials', description: 'Default username/password in code.', patterns: [/['"`]admin['"`]\s*,\s*['"`]admin['"`]/, /['"`]root['"`]\s*,\s*['"`]root['"`]/, /['"`]admin['"`]\s*,\s*['"`]password['"`]/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Replace default credentials. Use environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1034
|
+
{ id: 'SEC-123', category: 'auth', owasp: 'A07:2021', severity: 'HIGH', name: 'Session Fixation', description: 'Session ID not regenerated after login.', patterns: [/session\.regenerate/, /req\.session\.regenerate/, /login.*session/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Regenerate session ID after authentication.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1035
|
+
{ id: 'SEC-124', category: 'auth', owasp: 'A01:2021', severity: 'HIGH', name: 'IDOR Vulnerability', description: 'User ID from request used in data access.', patterns: [/\.findById\s*\(\s*req\.params\.(?!.*(?:userId|ownerId|user_id)\s*[:=])/, /\.get\s*\(\s*req\.params\.id(?!.*(?:userId|ownerId|user_id)\s*[:=])/, /WHERE.*=\s*req\.params\./i, /SELECT.*req\.params\./i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Verify that the requesting user owns the resource.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1036
|
+
{ id: 'SEC-125', category: 'auth', owasp: 'A01:2021', severity: 'HIGH', name: 'Mass Assignment', description: 'Request body spread directly into model.', patterns: [/\.create\s*\(\s*req\.body/, /\.update\s*\(\s*[^,]*,\s*req\.body/, /new\s+\w+\s*\(\s*req\.body/, /\w+\s*\.\s*set\s*\(\s*req\.body/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Whitelist allowed fields.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1037
|
+
{ id: 'SEC-126', category: 'auth', owasp: 'A01:2021', severity: 'MEDIUM', name: 'Privilege Escalation', description: 'Role set from request without validation.', patterns: [/role\s*[:=]\s*req\.(?:body|params|query)\./, /req\.body\.role/, /\.role\s*=\s*req\./], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Never set user role from client input.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1038
|
+
{ id: 'SEC-127', category: 'auth', owasp: 'A07:2021', severity: 'HIGH', name: 'Missing Rate Limiting', description: 'Login endpoint without rate limiting.', patterns: [/router\.(?:post|get)\s*\(\s*['"`]\/(?:login|signin|auth)/, /@(?:Post|Get)\s*\(\s*['"].*(?:login|signin|auth)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Add rate limiting to auth endpoints.', confidence: 0.60, falsePositiveRisk: 'high', enabled: true },
|
|
1039
|
+
{ id: 'SEC-128', category: 'auth', owasp: 'A07:2021', severity: 'MEDIUM', name: 'Predictable Session ID', description: 'Custom session ID generation.', patterns: [/Math\.random.*session/i, /Date\.now\s*\(\s*\).*session/i, /session.*=.*toString\s*\(\s*36\s*\)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use framework built-in session ID generation.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1040
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1041
|
+
// SEC-129 – SEC-135 : JWT & Session (A07:2021)
|
|
1042
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1043
|
+
{ id: 'SEC-129', category: 'jwt', owasp: 'A07:2021', severity: 'CRITICAL', name: 'JWT None Algorithm', description: 'JWT verified without algorithm check.', patterns: [/algorithms\s*:\s*\[['"`]none['"`]\]/, /verify\s*\(\s*token.*none/i, /jwt\.verify\s*\((?!.*algorithms\s*:)/i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Always specify allowed algorithms in jwt.verify().', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1044
|
+
{ id: 'SEC-130', category: 'jwt', owasp: 'A07:2021', severity: 'HIGH', name: 'JWT weak secret', description: 'JWT signed with short/weak secret.', patterns: [/jwt\.sign\s*\([^,]*,\s*['"`][A-Za-z0-9]{1,15}['"`]\)/, /JWT_SECRET\s*=\s*['"`][A-Za-z0-9]{1,15}['"`]/], fix: 'Use a secret of at least 256 bits.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1045
|
+
{ id: 'SEC-131', category: 'jwt', owasp: 'A07:2021', severity: 'CRITICAL', name: 'JWT Signature Not Verified', description: 'JWT used without signature verification.', patterns: [/jwt\.decode\s*\(\s*token\s*\)/, /\.decode\s*\(\s*token\s*\)/, /jose.*decode.*without.*verify/i], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Always use jwt.verify() instead of jwt.decode().', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1046
|
+
{ id: 'SEC-132', category: 'jwt', owasp: 'A07:2021', severity: 'MEDIUM', name: 'JWT Long Expiration', description: 'JWT with excessively long expiration.', patterns: [/expiresIn\s*:\s*['"`]\d{2,}d['"`]/, /expiresIn\s*:\s*\d{8,}/, /maxAge\s*:\s*\d{8,}/], fileGlob: JS_TS_PY, fix: 'Use short-lived tokens with refresh tokens.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1047
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1048
|
+
// SEC-133 – SEC-142 : Crypto (A02:2021)
|
|
1049
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1050
|
+
{ id: 'SEC-133', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'MD2 Hash', description: 'MD2 hash used (broken).', patterns: [/['"`]md2['"`]/i, /MD2\s*\./, /hashlib\.md2/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use SHA-256 or better.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1051
|
+
{ id: 'SEC-134', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'MD4 Hash', description: 'MD4 hash used (broken).', patterns: [/['"`]md4['"`]/i, /MD4\s*\./, /hashlib\.md4/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use SHA-256 or better.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1052
|
+
{ id: 'SEC-135', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'DES Encryption', description: 'DES cipher used (broken).', patterns: [/['"`]des['"`]/i, /Cipher\.getInstance\s*\(\s*['"]DES/, /DES\.MODE/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use AES-256-GCM. Do not use DES.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1053
|
+
{ id: 'SEC-136', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: '3DES Encryption', description: '3DES cipher used (deprecated).', patterns: [/['"`]3des['"`]/i, /['"`]DESede['"`]/, /TripleDES/, /3DES\.MODE/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use AES-256-GCM.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1054
|
+
{ id: 'SEC-137', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'RC2 Encryption', description: 'RC2 cipher used (broken).', patterns: [/['"`]rc2['"`]/i, /RC2\.MODE/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use AES-256-GCM.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1055
|
+
{ id: 'SEC-138', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'RC4 Encryption', description: 'RC4 cipher used (broken).', patterns: [/['"`]rc4['"`]/i, /ArcFour/, /RC4\.MODE/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use AES-256-GCM.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1056
|
+
{ id: 'SEC-139', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'ECB Mode Encryption', description: 'ECB cipher mode used (insecure).', patterns: [/['"`]ECB['"`]/, /Cipher\.getInstance\s*\(\s*['"]AES\/ECB/, /AES\.MODE_ECB/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use AES-GCM or AES-CBC with random IV.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1057
|
+
{ id: 'SEC-140', category: 'crypto', owasp: 'A02:2021', severity: 'MEDIUM', name: 'Weak RSA Key Length', description: 'RSA key < 2048 bits.', patterns: [/RSA\.generate\s*\(\s*1024/, /rsa\.generate\s*\(\s*1024/], fileGlob: ALL_CODE, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use RSA keys of at least 2048 bits.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1058
|
+
{ id: 'SEC-141', category: 'crypto', owasp: 'A02:2021', severity: 'HIGH', name: 'Custom Crypto Implementation', description: 'Custom cryptography detected.', patterns: [/function\s+(?:encrypt|decrypt|hash|cipher)\s*\(/, /class\s+(?:Encrypt|Decrypt|Cipher|Hash)\s*\{/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use well-vetted libraries. Never roll your own crypto.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1059
|
+
{ id: 'SEC-142', category: 'crypto', owasp: 'A02:2021', severity: 'MEDIUM', name: 'Insecure UUID Generation', description: 'Non-cryptographic UUID.', patterns: [/Math\.random.*uuid/i, /\.substring.*Math\.random/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use crypto.randomUUID() instead.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1060
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1061
|
+
// SEC-143 – SEC-148 : CORS & Headers (A05:2021)
|
|
1062
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1063
|
+
{ id: 'SEC-143', category: 'cors-headers', owasp: 'A05:2021', severity: 'HIGH', name: 'CORS Wildcard Credentials', description: 'CORS with credentials and wildcard origin.', patterns: [/credentials\s*:\s*true.*origin\s*:\s*['"`]\*['"`]/, /origin\s*:\s*['"`]\*['"`].*credentials\s*:\s*true/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Specify explicit origins when using credentials.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1064
|
+
{ id: 'SEC-144', category: 'cors-headers', owasp: 'A05:2021', severity: 'HIGH', name: 'Missing HSTS Header', description: 'Strict-Transport-Security not configured.', patterns: [/hsts\s*:\s*false/i, /Strict-Transport-Security.*max-age=0/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Enable HSTS with max-age at least 1 year.', confidence: 0.60, falsePositiveRisk: 'medium', enabled: true },
|
|
1065
|
+
{ id: 'SEC-145', category: 'cors-headers', owasp: 'A05:2021', severity: 'MEDIUM', name: 'Missing X-Frame-Options', description: 'X-Frame-Options not set.', patterns: [/frameGuard\s*:\s*false/i, /frameguard\s*:\s*false/i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set X-Frame-Options: DENY or SAMEORIGIN.', confidence: 0.55, falsePositiveRisk: 'medium', enabled: true },
|
|
1066
|
+
{ id: 'SEC-146', category: 'cors-headers', owasp: 'A05:2021', severity: 'MEDIUM', name: 'Missing X-Content-Type-Options', description: 'nosniff header not set.', patterns: [/noSniff\s*:\s*false/i, /nosniff\s*:\s*false/i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set X-Content-Type-Options: nosniff.', confidence: 0.55, falsePositiveRisk: 'medium', enabled: true },
|
|
1067
|
+
{ id: 'SEC-147', category: 'cors-headers', owasp: 'A05:2021', severity: 'LOW', name: 'Missing Referrer Policy', description: 'Referrer-Policy not set.', patterns: [/referrerPolicy\s*:\s*false/i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set Referrer-Policy: strict-origin-when-cross-origin.', confidence: 0.45, falsePositiveRisk: 'high', enabled: true },
|
|
1068
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1069
|
+
// SEC-148 – SEC-155 : Info Disclosure (A09:2021)
|
|
1070
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1071
|
+
{ id: 'SEC-148', category: 'data-leak', owasp: 'A09:2021', severity: 'HIGH', name: 'Stack Trace Exposure', description: 'Error details sent to client.', patterns: [/res\.send\s*\(\s*err\.stack/, /\.status.*\.send.*err/, /\.json\s*\(\s*\{\s*error\s*:\s*err/, /traceback\.format_exc\s*\(\s*\)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Log errors server-side. Send generic error messages.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1072
|
+
{ id: 'SEC-149', category: 'data-leak', owasp: 'A09:2021', severity: 'MEDIUM', name: 'Source Map Exposure', description: 'Source maps deployed to production.', patterns: [/\.map['"`]\s*\)/, /sourceMap.*true.*production/i, /devtool.*source-map/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Do not deploy source maps to production.', confidence: 0.55, falsePositiveRisk: 'medium', enabled: true },
|
|
1073
|
+
{ id: 'SEC-150', category: 'data-leak', owasp: 'A09:2021', severity: 'HIGH', name: 'Git Directory Exposure', description: '.git directory accessible.', patterns: [/\.git\/HEAD/, /\.git\/config/], fileGlob: '**/*.{json,yaml,yml}', fix: 'Ensure .git directory is not deployed.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1074
|
+
{ id: 'SEC-151', category: 'data-leak', owasp: 'A09:2021', severity: 'MEDIUM', name: 'Backup File Exposure', description: 'Backup files deployed.', patterns: [/\.bak['"`]/, /\.backup['"`]/, /\.old['"`]/, /\.swp['"`]/, /~\s*$/], excludeGlob: DEFAULT_EXCLUDE, fix: 'Remove backup files before deployment.', confidence: 0.50, falsePositiveRisk: 'high', enabled: true },
|
|
1075
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1076
|
+
// SEC-152 – SEC-156 : Docker Security (A05:2021)
|
|
1077
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1078
|
+
{ id: 'SEC-152', category: 'docker', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Container Running As Root', description: 'Docker container runs as root.', patterns: [/USER\s+root/, /^USER\s+0$/m], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Run containers as a non-root user.', confidence: 0.55, falsePositiveRisk: 'high', enabled: true },
|
|
1079
|
+
{ id: 'SEC-153', category: 'docker', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Privileged Container', description: 'Container in privileged mode.', patterns: [/privileged\s*:\s*true/, /--privileged/], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Remove privileged mode.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1080
|
+
{ id: 'SEC-154', category: 'docker', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Docker Socket Mounted', description: 'Docker socket mounted.', patterns: [/\/var\/run\/docker\.sock/], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Do not mount Docker socket.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1081
|
+
{ id: 'SEC-155', category: 'docker', owasp: 'A05:2021', severity: 'HIGH', name: 'ADD Remote URL', description: 'ADD with remote URL.', patterns: [/^ADD\s+https?:/, /^ADD\s+http:/], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use curl with pinned checksums.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1082
|
+
{ id: 'SEC-156', category: 'docker', owasp: 'A05:2021', severity: 'MEDIUM', name: 'Latest Tag Usage', description: ':latest tag in production.', patterns: [/:latest/, /image\s*:\s*\w+\s*$/, /FROM\s+\w+\s*$/], fileGlob: DOCKER_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Pin to a specific version tag.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1083
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1084
|
+
// SEC-157 – SEC-162 : Kubernetes Security (A05:2021)
|
|
1085
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1086
|
+
{ id: 'SEC-157', category: 'kubernetes', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Privileged Pod', description: 'K8s pod with privileged context.', patterns: [/privileged\s*:\s*true/, /securityContext\s*:\s*\n\s*privileged\s*:\s*true/], fileGlob: K8S_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Do not run pods in privileged mode.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1087
|
+
{ id: 'SEC-158', category: 'kubernetes', owasp: 'A05:2021', severity: 'CRITICAL', name: 'HostPath Mount', description: 'hostPath volume mount.', patterns: [/hostPath\s*:/, /path\s*:\s*\/[^\/]/], fileGlob: K8S_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Avoid hostPath mounts.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1088
|
+
{ id: 'SEC-159', category: 'kubernetes', owasp: 'A05:2021', severity: 'HIGH', name: 'Run As Root', description: 'Pod with runAsUser 0.', patterns: [/runAsUser\s*:\s*0/, /runAsNonRoot\s*:\s*false/], fileGlob: K8S_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set runAsNonRoot: true.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1089
|
+
{ id: 'SEC-160', category: 'kubernetes', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Cluster Admin Binding', description: 'ClusterRoleBinding to cluster-admin.', patterns: [/cluster-admin/, /ClusterRoleBinding.*cluster-admin/], fileGlob: K8S_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use least-privilege roles.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1090
|
+
{ id: 'SEC-161', category: 'kubernetes', owasp: 'A05:2021', severity: 'HIGH', name: 'Host Network Enabled', description: 'Pod using host network.', patterns: [/hostNetwork\s*:\s*true/, /hostPID\s*:\s*true/, /hostIPC\s*:\s*true/], fileGlob: K8S_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable hostNetwork/hostPID/hostIPC.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1091
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1092
|
+
// SEC-162 – SEC-166 : Terraform/IaC (A05:2021)
|
|
1093
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1094
|
+
{ id: 'SEC-162', category: 'iac', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Open CIDR 0.0.0.0/0', description: 'Security group open to world.', patterns: [/cidr_blocks\s*=\s*\[['"]0\.0\.0\.0\/0['"]\]/, /source_ranges\s*=\s*\[['"]0\.0\.0\.0\/0['"]\]/], fileGlob: TF_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Restrict CIDR blocks.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1095
|
+
{ id: 'SEC-163', category: 'iac', owasp: 'A05:2021', severity: 'HIGH', name: 'Unencrypted Storage', description: 'Storage without encryption.', patterns: [/encrypted\s*=\s*false/, /encryption\s*{\s*enabled\s*=\s*false/], fileGlob: TF_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Enable encryption at rest.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1096
|
+
{ id: 'SEC-164', category: 'iac', owasp: 'A05:2021', severity: 'HIGH', name: 'Public Database Instance', description: 'Database publicly accessible.', patterns: [/publicly_accessible\s*=\s*true/], fileGlob: TF_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set publicly_accessible to false.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1097
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1098
|
+
// SEC-165 – SEC-172 : Cloud Security (A05:2021)
|
|
1099
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1100
|
+
{ id: 'SEC-165', category: 'cloud', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Public S3 Bucket', description: 'S3 bucket with public access.', patterns: [/acl\s*=\s*['"]public/, /block_public_acls\s*=\s*false/, /block_public_policy\s*=\s*false/], fileGlob: TF_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Block all public access to S3 buckets.', confidence: 0.50, falsePositiveRisk: 'high', enabled: true },
|
|
1101
|
+
{ id: 'SEC-166', category: 'cloud', owasp: 'A05:2021', severity: 'CRITICAL', name: 'Open Security Group', description: 'Security group wide-open ports.', patterns: [/from_port\s*=\s*0.*to_port\s*=\s*0/, /protocol\s*=\s*['"]\-1['"]/], fileGlob: TF_FILES, excludeGlob: DEFAULT_EXCLUDE, fix: 'Restrict security group rules.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1102
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1103
|
+
// SEC-167 – SEC-172 : API Security (A01/A04:2021)
|
|
1104
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1105
|
+
{ id: 'SEC-167', category: 'api-security', owasp: 'A01:2021', severity: 'HIGH', name: 'Missing API Authentication', description: 'API endpoint without auth.', patterns: [/router\.\w+\s*\(\s*['"`]\/api\/v?\d*\/\w+['"`]\s*,/, /@\w+\s*\(\s*['"`]\/api\/v?\d*\/\w+['"`]\)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Require authentication for all API endpoints.', confidence: 0.65, falsePositiveRisk: 'high', enabled: true },
|
|
1106
|
+
{ id: 'SEC-168', category: 'api-security', owasp: 'A04:2021', severity: 'MEDIUM', name: 'Missing Request Size Limit', description: 'No body size limit on API.', patterns: [/bodyParser\.json\s*\(\s*\)/, /express\.json\s*\(\s*\)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set a reasonable request body size limit.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1107
|
+
{ id: 'SEC-169', category: 'api-security', owasp: 'A04:2021', severity: 'MEDIUM', name: 'GraphQL Introspection Enabled', description: 'Introspection left enabled.', patterns: [/introspection\s*:\s*true/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Disable GraphQL introspection in production.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1108
|
+
{ id: 'SEC-170', category: 'api-security', owasp: 'A04:2021', severity: 'MEDIUM', name: 'GraphQL Excessive Depth', description: 'No depth limit on queries.', patterns: [/ApolloServer.*no.*depth/, /graphql.*no.*maxDepth/i, /validationRules.*\[\s*\]/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Set a maximum query depth.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1109
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1110
|
+
// SEC-171 – SEC-175 : Business Logic (A04:2021)
|
|
1111
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1112
|
+
{ id: 'SEC-171', category: 'business-logic', owasp: 'A04:2021', severity: 'MEDIUM', name: 'Integer Overflow', description: 'Integer arithmetic potentially vulnerable to overflow.', patterns: [/amount\s*\*\s*\d+\s*\+/, /\b(?:price|amount|total|cost)\s*\*\s*(?:req\.|request\.|params\.|query\.|body\.)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use BigInt for financial calculations.', confidence: 0.40, falsePositiveRisk: 'high', enabled: true },
|
|
1113
|
+
{ id: 'SEC-172', category: 'business-logic', owasp: 'A04:2021', severity: 'MEDIUM', name: 'Negative Value Processing', description: 'No validation for negative values.', patterns: [/price\s*=\s*req\.body\./, /amount\s*=\s*req\.body\./, /quantity\s*=\s*req\.body\./], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate that price/amount values are positive.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1114
|
+
{ id: 'SEC-173', category: 'business-logic', owasp: 'A04:2021', severity: 'MEDIUM', name: 'TOCTOU Vulnerability', description: 'Check-then-use pattern.', patterns: [/if\s*\(\s*existsSync/, /if\s*\(\s*fs\.existsSync/, /if\s*\(\s*os\.path\.exists/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Use atomic operations.', confidence: 0.75, falsePositiveRisk: 'low', enabled: true },
|
|
1115
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1116
|
+
// SEC-174 – SEC-180 : Misc (various OWASP)
|
|
1117
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1118
|
+
{ id: 'SEC-174', category: 'misc', owasp: 'A01:2021', severity: 'HIGH', name: 'Open Redirect', description: 'User-controlled URL in redirect.', patterns: [/res\.redirect\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, /redirect\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, /location\.(?:replace|href|assign)\s*=\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate redirect URLs against a whitelist.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1119
|
+
{ id: 'SEC-175', category: 'misc', owasp: 'A01:2021', severity: 'HIGH', name: 'Missing CSRF Protection', description: 'State-changing endpoint without CSRF.', patterns: [/csrf\s*:\s*false/i, /\.post\s*\(\s*['"`]\/[^'"]*['"`]\s*,(?!.*csrf)/], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Enable CSRF protection.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1120
|
+
{ id: 'SEC-176', category: 'misc', owasp: 'A03:2021', severity: 'MEDIUM', name: 'Unsafe Regex (ReDoS)', description: 'Regex with catastrophic backtracking.', patterns: [/\(\s*\w\+\s*\)\s*\+\s*\)/, /\(\s*\.\*\s*\)\s*\+/, /\(\s*(?:\w\+\s*\|)+\w\+\s*\)\s*\*/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Rewrite regex to avoid nested quantifiers.', confidence: 0.75, falsePositiveRisk: 'low', enabled: true },
|
|
1121
|
+
{ id: 'SEC-177', category: 'misc', owasp: 'A04:2021', severity: 'HIGH', name: 'Prototype Pollution', description: 'Object merge with user input.', patterns: [/Object\.assign\s*\(\s*[^,]*,\s*req\./, /\.\.\.\s*req\.body/, /\{\s*\.\.\.\s*req\.body/, /\.extend\s*\(\s*[^,]*,\s*req\./, /_.merge\s*\(\s*[^,]*,\s*req\./, /lodash.*merge.*req\./i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Validate user input before merging.', confidence: 0.90, falsePositiveRisk: 'low', enabled: true },
|
|
1122
|
+
{ id: 'SEC-178', category: 'misc', owasp: 'A01:2021', severity: 'MEDIUM', name: 'Reverse Tabnabbing', description: 'target="_blank" without noopener.', patterns: [/target\s*=\s*['"`]_blank['"`](?!.*noopener)/, /window\.open\s*\(\s*(?!.*noopener)/], fileGlob: '**/*.{jsx,tsx}', fix: 'Add rel="noopener noreferrer".', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1123
|
+
{ id: 'SEC-179', category: 'misc', owasp: 'A04:2021', severity: 'MEDIUM', name: 'Resource Exhaustion', description: 'Unbounded resource allocation.', patterns: [/\.split\s*\(\s*(?:req\.|request\.|params\.)/, /Array\s*\(\s*parseInt\s*\(\s*(?:req\.|request\.|params\.)/, /\.repeat\s*\(\s*(?:req\.|request\.|params\.)/], fileGlob: JS_TS_PY, excludeGlob: DEFAULT_EXCLUDE, fix: 'Apply limits to user-supplied values.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1124
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1125
|
+
// SEC-180 – SEC-185 : Dependency Security (A06:2021)
|
|
1126
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1127
|
+
{ id: 'SEC-180', category: 'dependency', owasp: 'A06:2021', severity: 'HIGH', name: 'Unpinned Dependency', description: 'Version not pinned.', patterns: [/"\*"\s*:/, /"\^/, /"~/, /">=/], fileGlob: '**/package.json', excludeGlob: DEFAULT_EXCLUDE, fix: 'Pin dependency versions.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1128
|
+
{ id: 'SEC-181', category: 'dependency', owasp: 'A06:2021', severity: 'HIGH', name: 'Untrusted Package Source', description: 'Non-standard registry.', patterns: [/registry\s*=\s*['"`]https?:\/\/(?!registry\.npmjs|registry\.yarnpkg)/], fileGlob: '**/{.npmrc,.yarnrc}', fix: 'Only use trusted registries.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1129
|
+
{ id: 'SEC-182', category: 'dependency', owasp: 'A06:2021', severity: 'MEDIUM', name: 'Unsafe Postinstall', description: 'postinstall scripts enabled.', patterns: [/ignore-scripts\s*=\s*false/, /unsafe-perm\s*=\s*true/], fileGlob: '**/{.npmrc,.yarnrc}', fix: 'Set ignore-scripts=true.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1130
|
+
{ id: 'SEC-183', category: 'dependency', owasp: 'A06:2021', severity: 'HIGH', name: 'Dependency Confusion', description: 'Internal package name collision.', patterns: [/"@(?:company|internal|private)\//], fileGlob: '**/package.json', excludeGlob: DEFAULT_EXCLUDE, fix: 'Use scoped packages with registry mappings.', confidence: 0.80, falsePositiveRisk: 'low', enabled: true },
|
|
1131
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1132
|
+
// SEC-184 – SEC-188 : Session & Cookie (A07:2021)
|
|
1133
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1134
|
+
{ id: 'SEC-184', category: 'config', owasp: 'A07:2021', severity: 'HIGH', name: 'Session Cookie Missing SameSite', description: 'Cookie without SameSite.', patterns: [/cookie\s*\(\s*['"`][^'"]*['"`]\s*,\s*(?!.*sameSite)/i, /session.*cookie.*(?!.*sameSite)/i], excludeGlob: DEFAULT_EXCLUDE, fileGlob: JS_TS_PY, fix: 'Set SameSite=Lax on session cookies.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1135
|
+
{ id: 'SEC-185', category: 'config', owasp: 'A07:2021', severity: 'MEDIUM', name: 'Session Stored Client Side', description: 'Session data in client storage.', patterns: [/localStorage\.setItem/, /sessionStorage\.setItem/, /document\.cookie\s*=\s*[^;]*token/i], fileGlob: JS_TS, excludeGlob: DEFAULT_EXCLUDE, fix: 'Store tokens in httpOnly cookies.', confidence: 0.85, falsePositiveRisk: 'low', enabled: true },
|
|
1136
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1137
|
+
// SEC-186 – SEC-190 : Additional Secrets
|
|
1138
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1139
|
+
{ id: 'SEC-186', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Hardcoded SMTP Credential', description: 'SMTP credentials hardcoded.', patterns: [/smtp.*(?:user|pass)\s*[:=]\s*['"`][^'"`]{3,}['"`]/i, /nodemailer.*auth\s*:\s*\{[^}]*pass\s*:\s*['"`]/i], fix: 'Use environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1140
|
+
{ id: 'SEC-187', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Hardcoded LDAP Credential', description: 'LDAP bind password.', patterns: [/ldap.*(?:password|bindpw)\s*[:=]\s*['"`][^'"`]{3,}['"`]/i], fix: 'Use environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1141
|
+
{ id: 'SEC-188', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Hardcoded FTP Credential', description: 'FTP password hardcoded.', patterns: [/ftp.*(?:password|pass)\s*[:=]\s*['"`][^'"`]{3,}['"`]/i], fix: 'Use environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1142
|
+
{ id: 'SEC-189', category: 'secrets', owasp: 'A02:2021', severity: 'CRITICAL', name: 'Anthropic API Key', description: 'Anthropic API key exposed.', patterns: [/sk-ant-[a-zA-Z0-9\-_]{30,}/, /anthropic.*(?:api[_-]?key|key)\s*[:=]\s*['"`]sk-ant/i], fix: 'Store in environment variables.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
1143
|
+
{ id: 'SEC-190', category: 'secrets', owasp: 'A02:2021', severity: 'HIGH', name: 'Hardcoded Database Password', description: 'DB password in connection string.', patterns: [/mysql:\/\/[^:]+:[^@]+@/, /postgres:\/\/[^:]+:[^@]+@/, /mongodb:\/\/[^:]+:[^@]+@/, /DATABASE_URL\s*=\s*[`'"][^`'"]*:[^`'"]*@/i], excludeGlob: DEFAULT_EXCLUDE, fix: 'Use environment variables for DB credentials.', confidence: 0.95, falsePositiveRisk: 'low', enabled: true },
|
|
929
1144
|
];
|
|
930
1145
|
// ── Utility functions ──
|
|
931
1146
|
export function loadRules() {
|