N'oublie pas git blame? À un moment ou à un autre, quelqu’un va devoir l’exécuter sur le code hérité, et cela mettra en lumière des choix que vous avez faits il y a des années. Cette personne sera perplexe. Peut-être que le projet a été confié à quelqu’un d’autre. Peut-être que vous avez quitté l’entreprise. PEUT-ÊTRE, il se peut que le projet ait été confié à la communauté pour qu'elle le développe. Mais le problème, c'est que personne ne sait pourquoi vous avez agi ainsi ni quelles étaient les contraintes auxquelles vous étiez soumis. La question qui se pose est simple : pourquoi avez-vous choisi ce dépôt plutôt qu'un autre ?
Les décisions que vous prenez aujourd’hui concernant les dépendances constituent un message destiné à la personne qui héritera un jour de cette base de code. Mais ce message est rarement transmis. En général, il ne s’agit que d’une ligne dans package.json avec une plage de versions telle que ^2.4.1 et rien qui explique comment cela s'est produit. C'est précisément sur ce point que bon nombre de systèmes de gestion des dépendances open source échouent discrètement.
Ce n'est pas un instantané
Ajouter un paquet ne revient pas à se limiter à un artefact statique. Vous héritez d’une trajectoire. En coulisses, de nombreux éléments et personnes sont impliqués lorsque vous intégrez la bibliothèque la plus récente et la plus performante, et de nombreux paquets apportent des dépendances transitives gérées par des personnes dont vous n’avez jamais entendu parler. La commande d’installation ne communique rien de tout cela. Les responsables de maintenance changent, tout comme le code. Un paquet reste inactif pendant des mois, et souvent personne ne s’en rend compte jusqu’à ce qu’une vulnérabilité CVE apparaisse et qu’il n’y ait plus personne pour y apporter un correctif.
Le fichier de verrouillage permet de contrôler ce qui est installé. Il enregistre l’arborescence résolue, y compris les versions et, pour les paquets du registre, les hachages d’intégrité, mais ne donne aucune explication sur la raison de leur présence. Cette absence d’explication se fait sentir plus tard, généralement au pire moment possible. Quelqu’un tente de déployer un correctif de sécurité et se rend compte que la dépendance qu’il doit mettre à jour n’a pas fait l’objet d’une nouvelle version depuis des années et cible toujours une version de Node.js dont la fin de vie (EOL) remonte à bien longtemps. Ce n’est pas bon.
C'est le code que vous transmettrez un jour.
Hérité sans contexte
La personne qui héritera de votre base de code n’héritera pas de votre raisonnement. Elle n’aura que des indices épars, avec très peu de contexte. La dépendance que vous avez choisie était probablement le bon choix compte tenu du temps disponible, de l’échéance et des alternatives que vous aviez étudiées. Tout cela avait du sens à l’époque. Mais assurer la transition, c’est une autre histoire. Si personne n’a consigné ce raisonnement par écrit, la personne qui prendra la relève ne pourra pas distinguer une décision mûrement réfléchie d’un choix fortuit.
Imaginez une dépendance bloquée sur une ancienne version, avec un message de commit indiquant : pin, do not bump. Un changement majeur dans l'API ? Un problème de licence ? Une fuite de mémoire sous charge ? Personne ne peut dire si la raison est toujours d'actualité, alors la broche reste en place car y toucher semble plus risqué. Sans le contexte, une décision sensée commence à ressembler à de la négligence.
Du coup, la personne suivante a le choix : soit elle fait aveuglément confiance à votre décision, soit elle la remet en cause en repartant de zéro. Les deux options coûtent cher. La première comporte le risque de perpétuer une décision qui s'est discrètement avérée erronée. La seconde fait perdre du temps à redémarrer de zéro pour aboutir à une conclusion à laquelle vous étiez déjà parvenu.
C’est là qu’un transfert de projet open source peut s’avérer un peu délicat. Un fork reprend le code, l’historique des commits et toute la documentation qui se trouvait dans le dépôt. Il ne reprend pas les notes griffonnées dans un carnet, les fils de discussion sur Slack ni les commentaires sur les tickets, qui contenaient pourtant une grande partie du raisonnement. La personne qui prend la relève doit soit reconstituer ce contexte à la dure, soit le perdre dans un océan de pull requests.
Les transferts ont également lieu en amont. En 2018, le responsable de la maintenance de event-stream, un package npm très populaire, a confié les droits de publication à un bénévole qui avait proposé son aide. Le 9 septembre, selon le rapport d'incident de npm, le nouveau responsable a ajouté flatmap-stream en tant que dépendance avec la plage ^0.1.0. Le 5 octobre, [email protected] livré avec un code obscurci qui ne s’activait que dans la version finale du portefeuille Bitcoin Copay, où il cherchait à s’emparer des clés privées. Toute installation résolvant cette plage à partir de zéro, sans fichier de verrouillage pour la fixer 0.1.0, a été infecté par la version malveillante. Elle est restée là jusqu'à ce qu'un développeur la signale le 20 novembre.
La décomposition est silencieuse, mais la documentation ne l'est pas
Les raisons qui motivent vos choix en matière de dépendances ne vieillissent pas toutes de la même manière. Certaines restent valables pendant une décennie. D’autres non. Certaines s’effondrent presque immédiatement. Cette dégradation n’est généralement pas visible tant que personne ne s’en aperçoit, souvent à la suite d’un scan de sécurité, de l’arrivée d’un produit en fin de vie (EOL) ou d’un audit. La maintenance logicielle et l’héritage open source s’apparentent beaucoup à l’achat d’une vieille maison. Personne ne s’intéresse aux fondations tant qu’elles ne commencent pas à bouger.
npm outdated compare les versions installées à vos plages déclarées et à la dernière version disponible, mais uniquement pour les dépendances directes, sauf si vous ajoutez --all. npm view <package> time indique la date de publication de chaque version. Aucun de ces éléments ne constitue un indicateur de qualité, car une bibliothèque bien établie peut passer des années sans nouvelle version, tandis qu’un dépôt très actif peut tout de même être mal géré.
La gestion des dépendances open source est généralement décrite comme le processus consistant à choisir, mettre à jour et sécuriser les paquets. Maintenir le raisonnement à jour fait également partie de cette liste. Les équipes qui transmettent leurs bases de code en toute transparence laissent des traces : pourquoi une dépendance a été choisie, pourquoi une version a été fixée, et quel est le plan prévu lorsqu’un élément arrive en fin de vie (EOL).
Ce que vous pouvez contrôler
Vous ne pouvez pas tout contrôler, notamment qui sera chargé de la maintenance d’un paquet dans six ans. Vous ne pouvez pas contrôler si un projet dont vous dépendez sera abandonné, changera d’orientation ou fera l’objet d’un fork. Ce que vous pouvez contrôler, c’est la qualité de la documentation de votre processus. Une trace des décisions prises et du raisonnement qui les a motivées fera le bonheur de la prochaine personne qui héritera du code.
Ces trois outils répondent chacun à une question différente. Un ADR (Architecture Decision Record) explique le « pourquoi », souvent sous la forme d’un court fichier Markdown dans docs/adr/ en consignant le contexte, les options envisagées, la décision prise et ses conséquences. Une SBOM (Software Bill of Materials) répond à la question « quoi ? ». Il s'agit d'un inventaire lisible par machine, généralement au format SPDX ou CycloneDX, des composants d'une version donnée, y compris les dépendances transitives lorsque le générateur les recense. La provenance répond aux questions « où » et « comment ». Un paquet publié avec Provenance npm provenant d'un fournisseur de CI/CD dans le cloud pris en charge comporte une attestation qui le relie à son dépôt source et à sa compilation. Cela indique d'où provient le code, mais ne garantit pas qu'il soit sûr.
La documentation rend compte de l'intention. Elle ne peut toutefois pas garantir la fiabilité des paquets eux-mêmes. Les registres publics offrent des mesures de sécurité, notamment des signatures, la vérification de la provenance et la suppression des paquets malveillants, mais de nombreux paquets ne disposent toujours pas d'informations sur leur provenance, et aucun de ces contrôles ne permet de vérifier si les éditeurs actuels sont bien ceux auxquels vous faisiez confiance au moment de l'adoption.
SecureChain adopte une approche différente. La solution recompile les paquets open source à partir de sources vérifiées, dans des environnements sécurisés et isolés, les analyse à la recherche de logiciels malveillants, puis les met à disposition via un référentiel contrôlé. Les paquets sont accompagnés d’une traçabilité SLSA de niveau 3 pour la recompilation, ainsi que de données VEX (Vulnerability Exploitability eXchange) indiquant quelles vulnérabilités connues les affectent, lesquelles ne les affectent pas et lesquelles ont été corrigées. Selon le paquet, ils incluent également une SBOM (liste des composants logiciels) détaillant leurs composants.
Tôt ou tard, un élément de votre arborescence atteindra sa fin de vie en amont, c'est-à-dire le moment où ses responsables de maintenance cesseront de publier des correctifs de sécurité, et il y a de fortes chances que cela se produise après votre départ. Le programme « Endless Lifecycle Support » (ELS) rétroporte les correctifs de sécurité vers les versions prises en charge ayant atteint leur fin de vie, de sorte qu'une version couverte déjà en production continue de bénéficier de correctifs. L’équipe qui hérite de votre base de code n’est pas contrainte de choisir entre l’exploitation d’un système non corrigé et une réécriture d’urgence sous la pression. Cela lui donne le temps de prendre une décision réfléchie concernant la migration, plutôt qu’une décision prise dans la précipitation.
Réflexions finales
Chaque dépendance que vous ajoutez représente un engagement à long terme que vous transmettez à celui ou celle qui assurera la maintenance du projet par la suite. Le code survit aux équipes, aux fonctions et aux entreprises. En documentant les raisons qui sous-tendent vos choix de dépendances, en conservant une trace vérifiable de ce que vous livrez et en anticipant le jour où le support en amont prendra fin, vous donnez à la base de code que vous transmettrez un jour une réelle chance de devenir un atout, et non un piège.
