muaddib-scanner 1.8.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fr.md +133 -10
- package/README.md +124 -4
- package/bin/muaddib.js +120 -5
- package/docker/sandbox-runner.sh +3 -2
- package/package.json +1 -1
- package/src/canary-tokens.js +184 -0
- package/src/index.js +227 -0
- package/src/maintainer-change.js +224 -0
- package/src/monitor.js +535 -10
- package/src/publish-anomaly.js +186 -0
- package/src/response/playbooks.js +68 -0
- package/src/rules/index.js +175 -0
- package/src/sandbox.js +62 -6
- package/src/temporal-analysis.js +260 -0
- package/src/temporal-ast-diff.js +405 -0
package/README.fr.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
Les attaques supply-chain npm et PyPI explosent. Shai-Hulud a compromis 25K+ repos en 2025. Les outils existants détectent, mais n'aident pas à répondre.
|
|
32
32
|
|
|
33
|
-
MUAD'DIB combine analyse statique + analyse dynamique (sandbox Docker) pour détecter les menaces ET guider votre réponse.
|
|
33
|
+
MUAD'DIB combine analyse statique + analyse dynamique (sandbox Docker) + **détection comportementale d'anomalies** (v2.0) pour détecter les menaces ET guider votre réponse — même avant leur apparition dans une base d'IOC.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
@@ -392,6 +392,119 @@ Détecte les patterns malveillants dans les fichiers YAML `.github/workflows/`,
|
|
|
392
392
|
| Supply chain compromise | T1195.002 | IOC matching |
|
|
393
393
|
| Package PyPI malveillant | T1195.002 | IOC matching |
|
|
394
394
|
| Sandbox analyse dynamique | Multiple | Docker + strace + tcpdump |
|
|
395
|
+
| Ajout soudain de script lifecycle | T1195.002 | Analyse temporelle |
|
|
396
|
+
| Injection d'API dangereuse entre versions | T1195.002 | Diff AST temporel |
|
|
397
|
+
| Anomalie de fréquence de publication | T1195.002 | Métadonnées registre |
|
|
398
|
+
| Changement de maintainer/publisher | T1195.002 | Métadonnées registre |
|
|
399
|
+
| Exfiltration de canary tokens | T1552.001 | Honey tokens sandbox |
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Détection d'anomalies supply chain (v2.0)
|
|
404
|
+
|
|
405
|
+
MUAD'DIB 2.0 introduit un changement de paradigme : de la **détection par IOC** (réactive, nécessite des menaces connues) à la **détection comportementale d'anomalies** (proactive, détecte les menaces inconnues en repérant les changements suspects).
|
|
406
|
+
|
|
407
|
+
Les scanners supply-chain traditionnels reposent sur des listes de packages malveillants connus. Le problème : ils ne détectent les menaces qu'APRÈS leur identification et signalement. Des attaques comme **ua-parser-js** (2021), **event-stream** (2018) et **Shai-Hulud** (2025) sont passées inaperçues pendant des heures ou des jours car aucun IOC n'existait encore.
|
|
408
|
+
|
|
409
|
+
MUAD'DIB 2.0 ajoute 5 features de détection comportementale capables d'attraper ces attaques **avant** leur apparition dans une base d'IOC, en analysant ce qui a changé entre les versions d'un package.
|
|
410
|
+
|
|
411
|
+
### Nouvelles features
|
|
412
|
+
|
|
413
|
+
#### 1. Détection soudaine de scripts lifecycle (`--temporal`)
|
|
414
|
+
|
|
415
|
+
Détecte quand des scripts `preinstall`, `install` ou `postinstall` apparaissent soudainement dans une nouvelle version d'un package qui n'en avait jamais. C'est le vecteur d'attaque #1 des attaques supply-chain.
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
muaddib scan . --temporal
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### 2. Diff AST temporel (`--temporal-ast`)
|
|
422
|
+
|
|
423
|
+
Télécharge les deux dernières versions de chaque dépendance et compare leur AST (Abstract Syntax Tree) pour détecter les APIs dangereuses nouvellement ajoutées : `child_process`, `eval`, `Function`, `net.connect`, `process.env`, `fetch`, etc.
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
muaddib scan . --temporal-ast
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### 3. Anomalie de fréquence de publication (`--temporal-publish`)
|
|
430
|
+
|
|
431
|
+
Détecte les patterns de publication anormaux : rafale de versions en 24h, package dormant soudainement mis à jour après 6+ mois, succession rapide de versions (plusieurs releases en moins d'1h).
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
muaddib scan . --temporal-publish
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### 4. Détection de changement de maintainer (`--temporal-maintainer`)
|
|
438
|
+
|
|
439
|
+
Détecte les changements de maintainers entre versions : nouveau maintainer ajouté, seul maintainer remplacé (pattern event-stream), noms de maintainers suspects, nouveau publisher.
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
muaddib scan . --temporal-maintainer
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### 5. Canary Tokens / Honey Tokens (sandbox)
|
|
446
|
+
|
|
447
|
+
Injecte de faux credentials (GITHUB_TOKEN, NPM_TOKEN, clés AWS) dans l'environnement sandbox avant d'installer un package. Si le package tente d'exfiltrer ces honey tokens via HTTP, DNS ou stdout, il est signalé comme malveillant confirmé.
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
muaddib sandbox suspicious-package
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Scan temporel complet
|
|
454
|
+
|
|
455
|
+
Activer toutes les features d'analyse temporelle en une commande :
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
muaddib scan . --temporal-full
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Exemples d'utilisation
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
# Scan comportemental complet (5 features)
|
|
465
|
+
muaddib scan . --temporal-full
|
|
466
|
+
|
|
467
|
+
# Détection lifecycle scripts uniquement
|
|
468
|
+
muaddib scan . --temporal
|
|
469
|
+
|
|
470
|
+
# Diff AST + changement maintainer
|
|
471
|
+
muaddib scan . --temporal-ast --temporal-maintainer
|
|
472
|
+
|
|
473
|
+
# Sandbox avec canary tokens (activé par défaut)
|
|
474
|
+
muaddib sandbox suspicious-package
|
|
475
|
+
|
|
476
|
+
# Sandbox sans canary tokens
|
|
477
|
+
muaddib sandbox suspicious-package --no-canary
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Nouvelles règles de détection (v2.0)
|
|
481
|
+
|
|
482
|
+
| Rule ID | Nom | Sévérité | Feature |
|
|
483
|
+
|---------|-----|----------|---------|
|
|
484
|
+
| MUADDIB-TEMPORAL-001 | Ajout soudain de script lifecycle (Critique) | CRITICAL | `--temporal` |
|
|
485
|
+
| MUADDIB-TEMPORAL-002 | Ajout soudain de script lifecycle | HIGH | `--temporal` |
|
|
486
|
+
| MUADDIB-TEMPORAL-003 | Script lifecycle modifié | MEDIUM | `--temporal` |
|
|
487
|
+
| MUADDIB-TEMPORAL-AST-001 | API dangereuse ajoutée (Critique) | CRITICAL | `--temporal-ast` |
|
|
488
|
+
| MUADDIB-TEMPORAL-AST-002 | API dangereuse ajoutée (High) | HIGH | `--temporal-ast` |
|
|
489
|
+
| MUADDIB-TEMPORAL-AST-003 | API dangereuse ajoutée (Medium) | MEDIUM | `--temporal-ast` |
|
|
490
|
+
| MUADDIB-PUBLISH-001 | Rafale de publications détectée | HIGH | `--temporal-publish` |
|
|
491
|
+
| MUADDIB-PUBLISH-002 | Pic de package dormant | HIGH | `--temporal-publish` |
|
|
492
|
+
| MUADDIB-PUBLISH-003 | Succession rapide de versions | MEDIUM | `--temporal-publish` |
|
|
493
|
+
| MUADDIB-MAINTAINER-001 | Nouveau maintainer ajouté | HIGH | `--temporal-maintainer` |
|
|
494
|
+
| MUADDIB-MAINTAINER-002 | Maintainer suspect détecté | CRITICAL | `--temporal-maintainer` |
|
|
495
|
+
| MUADDIB-MAINTAINER-003 | Seul maintainer changé | HIGH | `--temporal-maintainer` |
|
|
496
|
+
| MUADDIB-MAINTAINER-004 | Nouveau publisher détecté | MEDIUM | `--temporal-maintainer` |
|
|
497
|
+
| MUADDIB-CANARY-001 | Exfiltration de canary token | CRITICAL | sandbox |
|
|
498
|
+
|
|
499
|
+
### Pourquoi c'est important
|
|
500
|
+
|
|
501
|
+
Ces features détectent des attaques comme :
|
|
502
|
+
- **Shai-Hulud** (2025) : Détecté par temporal lifecycle + AST diff (ajout soudain de `postinstall` + `child_process`)
|
|
503
|
+
- **ua-parser-js** (2021) : Détecté par changement maintainer + détection lifecycle
|
|
504
|
+
- **event-stream** (2018) : Détecté par changement de seul maintainer + AST diff (nouvelle dépendance `flatmap-stream` avec `eval`)
|
|
505
|
+
- **coa/rc** (2021) : Détecté par rafale de publications + détection lifecycle
|
|
506
|
+
|
|
507
|
+
Le tout sans avoir besoin d'un seul IOC.
|
|
395
508
|
|
|
396
509
|
---
|
|
397
510
|
|
|
@@ -488,7 +601,7 @@ Les alertes apparaissent dans Security > Code scanning alerts.
|
|
|
488
601
|
## Architecture
|
|
489
602
|
|
|
490
603
|
```
|
|
491
|
-
MUAD'DIB Scanner
|
|
604
|
+
MUAD'DIB 2.0 Scanner
|
|
492
605
|
|
|
|
493
606
|
+-- IOC Match (225 000+ packages, JSON DB)
|
|
494
607
|
| +-- OSV.dev npm dump (200K+ entrées MAL-*)
|
|
@@ -500,14 +613,24 @@ MUAD'DIB Scanner
|
|
|
500
613
|
| +-- Snyk Known Malware
|
|
501
614
|
| +-- Static IOCs (Socket, Phylum)
|
|
502
615
|
|
|
|
503
|
-
+--
|
|
504
|
-
+--
|
|
505
|
-
+--
|
|
506
|
-
+--
|
|
507
|
-
+--
|
|
508
|
-
+--
|
|
616
|
+
+-- 12 Scanners Parallèles
|
|
617
|
+
| +-- AST Parse (acorn)
|
|
618
|
+
| +-- Pattern Matching (shell, scripts)
|
|
619
|
+
| +-- Typosquat Detection (npm + PyPI, Levenshtein)
|
|
620
|
+
| +-- Python Scanner (requirements.txt, setup.py, pyproject.toml)
|
|
621
|
+
| +-- Analyse Entropie Shannon
|
|
622
|
+
| +-- GitHub Actions Scanner
|
|
623
|
+
| +-- Package, Dependencies, Hash, npm-registry, Dataflow scanners
|
|
624
|
+
|
|
|
625
|
+
+-- Détection d'Anomalies Supply Chain (v2.0)
|
|
626
|
+
| +-- Détection Lifecycle Script Temporelle (--temporal)
|
|
627
|
+
| +-- Diff AST Temporel (--temporal-ast)
|
|
628
|
+
| +-- Anomalie Fréquence de Publication (--temporal-publish)
|
|
629
|
+
| +-- Détection Changement Maintainer (--temporal-maintainer)
|
|
630
|
+
| +-- Canary Tokens / Honey Tokens (sandbox)
|
|
631
|
+
|
|
|
509
632
|
+-- Paranoid Mode (ultra-strict)
|
|
510
|
-
+-- Docker Sandbox (
|
|
633
|
+
+-- Docker Sandbox (analyse comportementale, capture réseau, canary tokens)
|
|
511
634
|
+-- Moniteur Zero-Day (polling RSS npm + PyPI, alertes Discord, rapport quotidien)
|
|
512
635
|
|
|
|
513
636
|
v
|
|
@@ -552,7 +675,7 @@ npm test
|
|
|
552
675
|
|
|
553
676
|
### Tests
|
|
554
677
|
|
|
555
|
-
- **
|
|
678
|
+
- **541 tests unitaires/intégration** - 80% coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
|
|
556
679
|
- **56 tests de fuzzing** - YAML malformé, JSON invalide, fichiers binaires, ReDoS, unicode, inputs 10MB
|
|
557
680
|
- **15 tests adversariaux** - Packages malveillants simulés, taux de détection 15/15
|
|
558
681
|
- **8 tests multi-facteur typosquat** - Cas limites et comportement cache
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
npm and PyPI supply-chain attacks are exploding. Shai-Hulud compromised 25K+ repos in 2025. Existing tools detect threats but don't help you respond.
|
|
32
32
|
|
|
33
|
-
MUAD'DIB combines static analysis + dynamic analysis (Docker sandbox) to detect threats AND guide your response.
|
|
33
|
+
MUAD'DIB combines static analysis + dynamic analysis (Docker sandbox) + **behavioral anomaly detection** (v2.0) to detect threats AND guide your response — even before they appear in any IOC database.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
@@ -393,6 +393,119 @@ Detects malicious patterns in `.github/workflows/` YAML files, including Shai-Hu
|
|
|
393
393
|
| Supply chain compromise | T1195.002 | IOC matching |
|
|
394
394
|
| PyPI malicious package | T1195.002 | IOC matching |
|
|
395
395
|
| Sandbox dynamic analysis | Multiple | Docker + strace + tcpdump |
|
|
396
|
+
| Sudden lifecycle script addition | T1195.002 | Temporal analysis |
|
|
397
|
+
| Dangerous API injection between versions | T1195.002 | Temporal AST diff |
|
|
398
|
+
| Publish frequency anomaly | T1195.002 | Registry metadata |
|
|
399
|
+
| Maintainer/publisher change | T1195.002 | Registry metadata |
|
|
400
|
+
| Canary token exfiltration | T1552.001 | Sandbox honey tokens |
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Supply Chain Anomaly Detection (v2.0)
|
|
405
|
+
|
|
406
|
+
MUAD'DIB 2.0 introduces a paradigm shift: from **IOC-based detection** (reactive, requires known threats) to **behavioral anomaly detection** (proactive, detects unknown threats by spotting suspicious changes).
|
|
407
|
+
|
|
408
|
+
Traditional supply-chain scanners rely on blocklists of known malicious packages. The problem: they can only detect threats AFTER they've been identified and reported. Attacks like **ua-parser-js** (2021), **event-stream** (2018), and **Shai-Hulud** (2025) went undetected for hours or days because no IOC existed yet.
|
|
409
|
+
|
|
410
|
+
MUAD'DIB 2.0 adds 5 behavioral detection features that can catch these attacks **before** they appear in any IOC database, by analyzing what changed between package versions.
|
|
411
|
+
|
|
412
|
+
### New features
|
|
413
|
+
|
|
414
|
+
#### 1. Sudden Lifecycle Script Detection (`--temporal`)
|
|
415
|
+
|
|
416
|
+
Detects when `preinstall`, `install`, or `postinstall` scripts suddenly appear in a new version of a package that never had them before. This is the #1 attack vector for supply-chain attacks.
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
muaddib scan . --temporal
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### 2. Temporal AST Diff (`--temporal-ast`)
|
|
423
|
+
|
|
424
|
+
Downloads the two latest versions of each dependency and compares their AST (Abstract Syntax Tree) to detect newly added dangerous APIs: `child_process`, `eval`, `Function`, `net.connect`, `process.env`, `fetch`, etc.
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
muaddib scan . --temporal-ast
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### 3. Publish Frequency Anomaly (`--temporal-publish`)
|
|
431
|
+
|
|
432
|
+
Detects abnormal publishing patterns: burst of versions in 24h, dormant package suddenly updated after 6+ months, rapid version succession (multiple releases in under 1h).
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
muaddib scan . --temporal-publish
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### 4. Maintainer Change Detection (`--temporal-maintainer`)
|
|
439
|
+
|
|
440
|
+
Detects changes in package maintainers between versions: new maintainer added, sole maintainer replaced (event-stream pattern), suspicious maintainer names, new publisher.
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
muaddib scan . --temporal-maintainer
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### 5. Canary Tokens / Honey Tokens (sandbox)
|
|
447
|
+
|
|
448
|
+
Injects fake credentials (GITHUB_TOKEN, NPM_TOKEN, AWS keys) into the sandbox environment before installing a package. If the package attempts to exfiltrate these honey tokens via HTTP, DNS, or stdout, it's flagged as confirmed malicious.
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
muaddib sandbox suspicious-package
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Full temporal scan
|
|
455
|
+
|
|
456
|
+
Enable all temporal analysis features at once:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
muaddib scan . --temporal-full
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Usage examples
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
# Full behavioral scan (all 5 features)
|
|
466
|
+
muaddib scan . --temporal-full
|
|
467
|
+
|
|
468
|
+
# Only lifecycle script detection
|
|
469
|
+
muaddib scan . --temporal
|
|
470
|
+
|
|
471
|
+
# AST diff + maintainer change
|
|
472
|
+
muaddib scan . --temporal-ast --temporal-maintainer
|
|
473
|
+
|
|
474
|
+
# Sandbox with canary tokens (enabled by default)
|
|
475
|
+
muaddib sandbox suspicious-package
|
|
476
|
+
|
|
477
|
+
# Sandbox without canary tokens
|
|
478
|
+
muaddib sandbox suspicious-package --no-canary
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### New detection rules (v2.0)
|
|
482
|
+
|
|
483
|
+
| Rule ID | Name | Severity | Feature |
|
|
484
|
+
|---------|------|----------|---------|
|
|
485
|
+
| MUADDIB-TEMPORAL-001 | Sudden Lifecycle Script Added (Critical) | CRITICAL | `--temporal` |
|
|
486
|
+
| MUADDIB-TEMPORAL-002 | Sudden Lifecycle Script Added | HIGH | `--temporal` |
|
|
487
|
+
| MUADDIB-TEMPORAL-003 | Lifecycle Script Modified | MEDIUM | `--temporal` |
|
|
488
|
+
| MUADDIB-TEMPORAL-AST-001 | Dangerous API Added (Critical) | CRITICAL | `--temporal-ast` |
|
|
489
|
+
| MUADDIB-TEMPORAL-AST-002 | Dangerous API Added (High) | HIGH | `--temporal-ast` |
|
|
490
|
+
| MUADDIB-TEMPORAL-AST-003 | Dangerous API Added (Medium) | MEDIUM | `--temporal-ast` |
|
|
491
|
+
| MUADDIB-PUBLISH-001 | Publish Burst Detected | HIGH | `--temporal-publish` |
|
|
492
|
+
| MUADDIB-PUBLISH-002 | Dormant Package Spike | HIGH | `--temporal-publish` |
|
|
493
|
+
| MUADDIB-PUBLISH-003 | Rapid Version Succession | MEDIUM | `--temporal-publish` |
|
|
494
|
+
| MUADDIB-MAINTAINER-001 | New Maintainer Added | HIGH | `--temporal-maintainer` |
|
|
495
|
+
| MUADDIB-MAINTAINER-002 | Suspicious Maintainer Detected | CRITICAL | `--temporal-maintainer` |
|
|
496
|
+
| MUADDIB-MAINTAINER-003 | Sole Maintainer Changed | HIGH | `--temporal-maintainer` |
|
|
497
|
+
| MUADDIB-MAINTAINER-004 | New Publisher Detected | MEDIUM | `--temporal-maintainer` |
|
|
498
|
+
| MUADDIB-CANARY-001 | Canary Token Exfiltration | CRITICAL | sandbox |
|
|
499
|
+
|
|
500
|
+
### Why it matters
|
|
501
|
+
|
|
502
|
+
These features detect attacks like:
|
|
503
|
+
- **Shai-Hulud** (2025): Would be caught by temporal lifecycle + AST diff (sudden `postinstall` + `child_process` added)
|
|
504
|
+
- **ua-parser-js** (2021): Would be caught by maintainer change + lifecycle script detection
|
|
505
|
+
- **event-stream** (2018): Would be caught by sole maintainer change + AST diff (new `flatmap-stream` dependency with `eval`)
|
|
506
|
+
- **coa/rc** (2021): Would be caught by publish burst + lifecycle script detection
|
|
507
|
+
|
|
508
|
+
All without needing a single IOC entry.
|
|
396
509
|
|
|
397
510
|
---
|
|
398
511
|
|
|
@@ -489,7 +602,7 @@ Alerts appear in Security > Code scanning alerts.
|
|
|
489
602
|
## Architecture
|
|
490
603
|
|
|
491
604
|
```
|
|
492
|
-
MUAD'DIB Scanner
|
|
605
|
+
MUAD'DIB 2.0 Scanner
|
|
493
606
|
|
|
|
494
607
|
+-- IOC Match (225,000+ packages, JSON DB)
|
|
495
608
|
| +-- OSV.dev npm dump (200K+ MAL-* entries)
|
|
@@ -512,8 +625,15 @@ MUAD'DIB Scanner
|
|
|
512
625
|
| +-- GitHub Actions Scanner
|
|
513
626
|
| +-- Package, Dependencies, Hash, npm-registry, Dataflow scanners
|
|
514
627
|
|
|
|
628
|
+
+-- Supply Chain Anomaly Detection (v2.0)
|
|
629
|
+
| +-- Temporal Lifecycle Script Detection (--temporal)
|
|
630
|
+
| +-- Temporal AST Diff (--temporal-ast)
|
|
631
|
+
| +-- Publish Frequency Anomaly (--temporal-publish)
|
|
632
|
+
| +-- Maintainer Change Detection (--temporal-maintainer)
|
|
633
|
+
| +-- Canary Tokens / Honey Tokens (sandbox)
|
|
634
|
+
|
|
|
515
635
|
+-- Paranoid Mode (ultra-strict)
|
|
516
|
-
+-- Docker Sandbox (behavioral analysis, network capture)
|
|
636
|
+
+-- Docker Sandbox (behavioral analysis, network capture, canary tokens)
|
|
517
637
|
+-- Zero-Day Monitor (npm + PyPI RSS polling, Discord alerts, daily report)
|
|
518
638
|
|
|
|
519
639
|
v
|
|
@@ -558,7 +678,7 @@ npm test
|
|
|
558
678
|
|
|
559
679
|
### Testing
|
|
560
680
|
|
|
561
|
-
- **
|
|
681
|
+
- **541 unit/integration tests** - 80% code coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
|
|
562
682
|
- **56 fuzz tests** - Malformed YAML, invalid JSON, binary files, ReDoS, unicode, 10MB inputs
|
|
563
683
|
- **15 adversarial tests** - Simulated malicious packages, 15/15 detection rate
|
|
564
684
|
- **8 multi-factor typosquat tests** - Edge cases and cache behavior
|
package/bin/muaddib.js
CHANGED
|
@@ -25,6 +25,11 @@ let webhookUrl = null;
|
|
|
25
25
|
let paranoidMode = false;
|
|
26
26
|
let excludeDirs = [];
|
|
27
27
|
let entropyThreshold = null;
|
|
28
|
+
let temporalMode = false;
|
|
29
|
+
let temporalAstMode = false;
|
|
30
|
+
let temporalPublishMode = false;
|
|
31
|
+
let temporalMaintainerMode = false;
|
|
32
|
+
let temporalFullMode = false;
|
|
28
33
|
|
|
29
34
|
for (let i = 0; i < options.length; i++) {
|
|
30
35
|
if (options[i] === '--json') {
|
|
@@ -90,8 +95,20 @@ for (let i = 0; i < options.length; i++) {
|
|
|
90
95
|
i++;
|
|
91
96
|
} else if (options[i] === '--paranoid') {
|
|
92
97
|
paranoidMode = true;
|
|
98
|
+
} else if (options[i] === '--temporal-full') {
|
|
99
|
+
temporalFullMode = true;
|
|
100
|
+
} else if (options[i] === '--temporal-ast') {
|
|
101
|
+
temporalAstMode = true;
|
|
102
|
+
} else if (options[i] === '--temporal-publish') {
|
|
103
|
+
temporalPublishMode = true;
|
|
104
|
+
} else if (options[i] === '--temporal-maintainer') {
|
|
105
|
+
temporalMaintainerMode = true;
|
|
106
|
+
} else if (options[i] === '--temporal') {
|
|
107
|
+
temporalMode = true;
|
|
93
108
|
} else if (options[i] === '--strict') {
|
|
94
109
|
// Sandbox strict mode flag (parsed here, used by sandbox commands)
|
|
110
|
+
} else if (options[i] === '--no-canary') {
|
|
111
|
+
// Sandbox canary disable flag (parsed here, used by sandbox commands)
|
|
95
112
|
} else if (!options[i].startsWith('-')) {
|
|
96
113
|
target = options[i];
|
|
97
114
|
}
|
|
@@ -301,7 +318,7 @@ const helpText = `
|
|
|
301
318
|
muaddib remove-hooks [path] Remove MUAD'DIB git hooks
|
|
302
319
|
muaddib update Update IOCs
|
|
303
320
|
muaddib scrape Scrape new IOCs
|
|
304
|
-
muaddib sandbox <pkg> [--strict] Analyze in isolated Docker container
|
|
321
|
+
muaddib sandbox <pkg> [--strict] [--no-canary] Analyze in isolated Docker container
|
|
305
322
|
muaddib sandbox-report <pkg> Sandbox + detailed network report
|
|
306
323
|
muaddib version Show version
|
|
307
324
|
|
|
@@ -323,6 +340,12 @@ const helpText = `
|
|
|
323
340
|
--fail-on [level] Fail level (critical|high|medium|low)
|
|
324
341
|
--webhook [url] Discord/Slack webhook
|
|
325
342
|
--paranoid Ultra-strict mode
|
|
343
|
+
--temporal Detect sudden lifecycle script changes (network requests per package)
|
|
344
|
+
--temporal-ast Detect sudden dangerous API additions via AST diff (downloads tarballs)
|
|
345
|
+
--temporal-publish Detect publish frequency anomalies (bursts, dormant spikes)
|
|
346
|
+
--temporal-maintainer Detect maintainer changes (new maintainer, account takeover)
|
|
347
|
+
--temporal-full All temporal analyses (lifecycle + AST + publish + maintainer)
|
|
348
|
+
--no-canary Disable honey token injection in sandbox
|
|
326
349
|
--exclude [dir] Exclude directory from scan (repeatable)
|
|
327
350
|
--entropy-threshold [n] Custom string-level entropy threshold (default: 5.5)
|
|
328
351
|
--save-dev, -D Install as dev dependency
|
|
@@ -353,6 +376,10 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
353
376
|
failLevel: failLevel,
|
|
354
377
|
webhook: webhookUrl,
|
|
355
378
|
paranoid: paranoidMode,
|
|
379
|
+
temporal: temporalMode || temporalFullMode,
|
|
380
|
+
temporalAst: temporalAstMode || temporalFullMode,
|
|
381
|
+
temporalPublish: temporalPublishMode || temporalFullMode,
|
|
382
|
+
temporalMaintainer: temporalMaintainerMode || temporalFullMode,
|
|
356
383
|
exclude: excludeDirs,
|
|
357
384
|
entropyThreshold: entropyThreshold
|
|
358
385
|
}).then(exitCode => {
|
|
@@ -378,6 +405,92 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
378
405
|
console.error('[ERROR]', err.message);
|
|
379
406
|
process.exit(1);
|
|
380
407
|
});
|
|
408
|
+
} else if (command === 'monitor') {
|
|
409
|
+
const testPkg = options.filter(o => !o.startsWith('-'));
|
|
410
|
+
const isTemporal = options.includes('--temporal');
|
|
411
|
+
const isTemporalAst = options.includes('--temporal-ast');
|
|
412
|
+
const isTest = options.includes('--test');
|
|
413
|
+
|
|
414
|
+
if (isTemporalAst && isTest) {
|
|
415
|
+
const actualPkg = options.filter(o => !o.startsWith('-')).pop();
|
|
416
|
+
if (!actualPkg) {
|
|
417
|
+
console.log('Usage: muaddib monitor --temporal-ast --test <package-name>');
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
const { detectSuddenAstChanges } = require('../src/temporal-ast-diff.js');
|
|
421
|
+
console.log(`[TEMPORAL-AST] Analyzing ${actualPkg}...\n`);
|
|
422
|
+
detectSuddenAstChanges(actualPkg).then(result => {
|
|
423
|
+
console.log(`Package: ${result.packageName}`);
|
|
424
|
+
console.log(`Latest version: ${result.latestVersion || 'N/A'}`);
|
|
425
|
+
console.log(`Previous version: ${result.previousVersion || 'N/A'}`);
|
|
426
|
+
console.log(`Suspicious: ${result.suspicious ? 'YES' : 'NO'}`);
|
|
427
|
+
if (result.metadata.latestPublishedAt) {
|
|
428
|
+
console.log(`Published: ${result.metadata.latestPublishedAt}`);
|
|
429
|
+
}
|
|
430
|
+
if (result.findings.length > 0) {
|
|
431
|
+
console.log(`\nFindings:`);
|
|
432
|
+
for (const f of result.findings) {
|
|
433
|
+
console.log(` [${f.severity}] ${f.pattern}: ${f.description}`);
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
console.log(`\nNo dangerous API changes detected between the last two versions.`);
|
|
437
|
+
}
|
|
438
|
+
process.exit(result.suspicious ? 1 : 0);
|
|
439
|
+
}).catch(err => {
|
|
440
|
+
console.error(`[ERROR] ${err.message}`);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
});
|
|
443
|
+
} else if (isTemporal && isTest && testPkg.length > 0) {
|
|
444
|
+
const { detectSuddenLifecycleChange } = require('../src/temporal-analysis.js');
|
|
445
|
+
const pkgName = testPkg[testPkg.indexOf('--test') !== -1 ? testPkg.length - 1 : 0] || testPkg[0];
|
|
446
|
+
// Find the package name: it's the non-flag argument
|
|
447
|
+
const actualPkg = options.filter(o => !o.startsWith('-')).pop();
|
|
448
|
+
if (!actualPkg) {
|
|
449
|
+
console.log('Usage: muaddib monitor --temporal --test <package-name>');
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
console.log(`[TEMPORAL] Analyzing ${actualPkg}...\n`);
|
|
453
|
+
detectSuddenLifecycleChange(actualPkg).then(result => {
|
|
454
|
+
console.log(`Package: ${result.packageName}`);
|
|
455
|
+
console.log(`Latest version: ${result.latestVersion || 'N/A'}`);
|
|
456
|
+
console.log(`Previous version: ${result.previousVersion || 'N/A'}`);
|
|
457
|
+
console.log(`Suspicious: ${result.suspicious ? 'YES' : 'NO'}`);
|
|
458
|
+
if (result.metadata.note) {
|
|
459
|
+
console.log(`Note: ${result.metadata.note}`);
|
|
460
|
+
}
|
|
461
|
+
if (result.metadata.latestPublishedAt) {
|
|
462
|
+
console.log(`Published: ${result.metadata.latestPublishedAt}`);
|
|
463
|
+
}
|
|
464
|
+
if (result.metadata.maintainers && result.metadata.maintainers.length > 0) {
|
|
465
|
+
const names = result.metadata.maintainers.map(m => m.name || m.email).join(', ');
|
|
466
|
+
console.log(`Maintainers: ${names}`);
|
|
467
|
+
}
|
|
468
|
+
if (result.findings.length > 0) {
|
|
469
|
+
console.log(`\nFindings:`);
|
|
470
|
+
for (const f of result.findings) {
|
|
471
|
+
const action = f.type === 'lifecycle_added' ? 'ADDED' : f.type === 'lifecycle_modified' ? 'MODIFIED' : 'REMOVED';
|
|
472
|
+
const value = f.type === 'lifecycle_modified' ? f.newValue : f.value;
|
|
473
|
+
console.log(` [${f.severity}] ${f.script} script ${action}: "${value}"`);
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
console.log(`\nNo lifecycle script changes detected between the last two versions.`);
|
|
477
|
+
}
|
|
478
|
+
process.exit(result.suspicious ? 1 : 0);
|
|
479
|
+
}).catch(err => {
|
|
480
|
+
console.error(`[ERROR] ${err.message}`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
});
|
|
483
|
+
} else if (isTemporal && isTest) {
|
|
484
|
+
console.log('Usage: muaddib monitor --temporal --test <package-name>');
|
|
485
|
+
process.exit(1);
|
|
486
|
+
} else {
|
|
487
|
+
// Start full monitor
|
|
488
|
+
const { startMonitor } = require('../src/monitor.js');
|
|
489
|
+
startMonitor().catch(err => {
|
|
490
|
+
console.error('[ERROR]', err.message);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
});
|
|
493
|
+
}
|
|
381
494
|
} else if (command === 'daemon') {
|
|
382
495
|
startDaemon({ webhook: webhookUrl });
|
|
383
496
|
} else if (command === 'install' || command === 'i') {
|
|
@@ -404,13 +517,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
404
517
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
405
518
|
const packageName = sandboxOpts[0];
|
|
406
519
|
const strict = options.includes('--strict');
|
|
520
|
+
const canary = !options.includes('--no-canary');
|
|
407
521
|
if (!packageName) {
|
|
408
|
-
console.log('Usage: muaddib sandbox <package-name> [--strict]');
|
|
522
|
+
console.log('Usage: muaddib sandbox <package-name> [--strict] [--no-canary]');
|
|
409
523
|
process.exit(1);
|
|
410
524
|
}
|
|
411
525
|
|
|
412
526
|
buildSandboxImage()
|
|
413
|
-
.then(() => runSandbox(packageName, { strict }))
|
|
527
|
+
.then(() => runSandbox(packageName, { strict, canary }))
|
|
414
528
|
.then((results) => {
|
|
415
529
|
process.exit(results.suspicious ? 1 : 0);
|
|
416
530
|
})
|
|
@@ -422,13 +536,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
422
536
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
423
537
|
const packageName = sandboxOpts[0];
|
|
424
538
|
const strict = options.includes('--strict');
|
|
539
|
+
const canary = !options.includes('--no-canary');
|
|
425
540
|
if (!packageName) {
|
|
426
|
-
console.log('Usage: muaddib sandbox-report <package-name> [--strict]');
|
|
541
|
+
console.log('Usage: muaddib sandbox-report <package-name> [--strict] [--no-canary]');
|
|
427
542
|
process.exit(1);
|
|
428
543
|
}
|
|
429
544
|
|
|
430
545
|
buildSandboxImage()
|
|
431
|
-
.then(() => runSandbox(packageName, { strict }))
|
|
546
|
+
.then(() => runSandbox(packageName, { strict, canary }))
|
|
432
547
|
.then((results) => {
|
|
433
548
|
if (results.raw_report) {
|
|
434
549
|
console.log(generateNetworkReport(results.raw_report));
|
package/docker/sandbox-runner.sh
CHANGED
|
@@ -46,6 +46,7 @@ sleep 1
|
|
|
46
46
|
|
|
47
47
|
# ── 3. npm install with strace ──
|
|
48
48
|
echo "[SANDBOX] Installing $PACKAGE..." >&2
|
|
49
|
+
cd /sandbox/install
|
|
49
50
|
strace -f -e trace=network,process,open,openat,connect,execve,sendto,recvfrom \
|
|
50
51
|
-o /tmp/strace.log \
|
|
51
52
|
npm install "$PACKAGE" --ignore-scripts=false > /tmp/install.log 2>&1
|
|
@@ -65,8 +66,8 @@ DURATION_MS=$((END_MS - START_MS))
|
|
|
65
66
|
|
|
66
67
|
# ── 5. Filesystem diff ──
|
|
67
68
|
echo "[SANDBOX] Analyzing filesystem changes..." >&2
|
|
68
|
-
comm -13 /tmp/fs-before.txt /tmp/fs-after.txt | grep -v '^/sandbox/
|
|
69
|
-
comm -23 /tmp/fs-before.txt /tmp/fs-after.txt | grep -v '^/sandbox/
|
|
69
|
+
comm -13 /tmp/fs-before.txt /tmp/fs-after.txt | grep -v '^/sandbox/install/' | grep -v '^/tmp/' > /tmp/fs-created.txt
|
|
70
|
+
comm -23 /tmp/fs-before.txt /tmp/fs-after.txt | grep -v '^/sandbox/install/' > /tmp/fs-deleted.txt
|
|
70
71
|
|
|
71
72
|
# ── 6. Parse strace ──
|
|
72
73
|
echo "[SANDBOX] Parsing strace..." >&2
|