← Blog

Nos métriques internes (TPR sur 49 samples, ADR sur 107 adversariaux) mesurent la précision sur des échantillons choisis. Mais la vraie question est : que se passe-t-il sur 17 000 malwares réels, en conditions sauvages ?

Le dataset Datadog Security Labs contient 17 922 packages npm confirmés malveillants. On les a tous scannés.

Le setup

Exécution sur VPS. 17 922 packages, chacun téléchargé, extrait, et scanné avec les 14 scanners. Temps total : 32 141 secondes (~9 heures). Zéro erreur.

Sur les 17 922 packages :

  • 3 335 skips - packages sans fichiers JavaScript (binaires purs, assets, packages vides). Exclus du calcul.
  • 14 587 in-scope - packages avec du code JavaScript analysable

Les résultats

MétriqueValeur
Wild TPR global92.5% (13 486 / 14 587)
compromised_lib97.8% (904 / 924)
malicious_intent92.1% (12 582 / 13 663)
Erreurs0

La distinction compromised_lib vs malicious_intent est importante. Les compromised_lib sont des packages légitimes dont une version spécifique a été compromise (pattern ua-parser-js, event-stream). Les malicious_intent sont des packages créés avec l'intention de nuire (typosquats, droppers, exfiltrators). MUAD'DIB détecte mieux les compromissions (97.8%) car elles laissent plus de traces dans le diff entre versions.

Distribution des scores

ScorePackagesInterprétation
01 101Non détectés (aucune menace trouvée)
1-92 488Signaux faibles, sous le seuil
10-242 125Zone grise (seuil standard = 20)
25-494 900Détectés avec confiance modérée
50-741 527Détectés avec haute confiance
75-1002 446Détectés avec très haute confiance

La majorité des détections (8 873 packages, 61%) ont un score >= 25. Ce n'est pas du scoring marginal - ce sont des détections claires.

Les MISS : qu'est-ce qu'on rate ?

1 101 packages avec score 0 et 0 menaces. Analyse des causes :

  • @react-native-aria/* - Packages marqués compromised_lib dans le dataset Datadog, mais sans code JS malveillant détectable statiquement. La compromission est au niveau du registre/maintainer, pas du code source.
  • xrpl - Bibliothèque crypto légitime dont des versions spécifiques ont été compromises. Le code malveillant a été injecté via un processus de build, pas directement dans les sources.
  • Packages sans code - Certains packages ne contiennent que des fichiers de configuration ou des assets. Aucun code à analyser = aucune détection possible.

Ces MISS sont cohérents avec les limites connues de l'analyse statique : si le code malveillant n'est pas dans les sources scannées (build-time injection, registre compromis, binaires pré-compilés), un scanner statique ne peut pas le détecter.

Le rôle des compound rules

Avant les compound rules (v2.9.2), le Wild TPR estimé était autour de ~88%. Le gap de ~4.5% a été comblé par 6 règles de scoring composé :

CompoundComposants
crypto_staged_payloadstaged_binary_payload + crypto_decipher
lifecycle_typosquatlifecycle_script + typosquat_detected
lifecycle_inline_execlifecycle_script + node_inline_exec
lifecycle_remote_requirelifecycle_script + network_require

Ces combinaisons n'apparaissent jamais dans les 529 packages bénins du dataset de référence. Un package avec un script lifecycle ET un nom typosquat est toujours malveillant. Un script lifecycle qui fait require('https') aussi.

Comparaison avec les métriques internes

MétriqueInterneWild (Datadog)
TPR93.9% (46/49)92.5% (13 486/14 587)
ADR96.3% (103/107)-
compromised_lib-97.8%

Les métriques internes et sauvages convergent (~93% vs ~92.5%). C'est un bon signe : le ground truth de 49 samples et les 107 adversariaux ne sont pas sur-ajustés. La performance généralise au-delà du dataset de développement.

Leçon

Un benchmark sur 17K packages réels ne ment pas. Les métriques internes sur 49+107 échantillons étaient honnêtes - la performance sauvage le confirme. Mais le benchmark révèle aussi les limites structurelles de l'analyse statique : les compromissions build-time, les injections registre, et les binaires pré-compilés restent hors de portée.

92.5% n'est pas 100%. Les 7.5% restants sont un rappel que la détection statique est une couche de défense, pas la seule.