flagrix 0.1.0 → 0.1.2

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": "2025.01.26.001",
3
- "lastUpdated": "2026-07-04T18:31:51.928Z",
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": "high"
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
- "id": "OBFUSCATED_CODE_EVAL_DYNAMIC",
235
- "name": "Dynamic Code Execution",
236
- "pattern": "eval\\s*\\([^)]{0,200}\\)|new\\s+Function\\s*\\([^)]{0,200}\\)|setTimeout\\s*\\(\\s*[\"'`][^\"'`]{0,200}[\"'`]|setInterval\\s*\\(\\s*[\"'`][^\"'`]{0,200}[\"'`]",
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": [],
@@ -361,6 +361,91 @@ function collectEvidence(content, patterns, max = MAX_EVIDENCE_LINES) {
361
361
  return evidence;
362
362
  }
363
363
 
364
+ // node_modules/@flagrix/scanner-core/dist/utils/mask.js
365
+ var REGEX_ALLOWED_AFTER_CHAR = /* @__PURE__ */ new Set([..."=(:,[!&|?{};+*%^<>~"]);
366
+ var REGEX_ALLOWED_AFTER_WORD = /* @__PURE__ */ new Set(["return", "case", "typeof", "in", "of", "do", "void", "delete"]);
367
+ function maskRegexLiterals(content) {
368
+ return content.split("\n").map(maskLine).join("\n");
369
+ }
370
+ function maskLine(line) {
371
+ let out = "";
372
+ let i = 0;
373
+ while (i < line.length) {
374
+ const ch = line[i];
375
+ if (ch === '"' || ch === "'" || ch === "`") {
376
+ const end = stringEnd(line, i);
377
+ out += line.slice(i, end);
378
+ i = end;
379
+ continue;
380
+ }
381
+ if (ch === "/" && line[i + 1] === "/") {
382
+ out += line.slice(i);
383
+ break;
384
+ }
385
+ if (ch === "/" && regexCanStartAfter(out)) {
386
+ const end = regexEnd(line, i);
387
+ if (end !== -1) {
388
+ out += " ".repeat(end - i);
389
+ i = end;
390
+ continue;
391
+ }
392
+ }
393
+ out += ch;
394
+ i++;
395
+ }
396
+ return out;
397
+ }
398
+ function regexCanStartAfter(before) {
399
+ const trimmed = before.trimEnd();
400
+ if (trimmed === "")
401
+ return true;
402
+ if (REGEX_ALLOWED_AFTER_CHAR.has(trimmed.slice(-1)))
403
+ return true;
404
+ const word = trimmed.match(/[A-Za-z_$][A-Za-z0-9_$]*$/)?.[0];
405
+ return word !== void 0 && REGEX_ALLOWED_AFTER_WORD.has(word);
406
+ }
407
+ function regexEnd(line, start) {
408
+ let i = start + 1;
409
+ let inClass = false;
410
+ let body = 0;
411
+ for (; i < line.length; i++) {
412
+ const ch = line[i];
413
+ if (ch === "\\") {
414
+ i++;
415
+ body++;
416
+ continue;
417
+ }
418
+ if (inClass) {
419
+ if (ch === "]")
420
+ inClass = false;
421
+ } else if (ch === "[") {
422
+ inClass = true;
423
+ } else if (ch === "/") {
424
+ if (body === 0)
425
+ return -1;
426
+ let end = i + 1;
427
+ while (end < line.length && /[a-z]/i.test(line[end]))
428
+ end++;
429
+ return end;
430
+ }
431
+ body++;
432
+ }
433
+ return -1;
434
+ }
435
+ function stringEnd(line, start) {
436
+ const quote = line[start];
437
+ let i = start + 1;
438
+ for (; i < line.length; i++) {
439
+ if (line[i] === "\\") {
440
+ i++;
441
+ continue;
442
+ }
443
+ if (line[i] === quote)
444
+ return i + 1;
445
+ }
446
+ return line.length;
447
+ }
448
+
364
449
  // node_modules/@flagrix/scanner-core/dist/github/api-error.js
