Le moniteur MUAD'DIB tourne depuis decembre 2025. ~10 000 packages npm scannes par jour, 24/7. ~600 alertes webhook Discord quotidiennes. Apres 3 mois : zero malware confirme. Pas parce qu'il n'y en a pas, mais parce que 600 cards orange identiques sans hierarchie, personne ne les trie.
Le probleme : labels contamines
20 questions posees sur le pipeline. 5 causes racines. La plus grave : la contamination des labels ML.
- Le moniteur detecte un package suspect (score ≥ 20)
- Le sandbox Docker l'execute
- Le sandbox ne detecte rien
- Le moniteur labellise automatiquement le package comme
"fp"
Sauf que le sandbox n'avait pas de honey tokens pendant 3 mois. Un sandbox propre ne prouve rien - le malware peut ne pas s'etre active (time bomb, cible specifique, check d'environnement). 8176 records contamines.
Le classifier ML apprenait a dire "tout est FP". Precision 37.2%, recall 99.8%.
Le nettoyage
Conversion des 8176 labels "fp" automatiques en "unconfirmed", exclus de l'entrainement. Seuls les labels valides par review manuelle (manualReview: true) sont acceptes comme "fp".
Meme code, memes features, donnees propres :
| Metrique | Avant | Apres |
|---|---|---|
| Precision | 37.2% | 97.8% |
| Recall | 99.8% | 93.3% |
| F1 | 0.541 | 0.955 |
Benchmark Datadog
| Metrique | Valeur |
|---|---|
| In-scope | 14 587 |
| Detectes (@1) | 13 538 (92.8%) |
| Detectes (@20) | 10 202 (69.9%) |
| Score = 0 | 1 049 |
ML1 - Malware detector
XGBoost, 114 arbres, 21 features, seuil 0.500. Top features :
- score - score de risque global (aggregation des heuristiques)
- max_single_points - poids de la finding la plus grave
- file_score_max - score du fichier le plus suspect
- threat_density - findings / fichiers (malware = concentre, framework = dilue)
Le ML decide "est-ce que l'ensemble de signaux ressemble a un malware ou a du bruit". Les regles decident "quel type de menace".
ML2 - Bundler detector
XGBoost, 98 arbres, 30 features, seuil 0.100. Les bundlers (webpack, rollup) generent du code minifie qui accumule des findings legits - eval, Function, require dynamiques - qui ressemblent a du malware. ML2 detecte ces cas.
F1 de 0.996. Warning : unpacked_size_bytes correle fortement avec les bundlers et pourrait constituer un leakage. Le F1 reel en production sera probablement inferieur.
Triage P1/P2/P3
Classification visuelle des alertes :
- P1 (rouge) - IOC match, types haute confiance, canary token exfiltre, sandbox positif. ~26/jour.
- P2 (orange) - Score ≥ 50, compound scoring, lifecycle + intent coherence.
- P3 (jaune) - Reste. Score 20-49 sans signal fort. Majorite des FP.
Les ~26 vrais malwares sont visuellement distincts des ~574 FP. Triage P1 en 2 minutes/jour au lieu de noyer dans 600 cards identiques.
La suite : LLM Phase 3
Investigation manuelle de 9 packages live : tous FP. Temps moyen 5-10 minutes par package. A 600 alertes/jour, le triage humain ne scale pas.
Plan : Claude 3.5 Haiku en shadow mode pour analyser les alertes P2/P3. ~$17/mois. Phases : shadow → enforce P3 → enforce P2+P3. Les P1 ne seront jamais filtrees.
Lecon
Le meme algorithme XGBoost, les memes hyperparametres, les memes 21 features. Precision 37% → 98% avec un seul changement : des donnees propres. Un sandbox sans honey tokens qui labellise "fp" automatiquement, c'est du label poisoning sur soi-meme.