Un site e-commerce moderne, c’est un peu de code qu’on écrit et beaucoup de code qu’on importe. Le site, son API, le backend Magento, les fonctions serverless, l’infrastructure : chaque dépôt tire des centaines de paquets, et chacun de ces paquets peut publier demain une faille connue de tous.
Nous avions déjà un scan. Il tournait chaque jour, trouvait des vulnérabilités et ouvrait des tickets. Et c’est là que tout s’arrêtait : détecter ne corrige rien. Un ticket attend qu’un développeur ait le temps, cherche la bonne version, ouvre la merge request, relance la CI. Pendant ce temps, le correctif attend.
Le 23 septembre, nous avons remplacé cette chaîne par une boucle qui corrige elle-même : Renovate, auto-hébergé, qui ouvre les correctifs, et un scan quotidien qui vérifie ce qui reste exposé. Voici la méthode, parce que la sécurité de nos clients passe aussi par là.
Le principe : une merge request, c’est un signal
Renovate est connu pour ouvrir une merge request à chaque nouvelle version d’une dépendance. Utilisé ainsi sur tous nos dépôts, il noierait une équipe de trois développeurs sous les mises à jour, et les correctifs de sécurité se perdraient dans le bruit.
Nous avons pris le contre-pied : toutes les mises à jour sont coupées par défaut. Seules les alertes de vulnérabilité de la base OSV réactivent un paquet. Quand une faille est publiée sur une dépendance que nous utilisons, une merge request s’ouvre dans la foulée, avec le suffixe [SECURITY]. Si une merge request apparaît, c’est qu’il y a une raison de la traiter.
packageRules: [
{
description: 'Security updates only: vulnerability alerts re-enable the affected packages',
matchPackageNames: ['*'],
enabled: false,
},
// ...
],
osvVulnerabilityAlerts: true,
Toute la politique tient dans un seul fichier de configuration, dans un projet dédié. Aucun dépôt ne porte de fichier Renovate : changer une règle, c’est une merge request relue à un seul endroit, et elle s’applique partout.
Renovate ne tourne pas comme un service. Il n’a ni serveur ni base de données : chaque passage est un job de CI planifié, qui démarre, travaille sur chaque dépôt, puis s’arrête.
La boucle des quatre heures
Toutes les quatre heures, trois sources peuvent ouvrir une merge request.
Les dépendances directes. Les alertes OSV couvrent npm, Composer, Maven et PyPI, ainsi que les versions de Composer épinglées dans nos Dockerfiles. Une faille publiée le matin a son correctif proposé dans les quatre heures.
Les dépendances indirectes. C’est le trou classique : les alertes ne voient que les paquets déclarés directement, alors qu’une faille se cache souvent trois niveaux plus bas. Un job maison s’en charge après chaque passage de Renovate. Il scanne chaque dépôt, garde les vulnérabilités graves des dépendances indirectes pour lesquelles un correctif existe, et ne monte que ces paquets, dans les versions que leurs parents acceptent déjà, en touchant uniquement le lockfile. Puis il rescanne. Il n’ouvre sa merge request que si des failles ont disparu et qu’aucune n’est apparue. Quand le correctif sort des versions acceptées par le parent, il ne force rien : il le signale, et c’est un humain qui décide de monter le parent.
Les rafraîchissements du lundi. Certaines versions n’ont aucun flux de vulnérabilités : les images de base de nos Dockerfiles, les outils installés par version, les lockfiles eux-mêmes. Chaque lundi avant 6 h, Renovate les regroupe dans une merge request par dépôt. Les images restent sur leur version majeure, PHP et Python sur leur version mineure : changer de majeure est une migration, pas un rafraîchissement. Ces mises à jour ne proposent que des versions publiées depuis au moins trois jours, le temps qu’un paquet compromis soit repéré et retiré par son registre.
Pour qu’une version épinglée dans un Dockerfile soit suivie, il suffit d’un commentaire au-dessus :
# renovate: datasource=github-releases depName=helm/helm
ENV HELM_VERSION="v4.3.0"
# renovate: datasource=github-releases depName=jkroepke/helm-secrets extractVersion=^v(?<version>.*)$
ARG HELM_SECRETS_VERSION=4.7.5
Qui a le droit de fusionner seul
C’est la question qui inquiète, à juste titre. Notre réponse tient en quelques règles strictes.
Trois dépôts seulement : le site, le backend et l’application des boutiques. Ce sont ceux où une fusion part d’abord en préproduction, et où la production reste une release manuelle, après recette. Dans les autres dépôts, où une fusion part directement en production, tout passe par un humain.
Patch et minor seulement. Les versions majeures attendent toujours une personne : elles peuvent demander d’adapter le code.
Une pipeline verte, obligatoire. Renovate fusionne lui-même, au passage suivant, une fois la pipeline de la merge request au vert : tests, build, environnement éphémère. Nous avons désactivé la fusion automatique déléguée à GitLab, qui fusionne immédiatement une merge request sans pipeline quand on lui demande de fusionner « au succès de la pipeline ». Une branche sans pipeline n’est jamais fusionnée seule.
Une revue de code pour chaque correctif de sécurité. Notre agent de revue relit chaque merge request [SECURITY] et pose un statut sur le commit. En dessous de 80/100, rien ne fusionne. Pour ne pas relancer une revue à chaque passage, Renovate ne rebase une branche qu’en cas de conflit.
Pas le vendredi, pas la nuit. Les fusions automatiques n’ont lieu que du lundi au jeudi, entre 9 h et 17 h. Si quelque chose casse en préproduction, quelqu’un est là pour le voir.
Jamais le cœur de Magento ni un paquet que nous patchons. Notre backend applique des patchs Composer sur certains paquets. Une mise à jour peut laisser un patch s’appliquer sans erreur tout en changeant son sens, et aucun test ne le verrait. Avant chaque passage, un script relit la liste des paquets patchés dans le composer.json. S’il ne peut pas la lire, plus aucune mise à jour du backend ne fusionne seule : la règle échoue du côté prudent.
Enfin, Renovate ouvre au plus cinq merge requests à la fois et deux par heure. Nos pipelines partagent les mêmes runners que les déploiements, et une vague de mises à jour ne doit pas retarder une mise en production.
Le contrôle quotidien
Renovate corrige ce qu’il sait corriger. Il reste à vérifier ce qui est encore exposé, et c’est le rôle du scan quotidien.
Chaque matin à 6 h 30, Trivy analyse :
- la branche principale de chaque dépôt, c’est-à-dire le code en production : dépendances de production et secrets commités par erreur ;
- les charts Helm et manifestes Kubernetes, pour les erreurs de configuration critiques ;
- 25 images de conteneur, publiques et privées. Les images privées sont lues par un rôle AWS en lecture seule, qui ne fait confiance qu’aux pipelines de la branche principale du projet, par OIDC, sans aucune clé stockée.
C’est le seul scan de dépendances de tous nos dépôts. Aucun n’en lance un de son côté : un seul endroit, un seul seuil, un seul canal d’alerte.
Un constat n’a que trois issues possibles.
Le corriger. La plupart du temps, la merge request de Renovate est déjà ouverte : il suffit de la fusionner.
Reconstruire. Une faille système dans l’une de nos images de base déclenche sa reconstruction, avec les derniers paquets de la distribution. Le scan du lendemain confirme la correction.
L’accepter, par écrit et pour un temps limité. Parfois, aucun correctif n’existe encore en amont, ou il demande une migration qui se planifie. Le risque s’accepte alors par une merge request dans le projet, avec un motif et une date d’expiration :
vulnerabilities:
- id: CVE-AAAA-NNNNN
statement: "Correctif uniquement dans la majeure suivante, migration planifiée"
expired_at: 2026-11-30
La CI du projet refuse toute acceptation sans date. Une acceptation sans date serait permanente, et une acceptation permanente, c’est un oubli. À l’expiration, le constat refait passer le scan au rouge, jusqu’à sa correction ou une nouvelle décision explicite.
L’existant a suivi la même règle dès le premier jour : une acceptation datée et courte, après quoi chaque constat restant redevient une alerte.
Surveiller la surveillance
Un système de sécurité qui s’arrête sans prévenir est pire que pas de système : il donne une fausse assurance. Un chien de garde tourne donc chaque jour, et passe au rouge si :
- aucun passage de Renovate n’a réussi depuis 24 heures ;
- une merge request de sécurité est ouverte depuis plus de trois jours ouvrés ;
- la revue d’un correctif de sécurité est en attente depuis plus d’une heure.
Chaque action du système est annoncée dans un canal Slack dédié : merge request ouverte, fusionnée automatiquement, reconstruction lancée, constat du scan, job en échec avec sa cause. Personne n’a besoin d’aller chercher l’information.
Dernier détail qui compte : les planifications appartiennent à un compte de service, pas à une personne. Elles continuent de tourner quand quelqu’un part en vacances, change d’équipe ou voit son jeton expirer.
Deux semaines plus tard
Au 7 octobre, 37 merge requests de sécurité ou de maintenance ont été fusionnées sur cinq dépôts. Une partie l’a été sans intervention humaine, avec une pipeline verte et une revue validée. Pour le reste, le travail d’un développeur se résume à relire et fusionner une merge request déjà prête, au lieu de partir d’un ticket.
La boucle a aussi trouvé ses propres limites, et nous les avons corrigées au fil de l’eau : des outils épinglés à deux endroits qui devaient bouger ensemble, des connecteurs de notre bus d’intégration sans aucun flux de vulnérabilités, qui ont maintenant leur rafraîchissement hebdomadaire, ou des images de base à reconstruire automatiquement dès qu’une faille système y est trouvée.
Ce qu’on a appris
- Détecter ne suffit pas. Un ticket de vulnérabilité est une tâche de plus. Une merge request prête à fusionner est une décision de cinq minutes.
- Moins de merge requests, mieux traitées. Couper toutes les mises à jour sauf la sécurité a rendu chaque merge request légitime.
- L’automatisation se mérite. La fusion automatique n’existe que là où une préproduction et une release manuelle protègent la production, et seulement pour des mises à jour mineures, testées et relues.
- Accepter un risque est une décision, donc elle a une date. Une exception sans échéance devient une faille oubliée.
- Surveiller la surveillance. Un outil de sécurité qui s’arrête en silence est le pire des scénarios. Le chien de garde est aussi important que le scan.
La sécurité des dépendances n’est pas un projet qu’on termine. C’est une boucle qui tourne toutes les quatre heures, et notre travail consiste à faire en sorte qu’elle ne s’arrête jamais.