365
450
  async function githubApiError(response, fallback = "GitHub API error") {
366
451
  let apiMessage = "";
@@ -441,9 +526,13 @@ function applyYaraRules(content, rules, filePath) {
441
526
  const isTest = isTestFile(filePath);
442
527
  for (const rule of rules) {
443
528
  try {
529
+ if (rule.fileExtensions && rule.fileExtensions.length > 0 && !rule.fileExtensions.some((ext) => filePath.endsWith(ext))) {
530
+ continue;
531
+ }
444
532
  const regex = new RegExp(rule.pattern, "gi");
445
533
  const matches2 = content.match(regex);
446
- if (matches2 && matches2.length > 0) {
534
+ const required = rule.minMatches && rule.minMatches > 1 ? rule.minMatches : 1;
535
+ if (matches2 && matches2.length >= required) {
447
536
  if (isTest && rule.severity !== "critical") {
448
537
  continue;
449
538
  }
@@ -535,6 +624,7 @@ async function scanGitHubRepo(repo, options) {
535
624
  }
536
625
  const maliciousPackages = options.signatures.maliciousPackages;
537
626
  const yaraRules = options.signatures.yaraRules;
627
+ const loadedRuleIds = new Set(yaraRules.map((r) => r.id));
538
628
  const fileContents = [];
539
629
  for (const file of filesToScan) {
540
630
  filesScanned++;
@@ -544,8 +634,9 @@ async function scanGitHubRepo(repo, options) {
544
634
  continue;
545
635
  const fileData = await contentResponse.json();
546
636
  const content = atob(fileData.content || "");
637
+ const matchable = /\.(?:js|ts)$/.test(file.path) ? maskRegexLiterals(content) : content;
547
638
  if (SCANNABLE_EXTENSIONS.some((ext) => file.path.endsWith(ext))) {
548
- fileContents.push({ path: file.path, content });
639
+ fileContents.push({ path: file.path, content: matchable });
549
640
  }
550
641
  if (file.path.endsWith("package.json")) {
551
642
  const pkgFindings = await scanPackageJson(content, maliciousPackages, file.path);
@@ -562,10 +653,10 @@ async function scanGitHubRepo(repo, options) {
562
653
  patternsMatched += pyFindings.length;
563
654
  }
564
655
  if (SCANNABLE_EXTENSIONS.some((ext) => file.path.endsWith(ext))) {
565
- const yaraFindings = applyYaraRules(content, yaraRules, file.path);
656
+ const yaraFindings = applyYaraRules(matchable, yaraRules, file.path);
566
657
  findings.push(...yaraFindings);
567
658
  patternsMatched += yaraFindings.length;
568
- const obfuscationFindings = detectObfuscation(content, file.path);
659
+ const obfuscationFindings = detectObfuscation(matchable, file.path, loadedRuleIds);
569
660
  findings.push(...obfuscationFindings);
570
661
  patternsMatched += obfuscationFindings.length;
571
662
  }
@@ -575,22 +666,22 @@ async function scanGitHubRepo(repo, options) {
575
666
  findings.push(...languageFindings);
576
667
  patternsMatched += languageFindings.length;
577
668
  for (const file of fileContents) {
578
- const secretFindings = detectHardcodedSecrets(file.content, file.path);
669
+ const secretFindings = detectHardcodedSecrets(file.content, file.path, loadedRuleIds);
579
670
  findings.push(...secretFindings);
580
671
  patternsMatched += secretFindings.length;
581
- const networkFindings = detectNetworkPatterns(file.content, file.path);
672
+ const networkFindings = detectNetworkPatterns(file.content, file.path, loadedRuleIds);
582
673
  findings.push(...networkFindings);
583
674
  patternsMatched += networkFindings.length;
584
- const miningFindings = detectCryptoMining(file.content, file.path);
675
+ const miningFindings = detectCryptoMining(file.content, file.path, loadedRuleIds);
585
676
  findings.push(...miningFindings);
586
677
  patternsMatched += miningFindings.length;
587
- const exfiltrationFindings = detectDataExfiltration(file.content, file.path);
678
+ const exfiltrationFindings = detectDataExfiltration(file.content, file.path, loadedRuleIds);
588
679
  findings.push(...exfiltrationFindings);
589
680
  patternsMatched += exfiltrationFindings.length;
590
- const backdoorFindings = detectBackdoors(file.content, file.path);
681
+ const backdoorFindings = detectBackdoors(file.content, file.path, loadedRuleIds);
591
682
  findings.push(...backdoorFindings);
592
683
  patternsMatched += backdoorFindings.length;
593
- const fileAccessFindings = detectSuspiciousFileAccess(file.content, file.path);
684
+ const fileAccessFindings = detectSuspiciousFileAccess(file.content, file.path, loadedRuleIds);
594
685
  findings.push(...fileAccessFindings);
595
686
  patternsMatched += fileAccessFindings.length;
596
687
  const socialEngFindings = detectSocialEngineering(file.content, file.path);
@@ -607,7 +698,8 @@ async function scanGitHubRepo(repo, options) {
607
698
  patternsMatched += supplyChainFindings.length;
608
699
  }
609
700
  }
610
- const integrityFindings = await detectCodeIntegrityIssues(fileContents, repo);
701
+ const allPaths = tree.tree.filter((i) => i.type === "blob").map((i) => i.path);
702
+ const integrityFindings = await detectCodeIntegrityIssues(fileContents, allPaths, repo);
611
703
  findings.push(...integrityFindings);
612
704
  patternsMatched += integrityFindings.length;
613
705
  const riskScore = calculateRiskScore(findings);
@@ -721,11 +813,19 @@ function scanPythonDeps(content, maliciousPackages, filePath) {
721
813
  }
722
814
  return findings;
723
815
  }
724
- function detectObfuscation(content, filePath) {
816
+ var BUILTIN_OBFUSCATION_RULE_IDS = {
817
+ base64: "OBF_BASE64_HEAVY",
818
+ hex: "OBF_HEX_STRINGS",
819
+ eval: "OBF_EVAL",
820
+ newFunction: "OBF_NEW_FUNCTION",
821
+ timerString: "OBF_SETTIMEOUT_STRING",
822
+ longLine: "OBF_LONG_LINE"
823
+ };
824
+ function detectObfuscation(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
725
825
  const findings = [];
726
826
  if (isTestFile(filePath))
727
827
  return findings;
728
- const base64Matches = content.match(/[A-Za-z0-9+/]{50,}={0,2}/g);
828
+ const base64Matches = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.base64) ? null : content.match(/[A-Za-z0-9+/]{50,}={0,2}/g);
729
829
  if (base64Matches && base64Matches.length > 5) {
730
830
  const snippet = base64Matches.slice(0, 3).join("\n") + (base64Matches.length > 3 ? "\n... and more" : "");
731
831
  findings.push({
@@ -737,7 +837,7 @@ function detectObfuscation(content, filePath) {
737
837
  codeExplanation: "\u{1F513} Multiple Base64-encoded strings found. Base64 is commonly used to hide malicious payloads, URLs, or commands from code reviewers."
738
838
  });
739
839
  }
740
- const hexMatches = content.match(/\\x[0-9a-fA-F]{2}/g);
840
+ const hexMatches = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.hex) ? null : content.match(/\\x[0-9a-fA-F]{2}/g);
741
841
  if (hexMatches && hexMatches.length > 20) {
742
842
  findings.push({
743
843
  severity: "medium",
@@ -749,11 +849,11 @@ function detectObfuscation(content, filePath) {
749
849
  });
750
850
  }
751
851
  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
- ];
852
+ { pattern: /eval\s*\([^)]{0,200}\)/gi, name: "eval", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.eval },
853
+ { pattern: /new\s+Function\s*\([^)]{0,200}\)/gi, name: "Function constructor", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.newFunction },
854
+ { pattern: /setTimeout\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setTimeout with string", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.timerString },
855
+ { pattern: /setInterval\s*\(\s*["'`][^"'`]{0,200}["'`]/gi, name: "setInterval with string", ruleId: BUILTIN_OBFUSCATION_RULE_IDS.timerString }
856
+ ].filter(({ ruleId }) => !coveredRuleIds.has(ruleId));
757
857
  for (const { pattern, name } of evalPatterns) {
758
858
  const matches2 = content.match(pattern);
759
859
  if (matches2 && matches2.length > 0) {
@@ -783,7 +883,7 @@ function detectObfuscation(content, filePath) {
783
883
  /atob\s*\(/i,
784
884
  /fromCharCode/i
785
885
  ];
786
- const lines = content.split("\n");
886
+ const lines = coveredRuleIds.has(BUILTIN_OBFUSCATION_RULE_IDS.longLine) ? [] : content.split("\n");
787
887
  const suspiciousLines = [];
788
888
  lines.forEach((line, index) => {
789
889
  if (line.length > 5e3) {
@@ -1029,23 +1129,23 @@ function detectSuspiciousPackageNames(content, filePath) {
1029
1129
  }
1030
1130
  return findings;
1031
1131
  }
1032
- function detectHardcodedSecrets(content, filePath) {
1132
+ function detectHardcodedSecrets(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1033
1133
  const findings = [];
1034
1134
  if (isTestFile(filePath))
1035
1135
  return findings;
1036
1136
  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
- ];
1137
+ { pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "API Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
1138
+ { pattern: /(?:secret[_-]?key|secret)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Secret Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
1139
+ { pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"]([^'"]{8,})['"]/gi, type: "Password", severity: "critical", ruleId: null },
1140
+ { pattern: /(?:token|auth[_-]?token)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Auth Token", severity: "critical", ruleId: "HARDCODED_API_KEY" },
1141
+ { pattern: /(?:private[_-]?key|privatekey)\s*[:=]\s*['"]([^'"]{20,})['"]/gi, type: "Private Key", severity: "critical", ruleId: "HARDCODED_API_KEY" },
1142
+ { pattern: /AKIA[0-9A-Z]{16}/g, type: "AWS Access Key", severity: "critical", ruleId: "HARDCODED_AWS_KEY" },
1143
+ { pattern: /ghp_[a-zA-Z0-9]{36}/g, type: "GitHub Personal Access Token", severity: "critical", ruleId: "HARDCODED_GITHUB_TOKEN" },
1144
+ { pattern: /gho_[a-zA-Z0-9]{36}/g, type: "GitHub OAuth Token", severity: "critical", ruleId: "HARDCODED_GITHUB_TOKEN" },
1145
+ { pattern: /sk_live_[a-zA-Z0-9]{24,}/g, type: "Stripe Live Key", severity: "critical", ruleId: "HARDCODED_STRIPE_KEY" },
1146
+ { pattern: /(?:mongodb|mongo)[+:\/\/]{0,6}[^@\s]+:[^@\s]+@/gi, type: "MongoDB Connection String", severity: "high", ruleId: "HARDCODED_DB_CONNECTION" },
1147
+ { pattern: /postgres:\/\/[^@\s]+:[^@\s]+@/gi, type: "PostgreSQL Connection String", severity: "high", ruleId: "HARDCODED_DB_CONNECTION" }
1148
+ ].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
1049
1149
  const foundSecrets = [];
1050
1150
  const matchedRegexes = [];
1051
1151
  for (const { pattern, type, severity } of secretPatterns) {
@@ -1069,7 +1169,7 @@ function detectHardcodedSecrets(content, filePath) {
1069
1169
  }
1070
1170
  return findings;
1071
1171
  }
1072
- function detectNetworkPatterns(content, filePath) {
1172
+ function detectNetworkPatterns(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1073
1173
  const findings = [];
1074
1174
  if (isTestFile(filePath))
1075
1175
  return findings;
@@ -1078,13 +1178,13 @@ function detectNetworkPatterns(content, filePath) {
1078
1178
  // dotted/number sequence (avoids flagging version strings like "1.2.3.4.5" or
1079
1179
  // impossible IPs like "999.999.999.999"), and excluding private/link-local ranges.
1080
1180
  { 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
- ];
1181
+ { pattern: /https?:\/\/discord(?:app)?\.com\/api\/webhooks\/\d+\/[a-zA-Z0-9_-]+/gi, type: "Discord Webhook", severity: "critical", ruleId: "NETWORK_DISCORD_WEBHOOK" },
1182
+ { pattern: /\d{8,10}:[a-zA-Z0-9_-]{35}/g, type: "Telegram Bot Token", severity: "high", ruleId: "NETWORK_TELEGRAM_TOKEN" },
1183
+ { pattern: /(?:pastebin\.com|hastebin\.com|paste\.ee)\/[a-zA-Z0-9]+/gi, type: "Pastebin URL", severity: "medium", ruleId: "NETWORK_PASTEBIN" },
1184
+ { pattern: /(?:bit\.ly|tinyurl\.com|t\.co)\/[a-zA-Z0-9]+/gi, type: "URL Shortener", severity: "medium", ruleId: "NETWORK_URL_SHORTENER" },
1185
+ { pattern: /https?:\/\/[a-zA-Z0-9-]+\.(?:tk|ml|ga|cf|gq|onion|xyz)(?:\/|$)/gi, type: "Suspicious Domain TLD", severity: "high", ruleId: "NETWORK_SUSPICIOUS_TLD" },
1186
+ { pattern: /https?:\/\/[a-zA-Z0-9-]+\.ngrok\.io/gi, type: "Ngrok Tunnel", severity: "medium", ruleId: "NETWORK_NGROK_TUNNEL" }
1187
+ ].filter((p) => !("ruleId" in p) || !coveredRuleIds.has(p.ruleId));
1088
1188
  for (const { pattern, type, severity } of suspiciousPatterns) {
1089
1189
  const matches2 = content.match(pattern);
1090
1190
  if (matches2 && matches2.length > 0) {
@@ -1108,8 +1208,10 @@ function detectNetworkPatterns(content, filePath) {
1108
1208
  }
1109
1209
  return findings;
1110
1210
  }
1111
- function detectCryptoMining(content, filePath) {
1211
+ function detectCryptoMining(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1112
1212
  const findings = [];
1213
+ if (coveredRuleIds.has("CRYPTO_MINER"))
1214
+ return findings;
1113
1215
  const miningPatterns = [
1114
1216
  /coinhive|coin-hive|crypto-loot|cryptoloot|jsecoin/gi,
1115
1217
  /stratum\+tcp|stratum\.|\bpool\./gi,
@@ -1142,18 +1244,18 @@ function detectCryptoMining(content, filePath) {
1142
1244
  }
1143
1245
  return findings;
1144
1246
  }
1145
- function detectDataExfiltration(content, filePath) {
1247
+ function detectDataExfiltration(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1146
1248
  const findings = [];
1147
1249
  if (isTestFile(filePath))
1148
1250
  return findings;
1149
1251
  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
- ];
1252
+ { pattern: /navigator\.clipboard\.read(?:Text)?\s*\(/gi, type: "Clipboard Access", severity: "high", ruleId: "EXFIL_CLIPBOARD" },
1253
+ { pattern: /localStorage\.getItem|sessionStorage\.getItem/gi, type: "Storage Access", severity: "medium", ruleId: null },
1254
+ { pattern: /document\.cookie/gi, type: "Cookie Access", severity: "high", ruleId: "EXFIL_COOKIE" },
1255
+ { pattern: /(?:addEventListener|on)\s*\(\s*['"](?:keydown|keypress|keyup)['"]/gi, type: "Keylogger Pattern", severity: "critical", ruleId: "EXFIL_KEYLOGGER" },
1256
+ { pattern: /(?:input|password|email)[\w-]*\.value/gi, type: "Form Data Access", severity: "medium", ruleId: null },
1257
+ { pattern: /new\s+FormData\s*\([^)]*\)[\s\S]{0,100}(?:fetch|axios|XMLHttpRequest)/gi, type: "Form Data Transmission", severity: "high", ruleId: null }
1258
+ ].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
1157
1259
  const detectedPatterns = [];
1158
1260
  const matchedRegexes = [];
1159
1261
  let highestSeverity = "medium";
@@ -1178,16 +1280,16 @@ function detectDataExfiltration(content, filePath) {
1178
1280
  }
1179
1281
  return findings;
1180
1282
  }
1181
- function detectBackdoors(content, filePath) {
1283
+ function detectBackdoors(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1182
1284
  const findings = [];
1183
1285
  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
- ];
1286
+ { pattern: /eval\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Remote Code Execution Endpoint", severity: "critical", ruleId: "BACKDOOR_RCE_ENDPOINT" },
1287
+ { pattern: /exec\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Command Injection Endpoint", severity: "critical", ruleId: "BACKDOOR_RCE_ENDPOINT" },
1288
+ { pattern: /require\s*\(\s*(?:request|req)(?:\.|\.body|\.query)/gi, type: "Dynamic Require", severity: "critical", ruleId: "BACKDOOR_DYNAMIC_REQUIRE" },
1289
+ { pattern: /new\s+Function\s*\([^)]*(?:request|req)/gi, type: "Dynamic Function from Request", severity: "critical", ruleId: null },
1290
+ { pattern: /(?:admin|debug|backdoor|shell)[\w]*\s*[:=]\s*(?:true|1|"[^"]*")\s*(?:\/\/|#)?\s*(?:TODO|FIXME|HACK)?/gi, type: "Debug/Admin Flag", severity: "high", ruleId: null },
1291
+ { pattern: /(?:password|auth)\s*(?:===?|==)\s*['"][^'"]{0,20}['"]\s*\)/gi, type: "Hardcoded Auth Bypass", severity: "critical", ruleId: "BACKDOOR_HARDCODED_AUTH" }
1292
+ ].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
1191
1293
  for (const { pattern, type, severity } of backdoorPatterns) {
1192
1294
  const matches2 = content.match(pattern);
1193
1295
  if (matches2) {
@@ -1242,17 +1344,17 @@ function detectSupplyChainRisks(content, filePath) {
1242
1344
  }
1243
1345
  return findings;
1244
1346
  }
1245
- function detectSuspiciousFileAccess(content, filePath) {
1347
+ function detectSuspiciousFileAccess(content, filePath, coveredRuleIds = /* @__PURE__ */ new Set()) {
1246
1348
  const findings = [];
1247
1349
  if (isTestFile(filePath))
1248
1350
  return findings;
1249
1351
  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
- ];
1352
+ { 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" },
1353
+ { 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" },
1354
+ { 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" },
1355
+ { pattern: /(?:fs\.write|writeFileSync|writeFile)\s*\([^)]*(?:\/etc|\/sys|\/bin|C:\\\\Windows)/gi, type: "System Directory Write", severity: "critical", ruleId: null },
1356
+ { pattern: /(?:fs\.unlink|unlinkSync|rmSync|rm\s+-rf)/gi, type: "File Deletion", severity: "medium", ruleId: null }
1357
+ ].filter(({ ruleId }) => !ruleId || !coveredRuleIds.has(ruleId));
1256
1358
  for (const { pattern, type, severity } of fileAccessPatterns) {
1257
1359
  const matches2 = content.match(pattern);
1258
1360
  if (matches2) {
@@ -1269,7 +1371,7 @@ function detectSuspiciousFileAccess(content, filePath) {
1269
1371
  }
1270
1372
  return findings;
1271
1373
  }
1272
- async function detectCodeIntegrityIssues(files, _repo) {
1374
+ async function detectCodeIntegrityIssues(files, allPaths, _repo) {
1273
1375
  const findings = [];
1274
1376
  for (const file of files) {
1275
1377
  if (file.path.endsWith(".min.js") || file.path.endsWith(".min.css"))
@@ -1291,8 +1393,9 @@ async function detectCodeIntegrityIssues(files, _repo) {
1291
1393
  }
1292
1394
  }
1293
1395
  }
1294
- const hasLicense = files.some((f) => /^LICENSE|^COPYING/i.test(f.path));
1295
- if (!hasLicense && files.length > 5) {
1396
+ const basename = (p) => p.split("/").pop() ?? p;
1397
+ const hasLicense = allPaths.some((p) => /^(LICENSE|COPYING)/i.test(basename(p)));
1398
+ if (!hasLicense && allPaths.length > 5) {
1296
1399
  findings.push({
1297
1400
  severity: "low",
1298
1401
  type: "CODE_INTEGRITY_ISSUE",
@@ -1300,8 +1403,8 @@ async function detectCodeIntegrityIssues(files, _repo) {
1300
1403
  codeExplanation: "\u{1F4C4} Missing license file. Legitimate open-source projects typically include a license. Absence may indicate a quickly assembled malicious repository."
1301
1404
  });
1302
1405
  }
1303
- const hasReadme = files.some((f) => /^README/i.test(f.path));
1304
- if (!hasReadme && files.length > 5) {
1406
+ const hasReadme = allPaths.some((p) => /^README/i.test(basename(p)));
1407
+ if (!hasReadme && allPaths.length > 5) {
1305
1408
  findings.push({
1306
1409
  severity: "low",
1307
1410
  type: "CODE_INTEGRITY_ISSUE",
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  scanGitHubRepo,
12
12
  scanGitHubUser,
13
13
  wantJson
14
- } from "./chunk-FJUDM3F7.js";
14
+ } from "./chunk-AS7DXMSG.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-S44QVYNH.js");
135
+ const { runMcp } = await import("./mcp-LS7GRB2C.js");
136
136
  return runMcp();
137
137
  }
138
138
  default:
@@ -7,7 +7,7 @@ import {
7
7
  scanGitHubRepo,
8
8
  scanGitHubUser,
9
9
  securityScore
10
- } from "./chunk-FJUDM3F7.js";
10
+ } from "./chunk-AS7DXMSG.js";
11
11
 
12
12
  // src/commands/mcp.ts
13
13
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagrix",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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": "github:flagrix-io/flagrix-scanner-core",
54
+ "@flagrix/scanner-core": "^0.1.2",
55
55
  "@types/node": "^20.0.0",
56
56
  "tsup": "^8.0.0",
57
57
  "typescript": "^5.0.0",