Utiliser un bot afin de mettre à jour automatiquement les dépendances
75 % des projets contiennent au moins une vulnérabilité et 49 % de ces mêmes projets contiennent des vulnérabilités qui présentent un risque élevé. C’est une des informations qui se dégagent de l’édition 2020 du rapport OSSRA, Open Source Security and Risk Analysis, publié par la société Synopsys.
Je t’ai déjà parlé de cette étude dans un précédent article qui expliquait comment automatiser la détection de vulnérabilités. Tu noteras au passage que l’année dernière seuls 60% des projets contenaient au moins une vulnérabilité. Je n’ai pas suffisamment de détails pour expliquer cette hausse. Au delà du nombre précis ce qui est important c’est de se demander ce que l’on peut mettre en place pour prévenir les risques que cela entraîne. Je pense qu’une bonne approche peut se résumer au moyen de ses deux stratégies :
- Rester constamment à jour
- Éviter les gros changements de type big-bang au profit de mise à jour plus ciblées
Il se trouve que j’ai découvert récemment un outil, Dependabot, qui permet d’automatiser la mise à jour de ces dépendances. Je vais te le présenter dans cet article.
Présentation Générale
Dependabot est une application GitHub qui va accéder à tes repos au moyen d’un access token. Chaque jour, elle analyse tes fichiers de dépendances et recherche celles qui peuvent être mise à jour. Si l’une des dépendances est obsolète, Dependabot ouvre un Pull Request pour la remplacer. Il te reste alors à vérifier l’exécution des tests, analyser le changelog de la bibliothèque et prendre une décision. Accepter la mise à jour en mergeant la PR ou ignorer la nouvelle version.
Ce que je trouve intéressant c’est la variété des langages supportés. On retrouve des langages de backend populaires tels que Javascript, Java et PHP. Mais aussi des langages moins répandus tels que Go, Rust ou encore Elixir.
Quelques Exemples
Afin de rédiger cet article j’ai pris le temps de tester l’outil sur plusieurs projets. Un projet en Java/Maven et un projet en Go. Ces deux stacks techniques sont disponibles respectivement en beta et en alpha. Cela c’est un peu ressenti à l’usage.
Un exemple de PR
Pour ce premier exemple je te montre une PR ajoutée par le bot pour un projet en Go. Comme pour toute PR il est possible de consulter la liste des fichiers modifiés :
Habituellement cela permet de comprendre l’impact des changements de code. Dans le cas d’une mise a jour de dépendance pas tant que ça. Et c’est là que l’outil est bien conçu ! Dans la description de la PR vont être renseignées trois informations. Tout d’abord les release notes, qui indiquent les changements majeurs de la version. Ensuite la liste des commits, cela permet d’avoir une information plus détaillée. Enfin, un score de comptabilité est aussi mentionné.
Comment est calculé ce score ? C’est très simple. Lorsqu’une nouvelle version de dépendance est publiée, Dependabot crée des PRs similaires sur des centaines de dépôts. Pour chaque repos avec l’intégration continue d’activée, Dependabot détermine si la mise à jour casse des tests. Le score de compatibilité est le pourcentage d’exécutions de CI qui ont réussies la mise à jour entre les versions concernées.
Clôture automatique des PRs obsolètes
Une des autres fonctionnalités que j’ai trouvé pratique c’est la clôture automatique des PRs obsolètes. Dans l’exemple ci dessus une version 0.26.0 venait d’être publiée. Le bot a donc automatiquement clôturé la PR concernant la version 0.25.0. Cerise sur le gâteau un commentaire indique que la PR est remplacée par une autre, en proposant un lien vers cette nouvelle PR.
Clôture sans raison
J’ai malheureusement constaté certaines incohérences dans le fonctionnement du bot. Par exemple j’ai vu une PR se voir clôturer sur le projet utilisant Maven.
Ici le bot commente en disant que la dépendance ne peut plus être mise à jour. Ce comportement est apparu après avoir approuvé une autre PR sur ce même repo. Pourtant la dépendance est actuellement en version 2.0.16 et il est donc possible de la mettre à jour.
Dans ce cas j’ai interagis avec le bot en commentant la PR. Tout t’abord pour la réouvrir, puis pour effectuer un rebase. C’est justement une des forces de l’outil, la possibilité de pouvoir interagir avec le bot directement dans la PR. Malheureusement cela n’a pas suffit. La PR était systématiquement refermée par le bot. L’outil étant en version beta pour les projets Maven, il n’est pas surprenant que quelques bugs puissent exister.
Besoin d’un petit coup de ménage
Sur un des projets en Go j’ai aussi constaté un comportement lacunaire. Suite à la mise à jour de testify, une dépendance transitive n’a pas été supprimée. Si j’ouvre le projet avec Visual Studio Code je constate un warning :
C’est d’autant plus dommage que c’est trivial à corriger. Si je tape la commande suivante :
go mod tidy
Le fichier go.mod est alors nettoyé. La dépendance transitive “gopkg.in/yaml.v2 v2.2.8” est supprimée.
Configuration Avancée
Il est possible de configurer plus finement le bot. Pour cela on utilise un fichier dependabot.yml, au format YAML donc. Il est nécessaire de placer ce fichier dans le repertoire .github du repo.
Les options disponibles concernent la fréquence des vérifications, le nommage des PRs, la stratégie de rebase et bien d’autres. Je te laisse consulter la documentation officielle pour obtenir une liste exhaustive.
Déployer sa propre instance
Les gestionnaires de sources actuellement compatibles avec Dependabot sont : GitHub, GitHub Enterprise, GitLab et Azure Devops. D’autre part je pense que certaines organisations peuvent être frileuses à l’idée d’autoriser l’accès à leur code source à un outil en ligne. Pour palier à ces limitations il semble qu’il soit possible de déployer sa propre instance de Dependabot.
Le projet étant implémenté en Ruby il faut compiler le projet puis le déployer. Pour faire fonctionner Dependabot et utiliser l’ensemble des langages supportés, tu auras besoin d’installer Ruby, Python, PHP, Elixir, Node, Go, Elm et Rust. Cependant, si tu souhaites l’utiliser pour un seul langage, tu peux te contenter d’installer ce langage ainsi que Ruby.
Autre possibilité : Docker. Bien qu’il soit possible de déployer Dependabot sans utiliser Docker, c’est l’approche recommandée par les concepteurs du projet. En effet l’image Docker contient déjà toutes les dépendances pour pouvoir utiliser les différents langages supportés. Je n’ai personnellement pas déployé ma propre instance. Je ne peux donc pas te dire si c’est rapide à réaliser.
Considérations diverses
Avant de conclure cet article je voudrais partager quelques remarques. Tout d’abord la nécessité des tests. Pour pouvoir utiliser cet outil avec confiance, il faut une base de code avec des tests. Plus encore, il faut une couverture de tests suffisantes. Un build vert doit garantir que l’on n’aura pas de régressions en production. On entend parfois dire que les tests coûtent chers. Je pense d’une part que cela dépend de l’expérience des développeurs de l’équipe en la matière. Pratiquer le TDD quand on ne l’a jamais fait avant prend du temps. Quand on est habitué c’est quasiment sans incidence sur le temps de développement. D’autre part le léger surcoût est remboursé sur la durée. Pouvoir mettre à jour fréquemment ses dépendances et ainsi limiter la probabilité qu’une fuite de données ou un hack ait lieu, ce n’est pas du luxe !
Une autre considération est le nombre de mise à jour qu’un projet en moyenne aura chaque semaine avec Dependabot. Dans un billet de blog, GitHub nous explique qu’un projet Ruby compte en moyenne 125 dépendances. 38 sont directes et 87 sont des dépendances transitives. Un projet Ruby typique reçoit habituellement deux mises à jour de dépendances par semaine. Parmi ces mises à jour, 94 % ne cassent pas le build. Cela signifie qu’en moyenne, tu n’auras besoin d’écrire du code en réponse à une mise à jour de dépendance qu’une fois tous les deux mois. Le reste du temps, tu peux simplement cliquer sur “merge” et continuer à opérer avec des dépendances sécurisées et mises à jour.
Enfin, il faut savoir que Dependabot a d’abord été un projet indépendant de GitHub. L’acquisition par GitHub a été annoncée en milieu d’année 2019. Soit environ un an après le rachat de GitHub par Microsoft. Il est intéressant de constater les nombreux changements apparus depuis ce rachat. N’oublions pas que Microsoft est aussi un cloud provider. Les cloud providers tels que AWS, GCP et Microsoft Azure proposent fréquemment de nouveaux services DevOps (gestionnaires de code source, de chaînes d’intégration, etc…). Cette semaine par exemple AWS a présenté son outil AWS CodeArtifact. Il va donc être interessant de voir si les services additionnels de GitHub vont permettre à Microsoft d’améliorer Azure Devops et ainsi obtenir un avantage concurrentiel.
Si tu es arrivé jusqu’ici, merci beaucoup d’avoir lu cet article !
Si il t’a plu, pense à me laisser un clap et à me suivre.
N’hésite pas à me laisser un commentaire ou une question, directement en bas de cet article ou en m’envoyant un message sur LinkedIn.
Photo de couverture par Rock’n Roll Monkey.
Cet article a initialement été publié sur Medium.