Votre lockfile cache-t-elle des vulnérabilités ?
Un lockfile est le document le plus honnête de votre dépôt : il liste la version exacte de chaque paquet que votre build installe réellement, sans plages, sans « latest », sans surprises. C'est aussi, discrètement, une liste de surface d'attaque — chaque version épinglée est une version dans laquelle quelqu'un a pu trouver une faille depuis que vous l'avez épinglée. Cet article explique comment un scanner transforme cette liste en réponses : comment la base de données OSV associe les versions aux vulnérabilités connues, pourquoi un indicateur CISA KEV compte plus qu'un score CVSS et pourquoi « aucune vulnérabilité trouvée » est une affirmation sur une base de données, pas sur votre code. Vous pouvez exécuter la vérification vous-même dans le SBOM Vulnerability Scanner, qui analyse votre lockfile dans le navigateur et ne le téléverse jamais.
Ce qu'un lockfile épingle — et pourquoi c'est la surface d'attaque
Le logiciel moderne est en grande partie le code d'autres personnes. Une application web typique fait appel à des centaines de paquets, et chacun d'eux en fait appel à d'autres : une dépendance directe sur un framework web peut facilement devenir deux cents paquets transitifs que vous n'avez jamais choisis ni jamais examinés. Le lockfile est l'enregistrement de cet arbre — package-lock.json pour npm, un requirements.txt entièrement épinglé pour Python, go.mod et go.sum pour Go — consigné pour que chaque installation, sur chaque machine, produise le même arbre. Sans lui, « ça marche sur ma machine » est un espoir, pas un fait.
Cette même exactitude fait du lockfile le bon endroit où chercher les vulnérabilités connues. Une base de données de vulnérabilités est indexée par nom de paquet, écosystème et version exacte : lodash 4.17.20 est vulnérable, lodash 4.17.21 ne l'est pas, et la différence tient à une seule recherche. Une plage flottante ne peut pas être jugée ainsi — le scanner devrait deviner quelle version vous avez réellement installée, et deviner n'est pas une pratique de sécurité. La première règle de l'hygiène des lockfiles est donc sans éclat et non négociable : commitez le lockfile, gardez-le épinglé et scannez l'arbre épinglé, pas la liste de souhaits de votre manifeste.
Comment OSV associe les versions aux vulnérabilités connues
OSV.dev est une base de données publique de vulnérabilités qui agrège les avis de GitHub, de la NVD et des écosystèmes de langages dans un index interrogeable. Un scanner lui envoie un lot de triplets nom, écosystème et version — lodash, npm, 4.17.20 — et reçoit en retour les identifiants de vulnérabilités qui affectent exactement cette version. Les identifiants sont ensuite récupérés en entier : un résumé, les plages affectées avec leurs versions corrigées, la gravité sous forme de vecteur CVSS et les alias CVE. Rien sur votre projet ne voyage avec la requête — juste les coordonnées publiques de paquets publics.
La correspondance est délibérément bête, et c'est sa force. Elle ne lit pas votre code, ne juge pas si vous appelez la fonction vulnérable et ne se soucie pas de la manière dont le paquet est utilisé. Si l'avis indique que les versions antérieures à 4.17.21 sont affectées et que vous épinglez 4.17.20, vous correspondez — même si votre code ne touche jamais la fonction vulnérable. Ce sur-signalement est une fonctionnalité : l'analyse d'atteignabilité est une question plus difficile et plus lente, et la question bon marché (« cette version est-elle connue comme mauvaise ? ») attrape déjà la dépendance que vous avez oublié de mettre à jour il y a trois ans.
Ce que le scanner ne peut pas voir
Une vérification par correspondance de version a une limite stricte, et il vaut la peine de l'énoncer clairement : elle ne peut trouver que des vulnérabilités que quelqu'un a déjà signalées, pour une version que la base de données connaît. Une vulnérabilité découverte hier et pas encore publiée est invisible. Un paquet qui n'a jamais été vulnérable mais qui est malveillant — un typosquat d'un nom populaire, une version compromise, une dépendance qui appelle son serveur — n'a aucune CVE, et aucun scanner de ce type ne le signalera. Il en va de même pour votre propre code : le scanner juge les paquets que vous installez, pas le code que vous écrivez.
Cette limite est la raison pour laquelle le résultat « aucune vulnérabilité trouvée » doit être lu avec soin. Il signifie : pour ces versions exactes, dans cette base de données, à cet instant, rien n'est enregistré. C'est un négatif utile — un scan propre d'un lockfile frais est vraiment une bonne nouvelle — mais ce n'est pas un certificat. Les défenses contre ce que le scanner ne peut pas voir sont d'autres outils : épingler et examiner les nouvelles dépendances avant qu'elles n'entrent dans l'arbre, surveiller les nouveaux avis en continu plutôt que trimestriellement, et regarder avec suspicion une nouvelle version soudaine d'un paquet silencieux. Le scanner est le détecteur de fumée, pas les pompiers.
Pourquoi un indicateur KEV prime sur un score CVSS élevé
Un score CVSS répond à une hypothèse : quelle serait la gravité de cette vulnérabilité si quelqu'un l'exploitait ? Le catalogue des vulnérabilités connues exploitées (Known Exploited Vulnerabilities) de la CISA répond à un fait : quelqu'un l'exploite-t-il, dans la nature, en ce moment même ? Les deux réponses sont régulièrement en désaccord. Une vulnérabilité notée 9,8 que personne n'a transformée en arme peut attendre la prochaine mise à jour planifiée ; une vulnérabilité notée 5,3 qui figure sur la liste KEV — avec une date limite de correction fixée par la CISA — mérite un correctif cette semaine, car l'exploit existe déjà et est déjà utilisé.
Cette inversion est la raison pour laquelle le scanner signale les lignes KEV séparément au lieu de les fondre dans le tri par gravité. L'ordre de tri vous dit ce qui est grave ; l'indicateur KEV vous dit ce qui est urgent. En pratique, le tri est court : corrigez d'abord les éléments KEV, puis les critiques avec une version corrigée publiée, puis planifiez le reste avec le prochain cycle de mise à jour. Le bureau Bulk CVE Triage de ce site replace les mêmes CVE dans leur contexte d'exploitation — statut KEV, probabilité EPSS et CVSS ensemble — quand un seul indicateur ne suffit pas à justifier le travail auprès d'une équipe.
Commitez le lockfile, scannez-le en CI — pas une fois par trimestre
Un lockfile qui n'est pas commité est un lockfile qui n'existe pas. S'il ne vit que sur la machine d'un développeur, chaque autre machine installe ce que « latest » résout ce jour-là, et l'arbre que vous avez scanné n'est pas l'arbre que vous livrez. Le commiter est le contrôle de chaîne d'approvisionnement le moins cher qui soit : il rend les installations reproductibles, rend les réponses du scanner vraies pour tout le monde et transforme une mise à jour de dépendance en un diff révisable au lieu d'une dérive silencieuse.
La seconde moitié, c'est la cadence. Des vulnérabilités sont publiées chaque jour, donc un scan exécuté une fois par trimestre est un instantané qui vieillit en quelques jours. La forme utile est un scan à chaque pull request qui touche le lockfile, plus un scan planifié de la branche principale — la même boucle qu'une suite de tests, avec la même propriété : un nouvel avis fait passer un build vert au rouge le jour de sa publication, quand le correctif est une montée de version, pas une revue post-incident. Le scanner de navigateur de ce site est la version manuelle de cette boucle : collez le lockfile après une montée dependabot, ou avant une version, et confirmez l'arbre que vous êtes sur le point de livrer.
Versions corrigées, changements cassants, et quand corriger plutôt qu'épingler
Chaque correspondance d'un scan porte une version corrigée — la première version dans laquelle la vulnérabilité a disparu — ou un honnête « aucune publiée ». Le cas courant est une version de patch ou mineure, et la mise à jour est un changement d'une ligne dans le lockfile. Mais une version corrigée n'est pas toujours une version sûre à adopter : un saut de version majeure peut changer un comportement dont votre code dépend, et un correctif publié hier n'a eu qu'une journée de test en conditions réelles. Le jugement est le même que pour toute mise à jour de dépendance : lisez le changelog, lancez les tests et mettez à jour d'abord les paquets que vous importez réellement.
Quand il n'y a pas de version corrigée, les options se réduisent à trois : supprimer la dépendance si rien n'en a besoin, la remplacer par une alternative maintenue, ou atténuer le chemin de code vulnérable — cesser de transmettre des entrées non fiables à la fonction concernée, ou placer l'appel derrière une vérification décrite par l'avis — et surveiller l'avis pour une mise à jour. Ce qu'il ne faut pas faire, c'est épingler la version vulnérable et l'oublier : une épingle est une décision, et elle mérite un commentaire dans le lockfile ou le suivi des tickets disant pourquoi, et une date pour la revoir. Relancez le scan après tout changement ; le scanner prend quelques secondes, et un second scan est la seule preuve que les résultats du premier scan ont disparu.