Avant chaque déploiement, je prends le temps d’auditer mes dépendances npm. Voici ma méthode en 5 étapes concrètes pour repérer une dépendance malveillante avant qu’elle n’atteigne la production.

Pourquoi il faut se méfier des dépendances npm

Les projets JavaScript modernes reposent souvent sur des milliers de paquets. Cette richesse crée une surface d’attaque : une seule dépendance compromise peut injecter du code malveillant, exfiltrer des données ou ouvrir une backdoor. J’ai déjà vu des cas où un paquet abandonné a été repris et modifié pour distribuer un malware — et c’est silencieux tant qu’on ne surveille pas.

Étape 1 — Inventaire et cartographie des dépendances

La première chose que je fais est d’avoir une vue claire de ce que mon projet utilise réellement.

  • Générer la liste complète : npm ls --all --json ou utiliser yarn list --json pour obtenir une arborescence complète.
  • Extraire les versions et les auteurs depuis le package-lock.json ou yarn.lock.
  • Je crée un petit script ou j’importe ces données dans un tableur pour repérer les dépendances directes vs transitives. Connaître la profondeur d’une dépendance m’aide à décider du niveau d’audit nécessaire : une dépendance directe reçoit toujours plus d’attention.

    Étape 2 — Vérifier la réputation et l’activité du package

    Un paquet malveillant présente souvent des signes révélateurs : mainteneur inactif, nombre de téléchargements très bas, ou un dépôt GitHub vide.

  • Regarder la page npm (ex. https://www.npmjs.com/package/nom-du-paquet) pour :
  • - le nombre de téléchargements
  • - la date de dernière publication
  • - les versions publiées récemment
  • Examiner le dépôt GitHub :
  • - issues et pull requests ouvertes/fermées
  • - activité des contributeurs
  • - présence d’un fichier README, LICENSE et tests
  • Si un paquet n’a aucune activité depuis des années puis subit une publication soudaine et suspecte, je le marque pour une inspection approfondie. Les paquets populaires (lodash, axios) restent généralement sûrs, mais il faut tout de même vérifier les versions récemment publiées.

    Étape 3 — Analyse statique du code et audit des scripts

    Une fois qu’un paquet attire mon attention, j’examine le code. Je clone le dépôt ou j’extrais le tarball depuis npm et je cherche des patterns dangereux.

  • Rechercher dans les fichiers :
  • - scripts d’installation (install, postinstall dans package.json)
  • - utilisation de child_process.exec, spawn, eval
  • - requêtes réseau sortantes non justifiées (fetch, axios, http.request)
  • - lecture/écriture de fichiers sensibles (ex. ~/.ssh, /etc/passwd)
  • Quelques commandes utiles :

    CommandeBut
    grep -R "postinstall" .Repérer les scripts d’installation
    grep -R "child_process" .Identifier l’exécution de commandes système
    rg "eval|new Function" .Trouver des exécutions dynamiques

    Je prête une attention particulière aux dépendances qui s’installent automatiquement (via postinstall) : elles peuvent exécuter du code sur la machine de l’installateur. Si un paquet contient des morceaux obfusqués (strings encodés, base64), je considère cela comme un signal d’alarme.

    Étape 4 — Tests en bac à sable et exécution contrôlée

    Après l’inspection statique, je fais tourner le paquet isolé dans un environnement sécurisé.

  • Créer un container Docker ou une VM sans accès aux secrets :
  • - docker run --rm -it node:16 bash
  • Installer le paquet et surveiller les appels réseau :
  • - utiliser tcpdump, Wireshark ou les outils intégrés de Docker pour capturer le trafic
  • Observer les processus et fichiers créés :
  • - lsof, ps, strace (ou dtrace) pour suivre l’activité système
  • Exécuter les tests unitaires du paquet (s’il en possède) peut aussi révéler des comportements inattendus. Pour les paquets qui s’exécutent comme CLI, je lance les commandes avec des variables d’environnement dépouillées et des répertoires temporaires pour voir ce qui est touché.

    Étape 5 — Automatiser la surveillance et mettre en place des garde-fous

    Une fois les vérifications manuelles faites, je mets en place des contrôles automatiques pour que l’alerte survienne avant la prochaine publication ou mise à jour.

  • Intégrer un scanner de vulnérabilités dans CI :
  • - npm audit, Snyk, Dependabot ou GitHub Dependabot Alerts
  • Configurer des règles strictes pour les mises à jour :
  • - refuser les upgrades automatiques pour les packages critiques
  • - exiger une revue manuelle pour toute mise à jour transitive
  • Bloquer les scripts d’installation dans les environnements sensibles :
  • - npm ci --ignore-scripts (pour les environnements de build où vous êtes sûr des dépendances)
  • Surveiller les changements de mainteneur :
  • - GitHub Actions ou outils de notification on-release pour être prévenu d’une publication ou d’un transfert de propriétaire
  • Je recommande d’ajouter un job CI qui exécute les 4 premières étapes de façon automatisée (scan de réputation API, grep statique, installation en sandbox limitée) et qui échoue si quelque chose d’anormal est détecté.

    Exemples concrets et signaux d’alerte

    Voici quelques signaux qui, pour moi, déclenchent une investigation :

  • Nouvelle version publiée avec un changement mineur mais sans changelog
  • Ajout d’un script postinstall qui télécharge un binaire
  • Présence de code obfusqué ou de payload encodé en base64
  • Accès réseau vers un domaine inconnu au moment de l’installation
  • Transfert du dépôt vers un compte GitHub sans activité communautaire
  • Si je découvre un package suspect, j’en informe le reste de l’équipe, bloque la version dans le lockfile et cherche une alternative. Parfois, il suffit de pinner à une version antérieure connue pour être saine.

    Outils que j’utilise régulièrement

    OutilUsage
    npm audit / yarn auditDétection de vulnérabilités connues
    SnykAnalyse approfondie et monitoring
    DependabotSurveillance des mises à jour et PRs
    DockerExécution en bac à sable
    Wireshark / tcpdumpSurveillance des connexions réseau

    Ces outils ne remplacent pas une revue humaine, mais ils réduisent considérablement le risque d’introduire une dépendance malveillante.

    En appliquant ces 5 étapes systématiquement, j’ai évité plusieurs pièges potentiels et j’ai renforcé la résilience de mes déploiements. Rester vigilant et automatiser les contrôles critiques est, selon moi, la meilleure défense contre les menaces liées aux dépendances npm.