codefence 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,691 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "schema": "codefence/rules-v1",
4
+ "rules": [
5
+ {
6
+ "id": "R001",
7
+ "name": "Hardcoded API keys / secrets",
8
+ "languages": [
9
+ "python",
10
+ "javascript"
11
+ ],
12
+ "severity": "critical",
13
+ "category": "secrets",
14
+ "description": "Hardcoded key/token/password detected in source.",
15
+ "detection": "regex",
16
+ "patterns": [
17
+ "(?i)(api[_-]?key|secret|token|password|passwd|pwd)\\s*[=:]\\s*[\"'][A-Za-z0-9_\\-]{12,}[\"']",
18
+ "\\bsk-[A-Za-z0-9]{16,}\\b",
19
+ "\\bghp_[A-Za-z0-9]{20,}\\b",
20
+ "\\bAKIA[0-9A-Z]{16}\\b"
21
+ ],
22
+ "message": "Possible hardcoded secret detected.",
23
+ "remediation": "Move the value to an environment variable or a secret manager.",
24
+ "references": [
25
+ "CWE-798",
26
+ "CWE-259"
27
+ ],
28
+ "confidence": "high",
29
+ "enabled": true,
30
+ "fix_before": "API_KEY = \"sk-abcdef1234567890\"",
31
+ "fix_after": "API_KEY = os.environ[\"API_KEY\"]"
32
+ },
33
+ {
34
+ "id": "R002",
35
+ "name": "SQL injection via string concatenation",
36
+ "languages": [
37
+ "python"
38
+ ],
39
+ "severity": "critical",
40
+ "category": "injection",
41
+ "description": "SQL query built via concatenation or f-string.",
42
+ "detection": "ast",
43
+ "handler": "check_sql_injection_python",
44
+ "message": "Possible SQL injection - use parameterized queries.",
45
+ "remediation": "Use cursor.execute(\"... = ?\", (value,)) instead of string building.",
46
+ "references": [
47
+ "CWE-89"
48
+ ],
49
+ "confidence": "high",
50
+ "enabled": true,
51
+ "fix_before": "cursor.execute(\"SELECT * FROM t WHERE id = \" + uid)",
52
+ "fix_after": "cursor.execute(\"SELECT * FROM t WHERE id = ?\", (uid,))"
53
+ },
54
+ {
55
+ "id": "R003",
56
+ "name": "eval() / exec() on untrusted input",
57
+ "languages": [
58
+ "python"
59
+ ],
60
+ "severity": "critical",
61
+ "category": "injection",
62
+ "description": "Use of eval() or exec() may allow arbitrary code execution.",
63
+ "detection": "ast",
64
+ "handler": "check_eval_exec_python",
65
+ "message": "Use of eval/exec detected.",
66
+ "remediation": "Replace with ast.literal_eval for literals, or a safe parser.",
67
+ "references": [
68
+ "CWE-95"
69
+ ],
70
+ "confidence": "high",
71
+ "enabled": true,
72
+ "fix_before": "result = eval(user_input)",
73
+ "fix_after": "result = ast.literal_eval(user_input) # literals only"
74
+ },
75
+ {
76
+ "id": "R004",
77
+ "name": "Command injection via shell=True",
78
+ "languages": [
79
+ "python"
80
+ ],
81
+ "severity": "critical",
82
+ "category": "injection",
83
+ "description": "subprocess call with shell=True can lead to command injection.",
84
+ "detection": "ast",
85
+ "handler": "check_shell_true_python",
86
+ "message": "subprocess call with shell=True detected.",
87
+ "remediation": "Pass arguments as a list and set shell=False.",
88
+ "references": [
89
+ "CWE-78"
90
+ ],
91
+ "confidence": "high",
92
+ "enabled": true,
93
+ "fix_before": "subprocess.run(\"ls \" + name, shell=True)",
94
+ "fix_after": "subprocess.run([\"ls\", name], shell=False)"
95
+ },
96
+ {
97
+ "id": "R005",
98
+ "name": "innerHTML with unsanitized data",
99
+ "languages": [
100
+ "javascript"
101
+ ],
102
+ "severity": "critical",
103
+ "category": "injection",
104
+ "description": "Assignment to innerHTML or document.write with non-literal data.",
105
+ "detection": "lexical",
106
+ "handler": "check_innerhtml_js",
107
+ "message": "innerHTML / document.write with dynamic data.",
108
+ "remediation": "Use textContent, or sanitize with DOMPurify.",
109
+ "references": [
110
+ "CWE-79"
111
+ ],
112
+ "confidence": "high",
113
+ "enabled": true,
114
+ "fix_before": "el.innerHTML = userInput;",
115
+ "fix_after": "el.textContent = userInput; // or DOMPurify.sanitize(userInput)"
116
+ },
117
+ {
118
+ "id": "R006",
119
+ "name": "Missing authentication on endpoints",
120
+ "languages": [
121
+ "python",
122
+ "javascript"
123
+ ],
124
+ "severity": "critical",
125
+ "category": "auth",
126
+ "description": "Route registered without an auth decorator or middleware.",
127
+ "detection": "lexical",
128
+ "handler": "check_missing_auth",
129
+ "message": "Route without authentication detected.",
130
+ "remediation": "Add an auth decorator or middleware on the route.",
131
+ "references": [
132
+ "CWE-306"
133
+ ],
134
+ "confidence": "medium",
135
+ "enabled": true,
136
+ "fix_before": "@app.get(\"/admin/users\")\ndef list_users(): ...",
137
+ "fix_after": "@app.get(\"/admin/users\")\n@login_required\ndef list_users(): ..."
138
+ },
139
+ {
140
+ "id": "R007",
141
+ "name": "Overly permissive CORS (*)",
142
+ "languages": [
143
+ "python",
144
+ "javascript"
145
+ ],
146
+ "severity": "high",
147
+ "category": "config",
148
+ "description": "CORS allows any origin.",
149
+ "detection": "regex",
150
+ "patterns": [
151
+ "allow_origins\\s*=\\s*\\[\\s*[\"']\\*[\"']\\s*\\]",
152
+ "Access-Control-Allow-Origin[\"']?\\s*[:=]\\s*[\"']\\*[\"']",
153
+ "cors\\(\\s*\\)",
154
+ "origin\\s*:\\s*[\"']\\*[\"']"
155
+ ],
156
+ "message": "Permissive CORS detected.",
157
+ "remediation": "Restrict allowed origins to a known list.",
158
+ "references": [
159
+ "CWE-942"
160
+ ],
161
+ "confidence": "high",
162
+ "enabled": true,
163
+ "fix_before": "allow_origins = [\"*\"]",
164
+ "fix_after": "allow_origins = [\"https://app.example.com\"]"
165
+ },
166
+ {
167
+ "id": "R008",
168
+ "name": "Insecure random for security tokens",
169
+ "languages": [
170
+ "python"
171
+ ],
172
+ "severity": "high",
173
+ "category": "crypto",
174
+ "description": "random module used for tokens or secrets.",
175
+ "detection": "ast",
176
+ "handler": "check_random_python",
177
+ "message": "random module used - not cryptographically secure.",
178
+ "remediation": "Use secrets module for tokens and secrets.",
179
+ "references": [
180
+ "CWE-338"
181
+ ],
182
+ "confidence": "high",
183
+ "enabled": true,
184
+ "fix_before": "token = random.randint(1000, 9999)",
185
+ "fix_after": "token = secrets.token_urlsafe(32)"
186
+ },
187
+ {
188
+ "id": "R009",
189
+ "name": "Disabled TLS/SSL verification",
190
+ "languages": [
191
+ "python",
192
+ "javascript"
193
+ ],
194
+ "severity": "high",
195
+ "category": "crypto",
196
+ "description": "TLS verification disabled in HTTP request or global config.",
197
+ "detection": "regex",
198
+ "patterns": [
199
+ "verify\\s*=\\s*False",
200
+ "_create_unverified_context\\s*\\(",
201
+ "NODE_TLS_REJECT_UNAUTHORIZED\\s*[=:]\\s*[\"']?0",
202
+ "rejectUnauthorized\\s*:\\s*false"
203
+ ],
204
+ "message": "TLS verification disabled.",
205
+ "remediation": "Remove verify=False and do not disable TLS verification.",
206
+ "references": [
207
+ "CWE-295"
208
+ ],
209
+ "confidence": "high",
210
+ "enabled": true,
211
+ "fix_before": "requests.get(url, verify=False)",
212
+ "fix_after": "requests.get(url) # verify=True is the default"
213
+ },
214
+ {
215
+ "id": "R010",
216
+ "name": "Path traversal",
217
+ "languages": [
218
+ "python",
219
+ "javascript"
220
+ ],
221
+ "severity": "high",
222
+ "category": "injection",
223
+ "description": "Path built from user input without containment check.",
224
+ "detection": "ast",
225
+ "handler": "check_path_traversal",
226
+ "message": "Possible path traversal.",
227
+ "remediation": "Resolve the path and verify it stays inside the base directory.",
228
+ "references": [
229
+ "CWE-22"
230
+ ],
231
+ "confidence": "medium",
232
+ "enabled": true,
233
+ "fix_before": "open(os.path.join(BASE, user_path))",
234
+ "fix_after": "full = os.path.realpath(os.path.join(BASE, user_path))\nif not full.startswith(os.path.realpath(BASE)):\n raise ValueError(\"path traversal\")\nopen(full)"
235
+ },
236
+ {
237
+ "id": "R011",
238
+ "name": "Hardcoded database credentials",
239
+ "languages": [
240
+ "python",
241
+ "javascript"
242
+ ],
243
+ "severity": "critical",
244
+ "category": "secrets",
245
+ "description": "Database URL with credentials hardcoded.",
246
+ "detection": "regex",
247
+ "patterns": [
248
+ "(?i)(postgres|postgresql|mysql|mariadb|mongodb|redis|amqp)://[A-Za-z0-9_\\-]+:[^@\\s\"']+@"
249
+ ],
250
+ "message": "Hardcoded database credentials detected.",
251
+ "remediation": "Load the connection string from environment variables.",
252
+ "references": [
253
+ "CWE-798"
254
+ ],
255
+ "confidence": "high",
256
+ "enabled": true,
257
+ "fix_before": "DATABASE_URL = \"postgres://admin:s3cr3t@db/app\"",
258
+ "fix_after": "DATABASE_URL = os.environ[\"DATABASE_URL\"]"
259
+ },
260
+ {
261
+ "id": "R012",
262
+ "name": "Broad except: pass",
263
+ "languages": [
264
+ "python"
265
+ ],
266
+ "severity": "medium",
267
+ "category": "quality",
268
+ "description": "Bare except or except Exception with pass silently swallows errors.",
269
+ "detection": "ast",
270
+ "handler": "check_except_pass_python",
271
+ "message": "Broad exception silently ignored.",
272
+ "remediation": "Catch specific exceptions and log or handle them.",
273
+ "references": [
274
+ "CWE-390"
275
+ ],
276
+ "confidence": "high",
277
+ "enabled": true,
278
+ "fix_before": "try:\n risky()\nexcept:\n pass",
279
+ "fix_after": "try:\n risky()\nexcept SpecificError as e:\n logger.warning(\"risky failed: %s\", e)"
280
+ },
281
+ {
282
+ "id": "R013",
283
+ "name": "Mutable default arguments",
284
+ "languages": [
285
+ "python"
286
+ ],
287
+ "severity": "medium",
288
+ "category": "quality",
289
+ "description": "Function defined with a mutable default argument.",
290
+ "detection": "ast",
291
+ "handler": "check_mutable_defaults_python",
292
+ "message": "Mutable default argument detected.",
293
+ "remediation": "Use None as the default and create the mutable inside the function.",
294
+ "references": [],
295
+ "confidence": "high",
296
+ "enabled": true,
297
+ "fix_before": "def add(item, items=[]):\n items.append(item)",
298
+ "fix_after": "def add(item, items=None):\n items = items if items is not None else []\n items.append(item)"
299
+ },
300
+ {
301
+ "id": "R014",
302
+ "name": "Prototype pollution",
303
+ "languages": [
304
+ "javascript"
305
+ ],
306
+ "severity": "high",
307
+ "category": "injection",
308
+ "description": "Assignment to __proto__ or dynamic key assignment into objects.",
309
+ "detection": "lexical",
310
+ "handler": "check_prototype_pollution_js",
311
+ "message": "Potential prototype pollution.",
312
+ "remediation": "Reject __proto__ and constructor keys; validate user input.",
313
+ "references": [
314
+ "CWE-1321"
315
+ ],
316
+ "confidence": "medium",
317
+ "enabled": true,
318
+ "fix_before": "obj[key] = value; // key from user input",
319
+ "fix_after": "if (key === \"__proto__\" || key === \"constructor\") throw new Error();\nobj[key] = value;"
320
+ },
321
+ {
322
+ "id": "R015",
323
+ "name": "Missing rate limiting on public endpoints",
324
+ "languages": [
325
+ "python",
326
+ "javascript"
327
+ ],
328
+ "severity": "high",
329
+ "category": "auth",
330
+ "description": "Login or public endpoint without a rate limiter.",
331
+ "detection": "lexical",
332
+ "handler": "check_missing_rate_limit",
333
+ "message": "Public endpoint without visible rate limiting.",
334
+ "remediation": "Apply a rate limiter on login and public endpoints.",
335
+ "references": [
336
+ "CWE-770"
337
+ ],
338
+ "confidence": "low",
339
+ "enabled": true,
340
+ "fix_before": "@app.post(\"/login\")",
341
+ "fix_after": "@limiter.limit(\"5/minute\")\n@app.post(\"/login\")"
342
+ },
343
+ {
344
+ "id": "R016",
345
+ "name": "MD5/SHA1 for password hashing",
346
+ "languages": [
347
+ "python",
348
+ "javascript"
349
+ ],
350
+ "severity": "critical",
351
+ "category": "crypto",
352
+ "description": "Weak hash used for password hashing.",
353
+ "detection": "regex",
354
+ "patterns": [
355
+ "hashlib\\.(md5|sha1)\\s*\\(",
356
+ "createHash\\s*\\(\\s*[\"'](md5|sha1)[\"']\\s*\\)"
357
+ ],
358
+ "message": "Weak hash function for password hashing.",
359
+ "remediation": "Use bcrypt, argon2, or scrypt.",
360
+ "references": [
361
+ "CWE-916"
362
+ ],
363
+ "confidence": "high",
364
+ "enabled": true,
365
+ "fix_before": "hashlib.md5(password.encode()).hexdigest()",
366
+ "fix_after": "bcrypt.hashpw(password.encode(), bcrypt.gensalt())"
367
+ },
368
+ {
369
+ "id": "R017",
370
+ "name": "Debug mode left enabled",
371
+ "languages": [
372
+ "python",
373
+ "javascript"
374
+ ],
375
+ "severity": "high",
376
+ "category": "config",
377
+ "description": "Debug mode enabled literally in source.",
378
+ "detection": "regex",
379
+ "patterns": [
380
+ "debug\\s*=\\s*True",
381
+ "DEBUG\\s*=\\s*True"
382
+ ],
383
+ "message": "Debug mode enabled.",
384
+ "remediation": "Load debug flag from environment, default False in production.",
385
+ "references": [
386
+ "CWE-489"
387
+ ],
388
+ "confidence": "high",
389
+ "enabled": true,
390
+ "fix_before": "app.run(debug=True)",
391
+ "fix_after": "app.run(debug=os.environ.get(\"DEBUG\") == \"1\")"
392
+ },
393
+ {
394
+ "id": "R018",
395
+ "name": "Open redirect",
396
+ "languages": [
397
+ "python",
398
+ "javascript"
399
+ ],
400
+ "severity": "high",
401
+ "category": "auth",
402
+ "description": "Redirect target taken from user-controlled input without validation.",
403
+ "detection": "lexical",
404
+ "handler": "check_open_redirect",
405
+ "message": "Possible open redirect.",
406
+ "remediation": "Only allow relative paths, or validate against an allowlist of hosts.",
407
+ "references": [
408
+ "CWE-601"
409
+ ],
410
+ "confidence": "medium",
411
+ "enabled": true,
412
+ "fix_before": "return redirect(request.args.get(\"next\"))",
413
+ "fix_after": "nxt = request.args.get(\"next\", \"/\")\nif not nxt.startswith(\"/\"):\n nxt = \"/\"\nreturn redirect(nxt)"
414
+ },
415
+ {
416
+ "id": "R019",
417
+ "name": "Unsafe deserialization",
418
+ "languages": [
419
+ "python"
420
+ ],
421
+ "severity": "critical",
422
+ "category": "injection",
423
+ "description": "pickle.loads or yaml.load used on untrusted data.",
424
+ "detection": "regex",
425
+ "patterns": [
426
+ "pickle\\.loads?\\s*\\(",
427
+ "yaml\\.load\\s*\\([^,)]*\\)",
428
+ "dill\\.loads?\\s*\\(",
429
+ "marshal\\.loads?\\s*\\("
430
+ ],
431
+ "message": "Unsafe deserialization detected.",
432
+ "remediation": "Use json, or yaml.safe_load for untrusted input.",
433
+ "references": [
434
+ "CWE-502"
435
+ ],
436
+ "confidence": "high",
437
+ "enabled": true,
438
+ "fix_before": "data = pickle.loads(payload)",
439
+ "fix_after": "data = json.loads(payload)"
440
+ },
441
+ {
442
+ "id": "R020",
443
+ "name": "Never-expiring tokens / JWT",
444
+ "languages": [
445
+ "python",
446
+ "javascript"
447
+ ],
448
+ "severity": "high",
449
+ "category": "auth",
450
+ "description": "JWT issued without an expiration claim.",
451
+ "detection": "lexical",
452
+ "handler": "check_jwt_no_exp",
453
+ "message": "JWT issued without expiration.",
454
+ "remediation": "Add exp (seconds) or expiresIn option.",
455
+ "references": [
456
+ "CWE-613"
457
+ ],
458
+ "confidence": "medium",
459
+ "enabled": true,
460
+ "fix_before": "jwt.encode({\"sub\": uid}, key, algorithm=\"HS256\")",
461
+ "fix_after": "jwt.encode({\"sub\": uid, \"exp\": now + 3600}, key, algorithm=\"HS256\")"
462
+ },
463
+ {
464
+ "id": "R021",
465
+ "name": "Missing security headers (Express)",
466
+ "languages": [
467
+ "javascript"
468
+ ],
469
+ "severity": "medium",
470
+ "category": "config",
471
+ "description": "Express app defined without helmet.",
472
+ "detection": "lexical",
473
+ "handler": "check_missing_helmet_js",
474
+ "message": "Express app without helmet middleware.",
475
+ "remediation": "Add app.use(helmet()).",
476
+ "references": [
477
+ "CWE-693"
478
+ ],
479
+ "confidence": "medium",
480
+ "enabled": true,
481
+ "fix_before": "const app = express();",
482
+ "fix_after": "const app = express();\napp.use(helmet());"
483
+ },
484
+ {
485
+ "id": "R022",
486
+ "name": "Race conditions in async code",
487
+ "languages": [
488
+ "python",
489
+ "javascript"
490
+ ],
491
+ "severity": "medium",
492
+ "category": "concurrency",
493
+ "description": "Check-then-act on filesystem or shared state.",
494
+ "detection": "lexical",
495
+ "handler": "check_race_conditions",
496
+ "message": "Possible race condition (check-then-act).",
497
+ "remediation": "Use atomic operations (open 'x', atomic rename, locks).",
498
+ "references": [
499
+ "CWE-362"
500
+ ],
501
+ "confidence": "low",
502
+ "enabled": true,
503
+ "fix_before": "if not os.path.exists(path):\n open(path, \"w\").write(data)",
504
+ "fix_after": "try:\n with open(path, \"x\") as f:\n f.write(data)\nexcept FileExistsError:\n pass"
505
+ },
506
+ {
507
+ "id": "R023",
508
+ "name": "Logging sensitive data",
509
+ "languages": [
510
+ "python",
511
+ "javascript"
512
+ ],
513
+ "severity": "high",
514
+ "category": "privacy",
515
+ "description": "Password, token, or secret passed to a logger or console.",
516
+ "detection": "regex",
517
+ "patterns": [
518
+ "(?i)(logger|logging|console|print)\\.[a-z]+\\([^)]*(password|passwd|pwd|token|secret|apikey|api_key)[^)]*\\)",
519
+ "(?i)(logger|logging|console)\\.(info|debug|warn|warning|error|log)\\([^)]*\\b(token|password|secret)\\b"
520
+ ],
521
+ "message": "Possible sensitive data logged.",
522
+ "remediation": "Remove secrets from log statements.",
523
+ "references": [
524
+ "CWE-532"
525
+ ],
526
+ "confidence": "medium",
527
+ "enabled": true,
528
+ "fix_before": "logger.info(\"login: %s / %s\", user, password)",
529
+ "fix_after": "logger.info(\"login: %s\", user)"
530
+ },
531
+ {
532
+ "id": "R024",
533
+ "name": "Unused imports / dead code",
534
+ "languages": [
535
+ "python"
536
+ ],
537
+ "severity": "low",
538
+ "category": "quality",
539
+ "description": "Import statement that is never referenced.",
540
+ "detection": "ast",
541
+ "handler": "check_unused_imports_python",
542
+ "message": "Unused import detected.",
543
+ "remediation": "Remove the unused import.",
544
+ "references": [],
545
+ "confidence": "high",
546
+ "enabled": true,
547
+ "fix_before": "import os\nimport sys # unused",
548
+ "fix_after": "import os"
549
+ },
550
+ {
551
+ "id": "R025",
552
+ "name": "Promise rejection ignored",
553
+ "languages": [
554
+ "javascript"
555
+ ],
556
+ "severity": "medium",
557
+ "category": "quality",
558
+ "description": "Promise chain without catch, or async call without try/catch.",
559
+ "detection": "lexical",
560
+ "handler": "check_promise_rejection_js",
561
+ "message": "Promise rejection may be silently ignored.",
562
+ "remediation": "Add .catch() or wrap await in try/catch.",
563
+ "references": [
564
+ "CWE-391"
565
+ ],
566
+ "confidence": "medium",
567
+ "enabled": true,
568
+ "fix_before": "fetch(url).then(r => r.json());",
569
+ "fix_after": "fetch(url).then(r => r.json()).catch(handleError);"
570
+ },
571
+ {
572
+ "id": "R026",
573
+ "name": "HTTP request without timeout",
574
+ "languages": [
575
+ "python"
576
+ ],
577
+ "severity": "medium",
578
+ "category": "reliability",
579
+ "description": "requests call without a timeout can hang indefinitely.",
580
+ "detection": "ast",
581
+ "handler": "check_requests_no_timeout_python",
582
+ "message": "HTTP request without timeout.",
583
+ "remediation": "Pass timeout=<seconds> to requests calls.",
584
+ "references": [
585
+ "CWE-400"
586
+ ],
587
+ "confidence": "high",
588
+ "enabled": true,
589
+ "fix_before": "requests.get(\"https://api.example.com\")",
590
+ "fix_after": "requests.get(\"https://api.example.com\", timeout=10)"
591
+ },
592
+ {
593
+ "id": "R027",
594
+ "name": "Naive datetime (no timezone)",
595
+ "languages": [
596
+ "python"
597
+ ],
598
+ "severity": "medium",
599
+ "category": "reliability",
600
+ "description": "datetime.now()/utcnow() without tz produces naive datetimes.",
601
+ "detection": "ast",
602
+ "handler": "check_datetime_no_tz_python",
603
+ "message": "Naive datetime created without timezone.",
604
+ "remediation": "Use datetime.now(timezone.utc) or timezone-aware arithmetic.",
605
+ "references": [
606
+ "CWE-190"
607
+ ],
608
+ "confidence": "medium",
609
+ "enabled": true,
610
+ "fix_before": "exp = datetime.now() + timedelta(hours=1)",
611
+ "fix_after": "exp = datetime.now(timezone.utc) + timedelta(hours=1)"
612
+ },
613
+ {
614
+ "id": "R028",
615
+ "name": "Env var with hardcoded fallback secret",
616
+ "languages": [
617
+ "python",
618
+ "javascript"
619
+ ],
620
+ "severity": "high",
621
+ "category": "secrets",
622
+ "description": "Secret is loaded from an environment variable but with a non-empty hardcoded fallback. If the env var is missing in production, the fallback is silently used.",
623
+ "detection": "ast",
624
+ "handler": "check_env_fallback_secret",
625
+ "message": "Environment variable with hardcoded fallback secret.",
626
+ "remediation": "Remove the fallback and fail fast if the variable is missing.",
627
+ "references": [
628
+ "CWE-1188",
629
+ "CWE-798"
630
+ ],
631
+ "confidence": "high",
632
+ "enabled": true,
633
+ "fix_before": "SECRET = os.environ.get(\"JWT_SECRET_KEY\", \"CHANGE-ME\")",
634
+ "fix_after": "SECRET = os.environ[\"JWT_SECRET_KEY\"] # fail fast if missing"
635
+ },
636
+ {
637
+ "id": "R030",
638
+ "name": "Typosquatted package import",
639
+ "languages": [
640
+ "python",
641
+ "javascript"
642
+ ],
643
+ "severity": "high",
644
+ "category": "supply-chain",
645
+ "description": "Import of a package whose name closely resembles a popular package (typosquatting).",
646
+ "detection": "regex",
647
+ "patterns": [
648
+ "\\bimport\\s+(reqeusts|requsets|requsts|requets|rquests)\\b",
649
+ "\\bfrom\\s+(reqeusts|requsets|requsts|requets|rquests)\\b",
650
+ "\\bimport\\s+(numpys|numppy|nunpy|numpi|nunmpy)\\b",
651
+ "\\bfrom\\s+(numpys|numppy|nunpy|numpi|nunmpy)\\b",
652
+ "\\bimport\\s+(panadas|pandass|pandas1|pands)\\b",
653
+ "\\bfrom\\s+(panadas|pandass|pandas1|pands)\\b",
654
+ "\\bimport\\s+(djagno|django1|djnago|djangoo)\\b",
655
+ "\\bfrom\\s+(djagno|django1|djnago|djangoo)\\b",
656
+ "require\\s*\\(\\s*['\\\"](reqeusts|requsets|requsts|requets|rquests)['\\\"]",
657
+ "require\\s*\\(\\s*['\\\"](expresss|expres|exrpess|exppress)['\\\"]",
658
+ "require\\s*\\(\\s*['\\\"](lodahs|lodas|loadsh|lodsah)['\\\"]"
659
+ ],
660
+ "message": "Typosquatted package import detected.",
661
+ "remediation": "Verify the package name. The intended package may be spelled differently.",
662
+ "references": [
663
+ "CWE-1357"
664
+ ],
665
+ "confidence": "high",
666
+ "enabled": true
667
+ },
668
+ {
669
+ "id": "R031",
670
+ "name": "TODO / FIXME / HACK in new code",
671
+ "languages": [
672
+ "python",
673
+ "javascript"
674
+ ],
675
+ "severity": "medium",
676
+ "category": "quality",
677
+ "description": "Developer-marker comment left in code. Common in AI-generated output that is committed without review.",
678
+ "detection": "regex",
679
+ "patterns": [
680
+ "#\\s*(TODO|FIXME|HACK|XXX)\\b[^\\n]*",
681
+ "//\\s*(TODO|FIXME|HACK|XXX)\\b[^\\n]*",
682
+ "/\\*\\s*(TODO|FIXME|HACK|XXX)\\b"
683
+ ],
684
+ "message": "TODO/FIXME/HACK marker in code.",
685
+ "remediation": "Address the item or remove the marker before committing.",
686
+ "references": [],
687
+ "confidence": "high",
688
+ "enabled": true
689
+ }
690
+ ]
691
+ }