flagrix 0.1.0 → 0.1.1
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
3
|
-
"lastUpdated": "2026-07-
|
|
2
|
+
"version": "2026.07.08.002",
|
|
3
|
+
"lastUpdated": "2026-07-08T16:18:50.866Z",
|
|
4
4
|
"maliciousPackages": [
|
|
5
5
|
{
|
|
6
6
|
"name": "beavertail",
|
|
@@ -130,7 +130,13 @@
|
|
|
130
130
|
"loader",
|
|
131
131
|
"apt"
|
|
132
132
|
],
|
|
133
|
-
"severity": "critical"
|
|
133
|
+
"severity": "critical",
|
|
134
|
+
"fileExtensions": [
|
|
135
|
+
".js",
|
|
136
|
+
".ts",
|
|
137
|
+
".mjs",
|
|
138
|
+
".cjs"
|
|
139
|
+
]
|
|
134
140
|
},
|
|
135
141
|
{
|
|
136
142
|
"id": "BEAVERTAIL_EXFIL",
|
|
@@ -143,7 +149,13 @@
|
|
|
143
149
|
"exfiltration",
|
|
144
150
|
"apt"
|
|
145
151
|
],
|
|
146
|
-
"severity": "critical"
|
|
152
|
+
"severity": "critical",
|
|
153
|
+
"fileExtensions": [
|
|
154
|
+
".js",
|
|
155
|
+
".ts",
|
|
156
|
+
".mjs",
|
|
157
|
+
".cjs"
|
|
158
|
+
]
|
|
147
159
|
},
|
|
148
160
|
{
|
|
149
161
|
"id": "INVISIBLEFERRET_KEYLOG",
|
|
@@ -156,7 +168,10 @@
|
|
|
156
168
|
"keylogger",
|
|
157
169
|
"apt"
|
|
158
170
|
],
|
|
159
|
-
"severity": "critical"
|
|
171
|
+
"severity": "critical",
|
|
172
|
+
"fileExtensions": [
|
|
173
|
+
".py"
|
|
174
|
+
]
|
|
160
175
|
},
|
|
161
176
|
{
|
|
162
177
|
"id": "REVERSE_SHELL",
|
|
@@ -168,7 +183,12 @@
|
|
|
168
183
|
"backdoor",
|
|
169
184
|
"remote-access"
|
|
170
185
|
],
|
|
171
|
-
"severity": "critical"
|
|
186
|
+
"severity": "critical",
|
|
187
|
+
"fileExtensions": [
|
|
188
|
+
".js",
|
|
189
|
+
".ts",
|
|
190
|
+
".py"
|
|
191
|
+
]
|
|
172
192
|
},
|
|
173
193
|
{
|
|
174
194
|
"id": "CRYPTO_MINER",
|
|
@@ -180,7 +200,13 @@
|
|
|
180
200
|
"crypto",
|
|
181
201
|
"resource-abuse"
|
|
182
202
|
],
|
|
183
|
-
"severity": "
|
|
203
|
+
"severity": "critical",
|
|
204
|
+
"fileExtensions": [
|
|
205
|
+
".js",
|
|
206
|
+
".ts",
|
|
207
|
+
".py",
|
|
208
|
+
".sh"
|
|
209
|
+
]
|
|
184
210
|
},
|
|
185
211
|
{
|
|
186
212
|
"id": "CREDENTIAL_THEFT",
|
|
@@ -192,7 +218,13 @@
|
|
|
192
218
|
"theft",
|
|
193
219
|
"stealer"
|
|
194
220
|
],
|
|
195
|
-
"severity": "critical"
|
|
221
|
+
"severity": "critical",
|
|
222
|
+
"fileExtensions": [
|
|
223
|
+
".js",
|
|
224
|
+
".ts",
|
|
225
|
+
".py",
|
|
226
|
+
".sh"
|
|
227
|
+
]
|
|
196
228
|
},
|
|
197
229
|
{
|
|
198
230
|
"id": "ENV_EXFIL_WEBHOOK",
|
|
@@ -204,7 +236,12 @@
|
|
|
204
236
|
"webhook",
|
|
205
237
|
"secrets"
|
|
206
238
|
],
|
|
207
|
-
"severity": "high"
|
|
239
|
+
"severity": "high",
|
|
240
|
+
"fileExtensions": [
|
|
241
|
+
".js",
|
|
242
|
+
".ts",
|
|
243
|
+
".py"
|
|
244
|
+
]
|
|
208
245
|
},
|
|
209
246
|
{
|
|
210
247
|
"id": "DISCORD_WEBHOOK_EXFIL",
|
|
@@ -216,7 +253,12 @@
|
|
|
216
253
|
"discord",
|
|
217
254
|
"webhook"
|
|
218
255
|
],
|
|
219
|
-
"severity": "high"
|
|
256
|
+
"severity": "high",
|
|
257
|
+
"fileExtensions": [
|
|
258
|
+
".js",
|
|
259
|
+
".ts",
|
|
260
|
+
".py"
|
|
261
|
+
]
|
|
220
262
|
},
|
|
221
263
|
{
|
|
222
264
|
"id": "OBFUSCATED_EVAL",
|
|
@@ -228,19 +270,12 @@
|
|
|
228
270
|
"eval",
|
|
229
271
|
"payload"
|
|
230
272
|
],
|
|
231
|
-
"severity": "high"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
"description": "eval(), new Function(), or setTimeout/setInterval with string argument — executes arbitrary dynamic code",
|
|
238
|
-
"tags": [
|
|
239
|
-
"obfuscation",
|
|
240
|
-
"eval",
|
|
241
|
-
"dynamic-code"
|
|
242
|
-
],
|
|
243
|
-
"severity": "high"
|
|
273
|
+
"severity": "high",
|
|
274
|
+
"fileExtensions": [
|
|
275
|
+
".js",
|
|
276
|
+
".ts",
|
|
277
|
+
".mjs"
|
|
278
|
+
]
|
|
244
279
|
},
|
|
245
280
|
{
|
|
246
281
|
"id": "HARDCODED_AWS_KEY",
|
|
@@ -252,7 +287,17 @@
|
|
|
252
287
|
"aws",
|
|
253
288
|
"credentials"
|
|
254
289
|
],
|
|
255
|
-
"severity": "critical"
|
|
290
|
+
"severity": "critical",
|
|
291
|
+
"fileExtensions": [
|
|
292
|
+
".js",
|
|
293
|
+
".ts",
|
|
294
|
+
".py",
|
|
295
|
+
".sh",
|
|
296
|
+
".env",
|
|
297
|
+
".json",
|
|
298
|
+
".yaml",
|
|
299
|
+
".yml"
|
|
300
|
+
]
|
|
256
301
|
},
|
|
257
302
|
{
|
|
258
303
|
"id": "HARDCODED_GITHUB_TOKEN",
|
|
@@ -264,7 +309,15 @@
|
|
|
264
309
|
"github",
|
|
265
310
|
"credentials"
|
|
266
311
|
],
|
|
267
|
-
"severity": "critical"
|
|
312
|
+
"severity": "critical",
|
|
313
|
+
"fileExtensions": [
|
|
314
|
+
".js",
|
|
315
|
+
".ts",
|
|
316
|
+
".py",
|
|
317
|
+
".sh",
|
|
318
|
+
".yaml",
|
|
319
|
+
".yml"
|
|
320
|
+
]
|
|
268
321
|
},
|
|
269
322
|
{
|
|
270
323
|
"id": "HARDCODED_STRIPE_KEY",
|
|
@@ -276,7 +329,12 @@
|
|
|
276
329
|
"stripe",
|
|
277
330
|
"credentials"
|
|
278
331
|
],
|
|
279
|
-
"severity": "critical"
|
|
332
|
+
"severity": "critical",
|
|
333
|
+
"fileExtensions": [
|
|
334
|
+
".js",
|
|
335
|
+
".ts",
|
|
336
|
+
".py"
|
|
337
|
+
]
|
|
280
338
|
},
|
|
281
339
|
{
|
|
282
340
|
"id": "HARDCODED_API_KEY",
|
|
@@ -288,7 +346,12 @@
|
|
|
288
346
|
"credentials",
|
|
289
347
|
"api-key"
|
|
290
348
|
],
|
|
291
|
-
"severity": "critical"
|
|
349
|
+
"severity": "critical",
|
|
350
|
+
"fileExtensions": [
|
|
351
|
+
".js",
|
|
352
|
+
".ts",
|
|
353
|
+
".py"
|
|
354
|
+
]
|
|
292
355
|
},
|
|
293
356
|
{
|
|
294
357
|
"id": "HARDCODED_DB_CONNECTION",
|
|
@@ -300,7 +363,13 @@
|
|
|
300
363
|
"database",
|
|
301
364
|
"credentials"
|
|
302
365
|
],
|
|
303
|
-
"severity": "high"
|
|
366
|
+
"severity": "high",
|
|
367
|
+
"fileExtensions": [
|
|
368
|
+
".js",
|
|
369
|
+
".ts",
|
|
370
|
+
".py",
|
|
371
|
+
".env"
|
|
372
|
+
]
|
|
304
373
|
},
|
|
305
374
|
{
|
|
306
375
|
"id": "NETWORK_DISCORD_WEBHOOK",
|
|
@@ -312,7 +381,13 @@
|
|
|
312
381
|
"discord",
|
|
313
382
|
"exfiltration"
|
|
314
383
|
],
|
|
315
|
-
"severity": "critical"
|
|
384
|
+
"severity": "critical",
|
|
385
|
+
"fileExtensions": [
|
|
386
|
+
".js",
|
|
387
|
+
".ts",
|
|
388
|
+
".py",
|
|
389
|
+
".sh"
|
|
390
|
+
]
|
|
316
391
|
},
|
|
317
392
|
{
|
|
318
393
|
"id": "NETWORK_TELEGRAM_TOKEN",
|
|
@@ -324,7 +399,13 @@
|
|
|
324
399
|
"telegram",
|
|
325
400
|
"c2"
|
|
326
401
|
],
|
|
327
|
-
"severity": "high"
|
|
402
|
+
"severity": "high",
|
|
403
|
+
"fileExtensions": [
|
|
404
|
+
".js",
|
|
405
|
+
".ts",
|
|
406
|
+
".py",
|
|
407
|
+
".sh"
|
|
408
|
+
]
|
|
328
409
|
},
|
|
329
410
|
{
|
|
330
411
|
"id": "NETWORK_PASTEBIN",
|
|
@@ -336,7 +417,13 @@
|
|
|
336
417
|
"pastebin",
|
|
337
418
|
"payload-hosting"
|
|
338
419
|
],
|
|
339
|
-
"severity": "medium"
|
|
420
|
+
"severity": "medium",
|
|
421
|
+
"fileExtensions": [
|
|
422
|
+
".js",
|
|
423
|
+
".ts",
|
|
424
|
+
".py",
|
|
425
|
+
".sh"
|
|
426
|
+
]
|
|
340
427
|
},
|
|
341
428
|
{
|
|
342
429
|
"id": "NETWORK_URL_SHORTENER",
|
|
@@ -348,7 +435,13 @@
|
|
|
348
435
|
"url-shortener",
|
|
349
436
|
"obfuscation"
|
|
350
437
|
],
|
|
351
|
-
"severity": "medium"
|
|
438
|
+
"severity": "medium",
|
|
439
|
+
"fileExtensions": [
|
|
440
|
+
".js",
|
|
441
|
+
".ts",
|
|
442
|
+
".py",
|
|
443
|
+
".sh"
|
|
444
|
+
]
|
|
352
445
|
},
|
|
353
446
|
{
|
|
354
447
|
"id": "NETWORK_SUSPICIOUS_TLD",
|
|
@@ -360,7 +453,13 @@
|
|
|
360
453
|
"suspicious-domain",
|
|
361
454
|
"malware-hosting"
|
|
362
455
|
],
|
|
363
|
-
"severity": "high"
|
|
456
|
+
"severity": "high",
|
|
457
|
+
"fileExtensions": [
|
|
458
|
+
".js",
|
|
459
|
+
".ts",
|
|
460
|
+
".py",
|
|
461
|
+
".sh"
|
|
462
|
+
]
|
|
364
463
|
},
|
|
365
464
|
{
|
|
366
465
|
"id": "NETWORK_NGROK_TUNNEL",
|
|
@@ -372,7 +471,13 @@
|
|
|
372
471
|
"ngrok",
|
|
373
472
|
"c2"
|
|
374
473
|
],
|
|
375
|
-
"severity": "medium"
|
|
474
|
+
"severity": "medium",
|
|
475
|
+
"fileExtensions": [
|
|
476
|
+
".js",
|
|
477
|
+
".ts",
|
|
478
|
+
".py",
|
|
479
|
+
".sh"
|
|
480
|
+
]
|
|
376
481
|
},
|
|
377
482
|
{
|
|
378
483
|
"id": "EXFIL_CLIPBOARD",
|
|
@@ -384,7 +489,11 @@
|
|
|
384
489
|
"clipboard",
|
|
385
490
|
"data-theft"
|
|
386
491
|
],
|
|
387
|
-
"severity": "high"
|
|
492
|
+
"severity": "high",
|
|
493
|
+
"fileExtensions": [
|
|
494
|
+
".js",
|
|
495
|
+
".ts"
|
|
496
|
+
]
|
|
388
497
|
},
|
|
389
498
|
{
|
|
390
499
|
"id": "EXFIL_COOKIE",
|
|
@@ -396,7 +505,11 @@
|
|
|
396
505
|
"cookies",
|
|
397
506
|
"session-theft"
|
|
398
507
|
],
|
|
399
|
-
"severity": "high"
|
|
508
|
+
"severity": "high",
|
|
509
|
+
"fileExtensions": [
|
|
510
|
+
".js",
|
|
511
|
+
".ts"
|
|
512
|
+
]
|
|
400
513
|
},
|
|
401
514
|
{
|
|
402
515
|
"id": "EXFIL_KEYLOGGER",
|
|
@@ -408,7 +521,11 @@
|
|
|
408
521
|
"keylogger",
|
|
409
522
|
"credential-theft"
|
|
410
523
|
],
|
|
411
|
-
"severity": "critical"
|
|
524
|
+
"severity": "critical",
|
|
525
|
+
"fileExtensions": [
|
|
526
|
+
".js",
|
|
527
|
+
".ts"
|
|
528
|
+
]
|
|
412
529
|
},
|
|
413
530
|
{
|
|
414
531
|
"id": "BACKDOOR_RCE_ENDPOINT",
|
|
@@ -420,7 +537,12 @@
|
|
|
420
537
|
"rce",
|
|
421
538
|
"critical"
|
|
422
539
|
],
|
|
423
|
-
"severity": "critical"
|
|
540
|
+
"severity": "critical",
|
|
541
|
+
"fileExtensions": [
|
|
542
|
+
".js",
|
|
543
|
+
".ts",
|
|
544
|
+
".py"
|
|
545
|
+
]
|
|
424
546
|
},
|
|
425
547
|
{
|
|
426
548
|
"id": "BACKDOOR_DYNAMIC_REQUIRE",
|
|
@@ -432,7 +554,11 @@
|
|
|
432
554
|
"rce",
|
|
433
555
|
"dynamic-require"
|
|
434
556
|
],
|
|
435
|
-
"severity": "critical"
|
|
557
|
+
"severity": "critical",
|
|
558
|
+
"fileExtensions": [
|
|
559
|
+
".js",
|
|
560
|
+
".ts"
|
|
561
|
+
]
|
|
436
562
|
},
|
|
437
563
|
{
|
|
438
564
|
"id": "BACKDOOR_HARDCODED_AUTH",
|
|
@@ -444,7 +570,12 @@
|
|
|
444
570
|
"auth-bypass",
|
|
445
571
|
"hardcoded"
|
|
446
572
|
],
|
|
447
|
-
"severity": "critical"
|
|
573
|
+
"severity": "critical",
|
|
574
|
+
"fileExtensions": [
|
|
575
|
+
".js",
|
|
576
|
+
".ts",
|
|
577
|
+
".py"
|
|
578
|
+
]
|
|
448
579
|
},
|
|
449
580
|
{
|
|
450
581
|
"id": "FILE_ACCESS_CREDENTIALS",
|
|
@@ -457,7 +588,12 @@
|
|
|
457
588
|
"ssh",
|
|
458
589
|
"aws"
|
|
459
590
|
],
|
|
460
|
-
"severity": "critical"
|
|
591
|
+
"severity": "critical",
|
|
592
|
+
"fileExtensions": [
|
|
593
|
+
".js",
|
|
594
|
+
".ts",
|
|
595
|
+
".py"
|
|
596
|
+
]
|
|
461
597
|
},
|
|
462
598
|
{
|
|
463
599
|
"id": "FILE_ACCESS_SYSTEM",
|
|
@@ -469,7 +605,12 @@
|
|
|
469
605
|
"system-files",
|
|
470
606
|
"privilege-escalation"
|
|
471
607
|
],
|
|
472
|
-
"severity": "critical"
|
|
608
|
+
"severity": "critical",
|
|
609
|
+
"fileExtensions": [
|
|
610
|
+
".js",
|
|
611
|
+
".ts",
|
|
612
|
+
".py"
|
|
613
|
+
]
|
|
473
614
|
},
|
|
474
615
|
{
|
|
475
616
|
"id": "FILE_ACCESS_BROWSER_DATA",
|
|
@@ -481,7 +622,12 @@
|
|
|
481
622
|
"browser-data",
|
|
482
623
|
"credential-theft"
|
|
483
624
|
],
|
|
484
|
-
"severity": "critical"
|
|
625
|
+
"severity": "critical",
|
|
626
|
+
"fileExtensions": [
|
|
627
|
+
".js",
|
|
628
|
+
".ts",
|
|
629
|
+
".py"
|
|
630
|
+
]
|
|
485
631
|
},
|
|
486
632
|
{
|
|
487
633
|
"id": "SOCIAL_ENGINEERING_SECURITY_BYPASS",
|
|
@@ -503,7 +649,15 @@
|
|
|
503
649
|
"base64",
|
|
504
650
|
"obfuscation"
|
|
505
651
|
],
|
|
506
|
-
"severity": "medium"
|
|
652
|
+
"severity": "medium",
|
|
653
|
+
"minMatches": 6,
|
|
654
|
+
"fileExtensions": [
|
|
655
|
+
".js",
|
|
656
|
+
".ts",
|
|
657
|
+
".py",
|
|
658
|
+
".sh",
|
|
659
|
+
".ps1"
|
|
660
|
+
]
|
|
507
661
|
},
|
|
508
662
|
{
|
|
509
663
|
"id": "OBF_HEX_STRINGS",
|
|
@@ -514,7 +668,13 @@
|
|
|
514
668
|
"hex-encoding",
|
|
515
669
|
"obfuscation"
|
|
516
670
|
],
|
|
517
|
-
"severity": "medium"
|
|
671
|
+
"severity": "medium",
|
|
672
|
+
"minMatches": 20,
|
|
673
|
+
"fileExtensions": [
|
|
674
|
+
".js",
|
|
675
|
+
".ts",
|
|
676
|
+
".py"
|
|
677
|
+
]
|
|
518
678
|
},
|
|
519
679
|
{
|
|
520
680
|
"id": "OBF_EVAL",
|
|
@@ -526,7 +686,12 @@
|
|
|
526
686
|
"dynamic-code",
|
|
527
687
|
"obfuscation"
|
|
528
688
|
],
|
|
529
|
-
"severity": "high"
|
|
689
|
+
"severity": "high",
|
|
690
|
+
"fileExtensions": [
|
|
691
|
+
".js",
|
|
692
|
+
".ts",
|
|
693
|
+
".mjs"
|
|
694
|
+
]
|
|
530
695
|
},
|
|
531
696
|
{
|
|
532
697
|
"id": "OBF_NEW_FUNCTION",
|
|
@@ -538,7 +703,11 @@
|
|
|
538
703
|
"dynamic-code",
|
|
539
704
|
"obfuscation"
|
|
540
705
|
],
|
|
541
|
-
"severity": "high"
|
|
706
|
+
"severity": "high",
|
|
707
|
+
"fileExtensions": [
|
|
708
|
+
".js",
|
|
709
|
+
".ts"
|
|
710
|
+
]
|
|
542
711
|
},
|
|
543
712
|
{
|
|
544
713
|
"id": "OBF_SETTIMEOUT_STRING",
|
|
@@ -550,7 +719,11 @@
|
|
|
550
719
|
"dynamic-code",
|
|
551
720
|
"obfuscation"
|
|
552
721
|
],
|
|
553
|
-
"severity": "high"
|
|
722
|
+
"severity": "high",
|
|
723
|
+
"fileExtensions": [
|
|
724
|
+
".js",
|
|
725
|
+
".ts"
|
|
726
|
+
]
|
|
554
727
|
},
|
|
555
728
|
{
|
|
556
729
|
"id": "OBF_FROMCHARCODE",
|
|
@@ -562,7 +735,11 @@
|
|
|
562
735
|
"obfuscation",
|
|
563
736
|
"eval-bypass"
|
|
564
737
|
],
|
|
565
|
-
"severity": "medium"
|
|
738
|
+
"severity": "medium",
|
|
739
|
+
"fileExtensions": [
|
|
740
|
+
".js",
|
|
741
|
+
".ts"
|
|
742
|
+
]
|
|
566
743
|
},
|
|
567
744
|
{
|
|
568
745
|
"id": "OBF_CHARCODE_ARRAY",
|
|
@@ -573,7 +750,11 @@
|
|
|
573
750
|
"charcode-array",
|
|
574
751
|
"obfuscation"
|
|
575
752
|
],
|
|
576
|
-
"severity": "medium"
|
|
753
|
+
"severity": "medium",
|
|
754
|
+
"fileExtensions": [
|
|
755
|
+
".js",
|
|
756
|
+
".ts"
|
|
757
|
+
]
|
|
577
758
|
}
|
|
578
759
|
],
|
|
579
760
|
"knownBadHashes": [],
|
|
@@ -441,9 +441,13 @@ function applyYaraRules(content, rules, filePath) {
|
|
|
441
441
|
const isTest = isTestFile(filePath);
|
|
442
442
|
for (const rule of rules) {
|
|
443
443
|
try {
|
|
444
|
+
if (rule.fileExtensions && rule.fileExtensions.length > 0 && !rule.fileExtensions.some((ext) => filePath.endsWith(ext))) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
444
447
|
const regex = new RegExp(rule.pattern, "gi");
|
|
445
448
|
const matches2 = content.match(regex);
|
|
446
|
-
|
|
449
|
+
const required = rule.minMatches && rule.minMatches > 1 ? rule.minMatches : 1;
|
|
450
|
+
if (matches2 && matches2.length >= required) {
|
|
447
451
|
if (isTest && rule.severity !== "critical") {
|
|
448
452
|
continue;
|
|
449
453
|
}
|
|
@@ -535,6 +539,7 @@ async function scanGitHubRepo(repo, options) {
|
|
|
535
539
|
}
|
|
536
540
|
const maliciousPackages = options.signatures.maliciousPackages;
|
|
537
541
|
const yaraRules = options.signatures.yaraRules;
|
|
542
|
+
const loadedRuleIds = new Set(yaraRules.map((r) => r.id));
|
|
538
543
|
const fileContents = [];
|
|
539
544
|
for (const file of filesToScan) {
|
|
540
545
|
filesScanned++;
|
|
@@ -565,7 +570,7 @@ async function scanGitHubRepo(repo, options) {
|
|
|
565
570
|
const yaraFindings = applyYaraRules(content, yaraRules, file.path);
|
|
566
571
|
findings.push(...yaraFindings);
|
|
567
572
|
patternsMatched += yaraFindings.length;
|
|
568
|
-
const obfuscationFindings = detectObfuscation(content, file.path);
|
|
573
|
+
const obfuscationFindings = detectObfuscation(content, file.path, loadedRuleIds);
|
|
569
574
|
findings.push(...obfuscationFindings);
|
|
570
575
|
patternsMatched += obfuscationFindings.length;
|
|
571
576
|
}
|
|
@@ -575,22 +580,22 @@ async function scanGitHubRepo(repo, options) {
|
|
|
575
580
|
findings.push(...languageFindings);
|
|
576
581
|
patternsMatched += languageFindings.length;
|
|
577
582
|
for (const file of fileContents) {
|
|
578
|
-
const secretFindings = detectHardcodedSecrets(file.content, file.path);
|
|
583
|
+
const secretFindings = detectHardcodedSecrets(file.content, file.path, loadedRuleIds);
|
|
579
584
|
findings.push(...secretFindings);
|
|
580
585
|
patternsMatched += secretFindings.length;
|
|
581
|
-
const networkFindings = detectNetworkPatterns(file.content, file.path);
|
|
586
|
+
const networkFindings = detectNetworkPatterns(file.content, file.path, loadedRuleIds);
|
|
582
587
|
findings.push(...networkFindings);
|
|
583
588
|
patternsMatched += networkFindings.length;
|
|
584
|
-
const miningFindings = detectCryptoMining(file.content, file.path);
|
|
589
|
+
const miningFindings = detectCryptoMining(file.content, file.path, loadedRuleIds);
|
|
585
590
|
findings.push(...miningFindings);
|
|
586
591
|
patternsMatched += miningFindings.length;
|
|
587
|
-
const exfiltrationFindings = detectDataExfiltration(file.content, file.path);
|
|
592
|
+
const exfiltrationFindings = detectDataExfiltration(file.content, file.path, loadedRuleIds);
|
|
588
593
|
findings.push(...exfiltrationFindings);
|
|
589
594
|
patternsMatched += exfiltrationFindings.length;
|
|
590
|
-
const backdoorFindings = detectBackdoors(file.content, file.path);
|
|
595
|
+
const backdoorFindings = detectBackdoors(file.content, file.path, loadedRuleIds);
|
|
591
596
|
findings.push(...backdoorFindings);
|
|
592
597
|
patternsMatched += backdoorFindings.length;
|
|
593
|
-
const fileAccessFindings = detectSuspiciousFileAccess(file.content, file.path);
|
|
598
|
+
const fileAccessFindings = detectSuspiciousFileAccess(file.content, file.path, loadedRuleIds);
|
|
594
599
|
findings.push(...fileAccessFindings);
|
|
595
600
|
patternsMatched += fileAccessFindings.length;
|
|
596
601
|
const socialEngFindings = detectSocialEngineering(file.content, file.path);
|
|
@@ -721,11 +726,19 @@ function scanPythonDeps(content, maliciousPackages, filePath) {
|
|
|
721
726
|
}
|
|
722
727
|
return findings;
|
|
723
728
|
}
|
|
724
|
-
|
|
729
|
+
var BUILTIN_OBFUSCATION_RULE_IDS = {
|
|
730
|
+
base64: "OBF_BASE64_HEAVY",
|
|
731
|
+
hex: "OBF_HEX_STRINGS",
|
|
732
|
+
eval: "OBF_EVAL",
|
|
733
|
+
newFunction: "OBF_NEW_FUNCTION",
|
|
734
|
+
timerString: "OBF_SETTIMEOUT_STRING",
|
|
735
|
+
longLine: "OBF_LONG_LINE"
|
|
736
|
+
};
|
|
737
|
+
function detectObfuscation(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
725
738
|
const findings = [];
|
|
726
739
|
if (isTestFile(filePath))
|
|
727
740
|
return findings;
|
|
728
|
-
const base64Matches = content.match(/[A-Za-z0-9+/]{50,}={0,2}/g);
|
|
741
|
+
const base64Matches = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.base64) ? null : content.match(/[A-Za-z0-9+/]{50,}={0,2}/g);
|
|
729
742
|
if (base64Matches && base64Matches.length > 5) {
|
|
730
743
|
const snippet = base64Matches.slice(0, 3).join("\n") + (base64Matches.length > 3 ? "\n... and more" : "");
|
|
731
744
|
findings.push({
|
|
@@ -737,7 +750,7 @@ function detectObfuscation(content, filePath) {
|
|
|
737
750
|
codeExplanation: "\u{1F513} Multiple Base64-encoded strings found. Base64 is commonly used to hide malicious payloads, URLs, or commands from code reviewers."
|
|
738
751
|
});
|
|
739
752
|
}
|
|
740
|
-
const hexMatches = content.match(/\\x[0-9a-fA-F]{2}/g);
|
|
753
|
+
const hexMatches = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.hex) ? null : content.match(/\\x[0-9a-fA-F]{2}/g);
|
|
741
754
|
if (hexMatches && hexMatches.length > 20) {
|
|
742
755
|
findings.push({
|
|
743
756
|
severity: "medium",
|
|
@@ -749,11 +762,11 @@ function detectObfuscation(content, filePath) {
|
|
|
749
762
|
});
|
|
750
763
|
}
|
|
751
764
|
const evalPatterns = [
|
|
752
|
-
{ pattern: /eval\s*\([^)]{0,200}\)/gi, name: "eval" },
|
|
753
|
-
{ pattern: /new\s+Function\s*\([^)]{0,200}\)/gi, name: "Function constructor" },
|
|
754
|
-
{ pattern: /setTimeout\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setTimeout with string" },
|
|
755
|
-
{ pattern: /setInterval\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setInterval with string" }
|
|
756
|
-
];
|
|
765
|
+
{ pattern: /eval\s*\([^)]{0,200}\)/gi, name: "eval", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.eval },
|
|
766
|
+
{ pattern: /new\s+Function\s*\([^)]{0,200}\)/gi, name: "Function constructor", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.newFunction },
|
|
767
|
+
{ pattern: /setTimeout\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setTimeout with string", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.timerString },
|
|
768
|
+
{ pattern: /setInterval\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setInterval with string", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.timerString }
|
|
769
|
+
].filter(({ ruleId }) => !coveredRuleIds.has(ruleId));
|
|
757
770
|
for (const { pattern, name } of evalPatterns) {
|
|
758
771
|
const matches2 = content.match(pattern);
|
|
759
772
|
if (matches2 && matches2.length > 0) {
|
|
@@ -783,7 +796,7 @@ function detectObfuscation(content, filePath) {
|
|
|
783
796
|
/atob\s*\(/i,
|
|
784
797
|
/fromCharCode/i
|
|
785
798
|
];
|
|
786
|
-
const lines = content.split("\n");
|
|
799
|
+
const lines = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.longLine) ? [] : content.split("\n");
|
|
787
800
|
const suspiciousLines = [];
|
|
788
801
|
lines.forEach((line, index) => {
|
|
789
802
|
if (line.length > 5e3) {
|
|
@@ -1029,23 +1042,23 @@ function detectSuspiciousPackageNames(content, filePath) {
|
|
|
1029
1042
|
}
|
|
1030
1043
|
return findings;
|
|
1031
1044
|
}
|
|
1032
|
-
function detectHardcodedSecrets(content, filePath) {
|
|
1045
|
+
function detectHardcodedSecrets(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1033
1046
|
const findings = [];
|
|
1034
1047
|
if (isTestFile(filePath))
|
|
1035
1048
|
return findings;
|
|
1036
1049
|
const secretPatterns = [
|
|
1037
|
-
{ pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "API Key", severity: "critical" },
|
|
1038
|
-
{ pattern: /(?:secret[_-]?key|secret)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Secret Key", severity: "critical" },
|
|
1039
|
-
{ pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"]([^'"]{8,})['"]/gi, type: "Password", severity: "critical" },
|
|
1040
|
-
{ pattern: /(?:token|auth[_-]?token)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Auth Token", severity: "critical" },
|
|
1041
|
-
{ pattern: /(?:private[_-]?key|privatekey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Private Key", severity: "critical" },
|
|
1042
|
-
{ pattern: /AKIA[0-9A-Z]{16}/g, type: "AWS Access Key", severity: "critical" },
|
|
1043
|
-
{ pattern: /ghp_[a-zA-Z0-9]{36}/g, type: "GitHub Personal Access Token", severity: "critical" },
|
|
1044
|
-
{ pattern: /gho_[a-zA-Z0-9]{36}/g, type: "GitHub OAuth Token", severity: "critical" },
|
|
1045
|
-
{ pattern: /sk_live_[a-zA-Z0-9]{24,}/g, type: "Stripe Live Key", severity: "critical" },
|
|
1046
|
-
{ pattern: /(?:mongodb|mongo)[+:\/\/]{0,6}[^@\s]+:[^@\s]+@/gi, type: "MongoDB Connection String", severity: "high" },
|
|
1047
|
-
{ pattern: /postgres:\/\/[^@\s]+:[^@\s]+@/gi, type: "PostgreSQL Connection String", severity: "high" }
|
|
1048
|
-
];
|
|
1050
|
+
{ pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "API Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
|
|
1051
|
+
{ pattern: /(?:secret[_-]?key|secret)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Secret Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
|
|
1052
|
+
{ pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"]([^'"]{8,})['"]/gi, type: "Password", severity: "critical", ruleId: null },
|
|
1053
|
+
{ pattern: /(?:token|auth[_-]?token)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Auth Token", severity: "critical", ruleId: "HARDCODED_API_KEY" },
|
|
1054
|
+
{ pattern: /(?:private[_-]?key|privatekey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Private Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
|
|
1055
|
+
{ pattern: /AKIA[0-9A-Z]{16}/g, type: "AWS Access Key", severity: "critical", ruleId: "HARDCODED_AWS_KEY" },
|
|
1056
|
+
{ pattern: /ghp_[a-zA-Z0-9]{36}/g, type: "GitHub Personal Access Token", severity: "critical", ruleId: "HARDCODED_GITHUB_TOKEN" },
|
|
1057
|
+
{ pattern: /gho_[a-zA-Z0-9]{36}/g, type: "GitHub OAuth Token", severity: "critical", ruleId: "HARDCODED_GITHUB_TOKEN" },
|
|
1058
|
+
{ pattern: /sk_live_[a-zA-Z0-9]{24,}/g, type: "Stripe Live Key", severity: "critical", ruleId: "HARDCODED_STRIPE_KEY" },
|
|
1059
|
+
{ pattern: /(?:mongodb|mongo)[+:\/\/]{0,6}[^@\s]+:[^@\s]+@/gi, type: "MongoDB Connection String", severity: "high", ruleId: "HARDCODED_DB_CONNECTION" },
|
|
1060
|
+
{ pattern: /postgres:\/\/[^@\s]+:[^@\s]+@/gi, type: "PostgreSQL Connection String", severity: "high", ruleId: "HARDCODED_DB_CONNECTION" }
|
|
1061
|
+
].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
|
|
1049
1062
|
const foundSecrets = [];
|
|
1050
1063
|
const matchedRegexes = [];
|
|
1051
1064
|
for (const { pattern, type, severity } of secretPatterns) {
|
|
@@ -1069,7 +1082,7 @@ function detectHardcodedSecrets(content, filePath) {
|
|
|
1069
1082
|
}
|
|
1070
1083
|
return findings;
|
|
1071
1084
|
}
|
|
1072
|
-
function detectNetworkPatterns(content, filePath) {
|
|
1085
|
+
function detectNetworkPatterns(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1073
1086
|
const findings = [];
|
|
1074
1087
|
if (isTestFile(filePath))
|
|
1075
1088
|
return findings;
|
|
@@ -1078,13 +1091,13 @@ function detectNetworkPatterns(content, filePath) {
|
|
|
1078
1091
|
// dotted/number sequence (avoids flagging version strings like "1.2.3.4.5" or
|
|
1079
1092
|
// impossible IPs like "999.999.999.999"), and excluding private/link-local ranges.
|
|
1080
1093
|
{ pattern: /(?:https?:\/\/)?(?<![\d.])(?!(?:127\.|10\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.))(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}(?![\d.])(?::\d{1,5})?/g, type: "Hardcoded IP Address", severity: "high" },
|
|
1081
|
-
{ pattern: /https?:\/\/discord(?:app)?\.com\/api\/webhooks\/\d+\/[a-zA-Z0-9_-]+/gi, type: "Discord Webhook", severity: "critical" },
|
|
1082
|
-
{ pattern: /\d{8,10}:[a-zA-Z0-9_-]{35}/g, type: "Telegram Bot Token", severity: "high" },
|
|
1083
|
-
{ pattern: /(?:pastebin\.com|hastebin\.com|paste\.ee)\/[a-zA-Z0-9]+/gi, type: "Pastebin URL", severity: "medium" },
|
|
1084
|
-
{ pattern: /(?:bit\.ly|tinyurl\.com|t\.co)\/[a-zA-Z0-9]+/gi, type: "URL Shortener", severity: "medium" },
|
|
1085
|
-
{ pattern: /https?:\/\/[a-zA-Z0-9-]+\.(?:tk|ml|ga|cf|gq|onion|xyz)(?:\/|$)/gi, type: "Suspicious Domain TLD", severity: "high" },
|
|
1086
|
-
{ pattern: /https?:\/\/[a-zA-Z0-9-]+\.ngrok\.io/gi, type: "Ngrok Tunnel", severity: "medium" }
|
|
1087
|
-
];
|
|
1094
|
+
{ pattern: /https?:\/\/discord(?:app)?\.com\/api\/webhooks\/\d+\/[a-zA-Z0-9_-]+/gi, type: "Discord Webhook", severity: "critical", ruleId: "NETWORK_DISCORD_WEBHOOK" },
|
|
1095
|
+
{ pattern: /\d{8,10}:[a-zA-Z0-9_-]{35}/g, type: "Telegram Bot Token", severity: "high", ruleId: "NETWORK_TELEGRAM_TOKEN" },
|
|
1096
|
+
{ pattern: /(?:pastebin\.com|hastebin\.com|paste\.ee)\/[a-zA-Z0-9]+/gi, type: "Pastebin URL", severity: "medium", ruleId: "NETWORK_PASTEBIN" },
|
|
1097
|
+
{ pattern: /(?:bit\.ly|tinyurl\.com|t\.co)\/[a-zA-Z0-9]+/gi, type: "URL Shortener", severity: "medium", ruleId: "NETWORK_URL_SHORTENER" },
|
|
1098
|
+
{ pattern: /https?:\/\/[a-zA-Z0-9-]+\.(?:tk|ml|ga|cf|gq|onion|xyz)(?:\/|$)/gi, type: "Suspicious Domain TLD", severity: "high", ruleId: "NETWORK_SUSPICIOUS_TLD" },
|
|
1099
|
+
{ pattern: /https?:\/\/[a-zA-Z0-9-]+\.ngrok\.io/gi, type: "Ngrok Tunnel", severity: "medium", ruleId: "NETWORK_NGROK_TUNNEL" }
|
|
1100
|
+
].filter((p) => !("ruleId" in p) || !coveredRuleIds.has(p.ruleId));
|
|
1088
1101
|
for (const { pattern, type, severity } of suspiciousPatterns) {
|
|
1089
1102
|
const matches2 = content.match(pattern);
|
|
1090
1103
|
if (matches2 && matches2.length > 0) {
|
|
@@ -1108,8 +1121,10 @@ function detectNetworkPatterns(content, filePath) {
|
|
|
1108
1121
|
}
|
|
1109
1122
|
return findings;
|
|
1110
1123
|
}
|
|
1111
|
-
function detectCryptoMining(content, filePath) {
|
|
1124
|
+
function detectCryptoMining(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1112
1125
|
const findings = [];
|
|
1126
|
+
if (coveredRuleIds.has("CRYPTO_MINER"))
|
|
1127
|
+
return findings;
|
|
1113
1128
|
const miningPatterns = [
|
|
1114
1129
|
/coinhive|coin-hive|crypto-loot|cryptoloot|jsecoin/gi,
|
|
1115
1130
|
/stratum\+tcp|stratum\.|\bpool\./gi,
|
|
@@ -1142,18 +1157,18 @@ function detectCryptoMining(content, filePath) {
|
|
|
1142
1157
|
}
|
|
1143
1158
|
return findings;
|
|
1144
1159
|
}
|
|
1145
|
-
function detectDataExfiltration(content, filePath) {
|
|
1160
|
+
function detectDataExfiltration(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1146
1161
|
const findings = [];
|
|
1147
1162
|
if (isTestFile(filePath))
|
|
1148
1163
|
return findings;
|
|
1149
1164
|
const exfiltrationPatterns = [
|
|
1150
|
-
{ pattern: /navigator\.clipboard\.read(?:Text)?\s*\(/gi, type: "Clipboard Access", severity: "high" },
|
|
1151
|
-
{ pattern: /localStorage\.getItem|sessionStorage\.getItem/gi, type: "Storage Access", severity: "medium" },
|
|
1152
|
-
{ pattern: /document\.cookie/gi, type: "Cookie Access", severity: "high" },
|
|
1153
|
-
{ pattern: /(?:addEventListener|on)\s*\(\s*['"](?:keydown|keypress|keyup)['"]/gi, type: "Keylogger Pattern", severity: "critical" },
|
|
1154
|
-
{ pattern: /(?:input|password|email)[\w-]*\.value/gi, type: "Form Data Access", severity: "medium" },
|
|
1155
|
-
{ pattern: /new\s+FormData\s*\([^)]*\)[\s\S]{0,100}(?:fetch|axios|XMLHttpRequest)/gi, type: "Form Data Transmission", severity: "high" }
|
|
1156
|
-
];
|
|
1165
|
+
{ pattern: /navigator\.clipboard\.read(?:Text)?\s*\(/gi, type: "Clipboard Access", severity: "high", ruleId: "EXFIL_CLIPBOARD" },
|
|
1166
|
+
{ pattern: /localStorage\.getItem|sessionStorage\.getItem/gi, type: "Storage Access", severity: "medium", ruleId: null },
|
|
1167
|
+
{ pattern: /document\.cookie/gi, type: "Cookie Access", severity: "high", ruleId: "EXFIL_COOKIE" },
|
|
1168
|
+
{ pattern: /(?:addEventListener|on)\s*\(\s*['"](?:keydown|keypress|keyup)['"]/gi, type: "Keylogger Pattern", severity: "critical", ruleId: "EXFIL_KEYLOGGER" },
|
|
1169
|
+
{ pattern: /(?:input|password|email)[\w-]*\.value/gi, type: "Form Data Access", severity: "medium", ruleId: null },
|
|
1170
|
+
{ pattern: /new\s+FormData\s*\([^)]*\)[\s\S]{0,100}(?:fetch|axios|XMLHttpRequest)/gi, type: "Form Data Transmission", severity: "high", ruleId: null }
|
|
1171
|
+
].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
|
|
1157
1172
|
const detectedPatterns = [];
|
|
1158
1173
|
const matchedRegexes = [];
|
|
1159
1174
|
let highestSeverity = "medium";
|
|
@@ -1178,16 +1193,16 @@ function detectDataExfiltration(content, filePath) {
|
|
|
1178
1193
|
}
|
|
1179
1194
|
return findings;
|
|
1180
1195
|
}
|
|
1181
|
-
function detectBackdoors(content, filePath) {
|
|
1196
|
+
function detectBackdoors(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1182
1197
|
const findings = [];
|
|
1183
1198
|
const backdoorPatterns = [
|
|
1184
|
-
{ pattern: /eval\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Remote Code Execution Endpoint", severity: "critical" },
|
|
1185
|
-
{ pattern: /exec\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Command Injection Endpoint", severity: "critical" },
|
|
1186
|
-
{ pattern: /require\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Dynamic Require", severity: "critical" },
|
|
1187
|
-
{ pattern: /new\s+Function\s*\([^)]*(?:request|req)/gi, type: "Dynamic Function from Request", severity: "critical" },
|
|
1188
|
-
{ pattern: /(?:admin|debug|backdoor|shell)[\w]*\s*[:=]\s*(?:true|1|"[^"]*")\s*(?:\/\/|#)?\s*(?:TODO|FIXME|HACK)?/gi, type: "Debug/Admin Flag", severity: "high" },
|
|
1189
|
-
{ pattern: /(?:password|auth)\s*(?:===?|==)\s*['"][^'"]{0,20}['"]\s*\)/gi, type: "Hardcoded Auth Bypass", severity: "critical" }
|
|
1190
|
-
];
|
|
1199
|
+
{ pattern: /eval\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Remote Code Execution Endpoint", severity: "critical", ruleId: "BACKDOOR_RCE_ENDPOINT" },
|
|
1200
|
+
{ pattern: /exec\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Command Injection Endpoint", severity: "critical", ruleId: "BACKDOOR_RCE_ENDPOINT" },
|
|
1201
|
+
{ pattern: /require\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Dynamic Require", severity: "critical", ruleId: "BACKDOOR_DYNAMIC_REQUIRE" },
|
|
1202
|
+
{ pattern: /new\s+Function\s*\([^)]*(?:request|req)/gi, type: "Dynamic Function from Request", severity: "critical", ruleId: null },
|
|
1203
|
+
{ pattern: /(?:admin|debug|backdoor|shell)[\w]*\s*[:=]\s*(?:true|1|"[^"]*")\s*(?:\/\/|#)?\s*(?:TODO|FIXME|HACK)?/gi, type: "Debug/Admin Flag", severity: "high", ruleId: null },
|
|
1204
|
+
{ pattern: /(?:password|auth)\s*(?:===?|==)\s*['"][^'"]{0,20}['"]\s*\)/gi, type: "Hardcoded Auth Bypass", severity: "critical", ruleId: "BACKDOOR_HARDCODED_AUTH" }
|
|
1205
|
+
].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
|
|
1191
1206
|
for (const { pattern, type, severity } of backdoorPatterns) {
|
|
1192
1207
|
const matches2 = content.match(pattern);
|
|
1193
1208
|
if (matches2) {
|
|
@@ -1242,17 +1257,17 @@ function detectSupplyChainRisks(content, filePath) {
|
|
|
1242
1257
|
}
|
|
1243
1258
|
return findings;
|
|
1244
1259
|
}
|
|
1245
|
-
function detectSuspiciousFileAccess(content, filePath) {
|
|
1260
|
+
function detectSuspiciousFileAccess(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
|
|
1246
1261
|
const findings = [];
|
|
1247
1262
|
if (isTestFile(filePath))
|
|
1248
1263
|
return findings;
|
|
1249
1264
|
const fileAccessPatterns = [
|
|
1250
|
-
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:\.ssh|\.aws|\.gnupg|\.docker|id_rsa|credentials)/gi, type: "SSH/AWS Credentials Access", severity: "critical" },
|
|
1251
|
-
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:etc\/passwd|etc\/shadow|\.bash_history|\.zsh_history)/gi, type: "System File Access", severity: "critical" },
|
|
1252
|
-
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:Chrome|Firefox|Safari|Edge)[\w\s/\\]*(?:Cookies|Login|History)/gi, type: "Browser Data Access", severity: "critical" },
|
|
1253
|
-
{ pattern: /(?:fs\.write|writeFileSync|writeFile)\s*\([^)]*(?:\/etc|\/sys|\/bin|C:\\\\Windows)/gi, type: "System Directory Write", severity: "critical" },
|
|
1254
|
-
{ pattern: /(?:fs\.unlink|unlinkSync|rmSync|rm\s+-rf)/gi, type: "File Deletion", severity: "medium" }
|
|
1255
|
-
];
|
|
1265
|
+
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:\.ssh|\.aws|\.gnupg|\.docker|id_rsa|credentials)/gi, type: "SSH/AWS Credentials Access", severity: "critical", ruleId: "FILE_ACCESS_CREDENTIALS" },
|
|
1266
|
+
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:etc\/passwd|etc\/shadow|\.bash_history|\.zsh_history)/gi, type: "System File Access", severity: "critical", ruleId: "FILE_ACCESS_SYSTEM" },
|
|
1267
|
+
{ pattern: /(?:fs\.read|readFileSync|readFile)\s*\([^)]*(?:Chrome|Firefox|Safari|Edge)[\w\s/\\]*(?:Cookies|Login|History)/gi, type: "Browser Data Access", severity: "critical", ruleId: "FILE_ACCESS_BROWSER_DATA" },
|
|
1268
|
+
{ pattern: /(?:fs\.write|writeFileSync|writeFile)\s*\([^)]*(?:\/etc|\/sys|\/bin|C:\\\\Windows)/gi, type: "System Directory Write", severity: "critical", ruleId: null },
|
|
1269
|
+
{ pattern: /(?:fs\.unlink|unlinkSync|rmSync|rm\s+-rf)/gi, type: "File Deletion", severity: "medium", ruleId: null }
|
|
1270
|
+
].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
|
|
1256
1271
|
for (const { pattern, type, severity } of fileAccessPatterns) {
|
|
1257
1272
|
const matches2 = content.match(pattern);
|
|
1258
1273
|
if (matches2) {
|
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
scanGitHubRepo,
|
|
12
12
|
scanGitHubUser,
|
|
13
13
|
wantJson
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QKP4NQM3.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import { parseArgs } from "util";
|
|
@@ -132,7 +132,7 @@ async function main() {
|
|
|
132
132
|
}
|
|
133
133
|
return runScanUser(target, { json: values.json, token: values.token });
|
|
134
134
|
case "mcp": {
|
|
135
|
-
const { runMcp } = await import("./mcp-
|
|
135
|
+
const { runMcp } = await import("./mcp-B4RQXV5T.js");
|
|
136
136
|
return runMcp();
|
|
137
137
|
}
|
|
138
138
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flagrix",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Scan GitHub repos and profiles for malware before you clone — CLI and MCP server for the Flagrix scanner",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"zod": "^3.24.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@flagrix/scanner-core": "
|
|
54
|
+
"@flagrix/scanner-core": "^0.1.0",
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"tsup": "^8.0.0",
|
|
57
57
|
"typescript": "^5.0.0",
|