2 points par GN⁺ 2024-03-27 | 1 commentaires | Partager sur WhatsApp
  • Dans l’écosystème Rust, dès qu’une dépendance abandonnée apparaît dans RUSTSEC, même une bibliothèque qui ne posait pas directement problème devient une dette technique via ses utilisateurs et la CI
  • insta dépendait de yaml-rust, où les demandes de fonctionnalités et les bugs s’étaient accumulés après le désintérêt progressif de son auteur initial
  • Après l’inscription dans RUSTSEC, les CI des utilisateurs directs et indirects ont échoué ; en termes financiers, cela revient à une dégradation de note et à un appel de marge
  • Les bibliothèques de remplacement ou les forks n’étaient pas non plus des solutions évidentes : même en changeant de dépendance, la charge de maintenance et le risque lié aux nouvelles dépendances subsistent
  • La réponse finale a consisté à vendoriser le code de yaml-rust dans insta, ce qui a conduit à la critique selon laquelle cela ressemblait à un CDO empaquetant une mauvaise dette technique en AAA

Comment la dépendance yaml-rust s’est révélée être une dette technique

  • insta dépendait de yaml-rust, et les issues s’y accumulaient depuis que son auteur initial s’en était désintéressé
    • Certaines étaient des demandes de fonctionnalités, d’autres de vrais bugs
    • Le mainteneur de insta ne rencontrait pas directement ces problèmes, mais le fait qu’il s’agisse d’une dépendance abandonnée en faisait une dette technique
  • La situation a changé lorsque yaml-rust a fait l’objet d’une discussion pour son ajout à la base de données RUSTSEC
    • Dans l’analogie financière, RUSTSEC joue le rôle d’agence de notation
    • Après l’inscription, la CI de nombreux projets utilisant yaml-rust directement ou indirectement a commencé à échouer en quelques minutes
    • Des utilisateurs ont signalé au mainteneur de insta le problème lié à l’utilisation de yaml-rust, ce qui correspond, dans l’analogie financière, à un appel de marge

Les options et la réponse réelle

  • Passer à une alternative n’était pas très séduisant
    • L’une des alternatives est un fork de yaml-rust, avec un seul mainteneur, et ajoute trois dépendances
    • L’une d’elles avait déjà reçu une note « B- »
    • Une autre option de l’écosystème avait pris la décision de changer les valeurs par défaut avant d’être mise en cause
  • Faire son propre fork n’était pas non plus une solution de fond
    • Une bibliothèque forkée entraîne les mêmes exigences de maintenance
    • Si les rapports de bugs ne sont pas traités, elle finira par être pointée du doigt comme l’ancien yaml-rust
    • Un fork peut donc faire gagner du temps, mais ne fait pas disparaître le problème
  • La réponse réelle a été la vendorisation, en intégrant le code de yaml-rust dans insta
    • insta est désormais une combinaison du code de insta et de yaml-rust
    • C’est une structure qui revient à rehausser une mauvaise dette technique en AAA
    • Le CDO du titre désigne les obligations adossées à des actifs, rendues tristement célèbres lors de la crise financière de 2007
  • Le bilan final est proche de « personne n’a gagné »
    • Le code problématique n’a pas disparu ; il a simplement été déplacé à l’intérieur de insta
    • La pression liée à son apparence de dépendance externe a diminué, mais la charge de maintenance elle-même demeure

1 commentaires

 
GN⁺ 2024-03-27
Avis Hacker News
  • Pour parler franchement, l’auteur du parseur YAML le plus populaire, serde_yaml(https://lib.rs/crates/serde_yaml), a soudainement lâché l’affaire sans préavis ni désignation d’un mainteneur successeur, et l’a marqué comme deprecated et unmaintained.
    Ce n’est pas exactement comme left-pad. Le package fonctionne toujours et crates.io n’autorise pas non plus sa suppression, mais ce package est utilisé par 4 000 autres crates.
    Les outils d’audit et de mise à jour automatique vont commencer à signaler comme problématique l’utilisation d’une crate non maintenue.

    • Ce n’est pas exact. Le texte original dit qu’il dépend de yaml-rust(https://github.com/chyh1990/yaml-rust), et que ce projet n’est désormais plus maintenu.
      En même temps, serde_yaml a aussi été marqué comme unmaintained afin d’empêcher que les gens migrent massivement vers lui.
    • Ça ne s’applique pas ici, mais une bibliothèque sans dépendances peut parfois être réellement complète fonctionnellement, au point de n’avoir presque plus besoin de changements hors correctifs de sécurité.
      J’aimerais que les outils d’audit et les politiques d’entreprise soient assez intelligents pour distinguer ces cas au lieu de continuer à alerter sur « non maintenu » au seul motif qu’il n’y a pas eu de commit récent.
    • Cet auteur n’est-il pas quelqu’un de plutôt prolifique dans l’écosystème des bibliothèques Rust ? Il n’a pas pris ce genre de mesure pour ses autres bibliothèques.
      Je me demande s’il y a plus d’informations.
  • L’acronyme CDO, sorti sans explication, ne m’a pas été immédiatement clair, mais vu que le texte emploie plusieurs fois le terme collateralized, il s’agit probablement d’une obligation adossée à des actifs (collateralized debt obligation).
    https://en.wikipedia.org/wiki/Collateralized_debt_obligation
    Au début, j’ai pensé à chief data officer.

    • La métaphore ici tient au fait qu’un CDO est un produit financier constitué à partir d’autres dettes.
      Plus précisément, en 2008, il s’agissait de quelque chose comme une propriété partielle sur plusieurs prêts hypothécaires, et lorsque les prêts hypothécaires pourris ont fait défaut, les CDO se sont effondrés avec eux [1].
      Quoi qu’il en soit, ce que l’auteur a en tête ressemble moins à une métaphore fondée sur la dette qu’à un risque systémique, comme leftpad ou une panne DNS en cascade. L’une des grandes raisons pour lesquelles 2008 a été un tel chaos tenait davantage à des problèmes systémiques qu’au simple fait d’avoir créé des produits à partir de dettes [2].
      1. Fait amusant : avant 2008, la demande de produits financiers adossés à des prêts hypothécaires est devenue si énorme qu’on a commencé à créer des CDO faits de CDO. Accorder un prêt hypothécaire à quelqu’un sans emploi, puis l’emballer dans un produit noté AAA, en faisait un produit rentable et facile à vendre.
      2. Il y a aussi le fait que les ratios de levier de Lehman et Bear Stearns étaient de 30 à 40 fois. C’est délirant.
    • Exactement. L’implication est que si l’on crée des incitations à regrouper de la mauvaise dette technique dans des packages réputés, c’est-à-dire des packages AAA, l’écosystème Rust peut se diriger vers une bulle puis finir par s’effondrer.
      Cela signifie aussi que les agences de notation censées jouer un rôle de régulateur ont déjà été capturées.
    • Il faut regarder The Big Short. C’est là que j’ai appris ce qu’était un CDO.
    • Je viens de réaliser qu’il existe maintenant une génération qui était trop jeune pour lire les gros titres de l’époque où les CDO faisaient s’écrouler l’économie mondiale. Ça me donne un coup de vieux.
    • J’ai pensé à https://en.wikipedia.org/wiki/Collaboration_Data_Objects ;)
  • Que ce soit une « victoire » dépend tellement de la définition de « victoire » que je n’ai pas envie d’en débattre, mais il y a clairement des avantages. Un chemin de code vulnérable qui ne s’exécute pas et qui n’est pas atteignable depuis une bibliothèque externe devient maintenant un chemin de code sûr.
    Bien sûr, la situation reste préoccupante, mais elle est sûre.
    Ce genre de vendoring a aussi un autre avantage. Si votre bibliothèque dispose d’une couverture de tests solide, vous pouvez lancer des outils de couverture de code sur la bibliothèque nouvellement importée.
    Modifier la bibliothèque peut être difficile, mais il peut être relativement facile de supprimer progressivement les parties que votre propre code ne touche pas. Cela dépend de la structure, mais si tout le code vulnérable est supprimé, le gain est évident ; et si vous découvrez que vous en utilisiez en fait une partie, le gain peut être encore plus clair.
    Autrement dit, forker une bibliothèque pour en assurer la maintenance publique est une lourde responsabilité, mais vendorer uniquement les parties dont on a besoin et les retoucher représente une charge bien moindre. Même sans élagage effectif, le simple fait de rendre cela facile est déjà un progrès.
    Il y a évidemment aussi des inconvénients, mais tout n’est pas mauvais.

    • En réalité, c’est un très bon résultat. Toute dépendance est un risque de sécurité, et l’argument contre le vendoring de toutes les dépendances repose sur l’idée qu’il faut garder les dépendances externes à jour pour éviter les problèmes de sécurité connus.
      Mais si plus personne ne surveille une dépendance externe, cet argument disparaît, et cette dépendance devient un fardeau.
      Le fait que la dépendance soit marquée comme abandoned et commence à apparaître dans les rapports de sécurité est le comportement souhaité. Cela permet de décider en connaissance de cause s’il faut la vendorer, c’est-à-dire supprimer le risque qu’un « mauvais acteur y glisse quelque chose en douce », ou choisir une autre option.
      C’est aussi bien que le système de build de Rust rende cela facile.
    • Il vaut la peine de noter que chez Google, toutes les dépendances tierces doivent, par politique interne, être vendorées.
      En général, une seule version de cette dépendance est aussi autorisée dans l’ensemble de l’immense monorepo. Cela a peut-être changé depuis mon passage.
      Et chaque dépendance third_party a un responsable désigné ou des OWNERS.
      Cela permet de maintenir un ordre relatif.
      Cela dit, ce type de discipline imposée peut bien convenir à une organisation comme Google, qui dispose de suffisamment de temps et d’argent et n’est pas prisonnière de la philosophie « move fast and break things ». Je ne sais pas dans quelle mesure cela fonctionnerait bien dans une startup.
    • Je me demande s’il existe des outils permettant au mainteneur d’une bibliothèque de calculer la couverture de code transitive de sa bibliothèque, par exemple quelles suites de tests d’autres bibliothèques l’utilisent.
      Dans un certain sens, comme les autres bibliothèques fournissent des appels presque aléatoires, cela pourrait constituer une stratégie de test intéressante, proche d’un fuzz test orienté.
  • J’ai observé le même schéma dans l’écosystème JS npm
    npm audit se comporte le plus souvent comme le garçon qui criait au loup en matière de sécurité, et, si la licence le permet, rapatrier le code en interne est l’un des moyens les plus stables de ne pas être submergé par de faux problèmes signalés par les utilisateurs
    Souvent, les utilisateurs ne comprennent pas le contexte, ou s’en moquent parce que les politiques de leur employeur ont été élaborées dans un endroit déconnecté de la réalité
    Une expression régulière utilisée dans une partie du code d’une dépendance transitive de la pipeline de build ne peut pas forcément être exploitée en pratique pour une attaque par déni de service
    Les « problèmes » dans des dépendances transitives profondes peuvent être particulièrement pénibles à contourner. Structurellement, il est souvent difficile de prouver techniquement des faits comme « nous n’empruntons jamais ce chemin de code, donc nous ne sommes pas affectés par la faille » ou « le seul cas où ce chemin est emprunté implique une entrée de confiance dans un environnement hors ligne »

    • Dans le monde JS, les problèmes touchant les dépendances de développement sont un vrai casse-tête
      Il existe bien des scénarios où ce genre de problème compte. Par exemple, un outil de build compromis peut injecter du code malveillant dans la bibliothèque en cours de compilation
      Mais ces cas sont extrêmement rares, et ils se retrouvent noyés dans une vague d’expressions régulières pouvant potentiellement provoquer un déni de service, qui en pratique n’ont pas d’importance parce qu’elles ne sont invoquées que pendant le build
      Ajoutez à cela le fait qu’un outil de build courant traîne un arbre d’environ cinquante milliards de dépendances transitives, et cela devient vraiment pénible
      À mon avis, les outils qui signalent ce type de problèmes devraient distinguer « exploitable en cas de redistribution » et « exploitable en cas d’utilisation dans la pipeline de build »
    • Même si l’on n’emprunte pas aujourd’hui ce chemin problématique, une mise à jour ultérieure de la dépendance peut faire en sorte qu’on l’emprunte
  • Dans « une dette technique douteuse vient soudain de recevoir une note AAA », le mot « soudain » semble vouloir dire qu’il n’a pas de sens que le même code reçoive une meilleure notation de dette simplement parce qu’il a été vendorizé
    Mais cela ne considère que la valeur du code lui-même, et passe à côté de la partie la plus importante de la proposition de valeur globale
    Quand un mainteneur rapatrie le code en interne, ce code devient désormais sa propriété. Si un mainteneur actif vendorise le code d’un projet mort, il existe alors une personne active capable de répondre aux issues, de relire les pull requests et de corriger les bugs, ce qui augmente la valeur de ce code
    Pour prendre une autre analogie, c’est comme confier un animal de compagnie négligé à un nouveau propriétaire : il sera mieux soigné, en meilleure santé et vivra plus longtemps, donc sa valeur augmente

    • Cela ne vaut que pour les personnes qui utilisent cette dépendance indirectement. Dans ce cas, il y a probablement très peu d’yeux pour repérer les bugs
      Le mainteneur devra aussi s’adapter à une grosse base de code inconnue, ce qui créera une barrière à l’entrée pour implémenter des correctifs ou les relire
  • C’est un peu hors sujet et controversé, mais je pense qu’un gestionnaire de paquets fondé sur les sources aura du mal à éviter de terribles problèmes s’il ne garantit pas le droit légal pour le registre de reprendre de force la maintenance des paquets publiés
    Je parle de problèmes comme l’abandon, les modifications malveillantes, la suppression malveillante ou l’usurpation d’identité
    Si l’on juge qu’un paquet est suffisamment important pour une communauté plus large, il faut un moyen de retirer l’entrée du registre des mains de son propriétaire initial et de la faire pointer vers un fork
    Évidemment, ce genre de mesure entraînera beaucoup de drames, mais elle peut protéger activement les utilisateurs en aval

    • Je ne pense pas que ce soit un gros problème. La beauté de l’open source, c’est qu’on peut forker
      Le vrai problème, c’est que maintenir un projet open source demande du temps et des efforts, et qu’il n’est pas facile de trouver quelqu’un d’autre qui ait du temps libre
    • Un gestionnaire de paquets fondé sur les sources qui confisque la propriété d’un paquet quand le registre l’estime nécessaire devrait avoir du mal à attirer des contributions
      C’est encore moins attirant qu’un modèle de copyright où toutes les contributions deviennent automatiquement la propriété d’un groupe donné
      On pourrait soutenir que GitHub, tout en hébergeant du logiciel libre, est spécialisé dans la violation du copyright de ces logiciels, mais au moins, pour l’instant, il n’essaie pas de confisquer la propriété nominale des paquets
    • Si la raison est l’importance pour la communauté, c’est au mieux quelque chose à traiter au niveau du paquet, pas au niveau du registre
      Puisqu’on parle de communauté, il ne s’agit pas d’une relation client-fournisseur, et la plupart des paquets ne sont importants qu’au point d’être fournis gratuitement
      Cela pourrait finir en montants humiliants du genre « on vous donne 10 dollars par mois pour maintenir 100 000 paquets pour nous »
    • Ces problèmes ne sont pas tous de même nature. Un paquet figé signifie que les dépendances en aval doivent prendre une décision, mais il existe suffisamment d’options, y compris le vendoring
      Internet regorge de code source non maintenu, simplement publié une fois tel quel. Personne n’a la garantie de recevoir des mises à jour pour du code gratuit
    • L’alternative, c’est le problème Kik NPM. Dans les deux cas, c’est mauvais
  • On a eu pas mal de chance que quelqu’un ait déjà forké yaml-rust pour créer yaml-rust2(https://github.com/Ethiraric/yaml-rust2/blob/master/document...)
    C’est aussi appréciable que ce fork passe entièrement la suite de tests YAML et soit plus rapide dans les benchmarks. La migration semble également simple
    Au fond, le problème demeure. Nous dépendons aujourd’hui du travail d’autres personnes qui acceptent de fournir gratuitement leur travail, mais cela ne durera peut-être pas éternellement
    Je ne sais pas s’il existe une solution de contournement, à part les rémunérer pour leur temps et leurs efforts et espérer qu’elles continuent à faire du bon travail

    • yaml-rust était à l’origine une implémentation pure Rust, et son slogan le disait littéralement
      “A pure rust YAML implementation.”
      À l’inverse, serde_yaml était plus difficile à considérer comme du Rust pur, puisqu’il dépendait de unsafe-libyaml, une conversion de libyaml via c2rust
    • Les projets dont le bus factor est de 1 sont intrinsèquement risqués
      Sur un horizon temporel suffisamment long, la probabilité qu’un mainteneur open source abandonne un projet est de 1
      La seule façon d’éviter cela est de faire des projets maintenus par une seule personne un tabou
    • Ma solution par défaut à ce problème est d’éviter autant que possible les dépendances tierces
      Ce n’est probablement pas une option pour les projets Rust, parce que les personnes qui ont conçu la bibliothèque standard de Rust veulent qu’elle reste minimale
      Mais c’est certainement possible lorsqu’on utilise d’autres langages dotés d’une bibliothèque standard suffisamment complète
      Comme j’utilise volontairement des langages qui intègrent ce dont j’ai besoin, je n’utilise presque aucune bibliothèque externe en dehors des pilotes de base de données
  • Toute cette situation me paraît un peu absurde. Si le code fonctionne et que c’est le cas depuis des années, je ne vois pas en quoi le fait qu’il ne soit pas maintenu pose problème.
    S’il n’a pas besoin d’être modifié et qu’on connaît ses limites et ses fonctionnalités, ça va.
    Le code ne se détériore pas tout seul. Il m’est arrivé plusieurs fois d’emprunter ou d’intégrer du code vieux de plusieurs décennies et de l’utiliser sans problème.
    Personnellement, je pense que j’ignorerais simplement toutes les plaintes concernant cette bibliothèque et que je passerais à autre chose.

    • La réponse évidente, c’est qu’elle ne « fonctionne » pas à bien des égards.
      Beaucoup de bugs se sont déjà accumulés et, même quand des correctifs existent, ils ne seront jamais appliqués. Il suffit de regarder https://github.com/chyh1990/yaml-rust/issues et /pulls.
    • Quand tu dis avoir emprunté ou intégré du code vieux de plusieurs décennies, ce n’est pas ce que l’auteur a fait en vendoriant la bibliothèque dans son propre projet ?
      Cela revient à prendre en charge la maintenance du morceau de code qu’on utilise.
      Parfois, ça peut être mauvais : ça peut tourner au copier-coller ou à la duplication d’efforts.
      Mais si un composant tiers n’est plus du tout maintenu, c’est assez compréhensible.
  • Oui, les dépendances peuvent être vendoriées. Pour les dépendances « presque terminées », dont le développement et la maintenance ont ralenti, c’est globalement ce qu’on fait depuis 20 ans.
    Cela dit, je n’ai jamais travaillé dans un langage où les « batteries ne sont pas incluses ».

  • Y aurait-il un moyen de créer quelque chose comme cargo vendor --aggressive, qui élaguerait tout le code mort des dépendances par rapport à ma crate ?
    Je me demande si cela pourrait rendre le problème du « revoir ses dépendances » plus gérable.
    C’est un peu en marge du sujet principal de l’article, mais c’est lié au fait qu’au final, le choix des dépendances et toutes les responsabilités qui en découlent nous incombent.
    Il semble y avoir de la place pour des outils qui nous aideraient à mieux assumer la responsabilité de ce qui est effectivement compilé dans une crate.

    • Et alors, on copie aussi tous les rapports de bugs de la bibliothèque dans son propre outil de suivi ?
      Sinon, je ne vois pas vraiment ce que ça améliore.