- Dans Git,
--n’est pas un terminateur d’options générique : il sépare les révisions des spécifications de chemin. Pour transmettre en toute sécurité une révision non fiable, il faut donc utiliser--end-of-options, pris en charge depuis Git 2.24.0 - Dans
git log --end-of-options "$rev" -- "$path", le premier marqueur sépare les options et la révision, tandis que le second--sépare la révision et le chemin ; ils ne sont pas interchangeables - Même sans shell, en exécutant directement un tableau
argv, si une entrée commençant par un tiret est interprétée comme une option telle que--upload-pack,core.sshCommandouProxyCommand, cela peut provoquer une injection d’argument CWE-88 - Sur 19 gestionnaires de paquets examinés, 17 exécutent le binaire Git comme méthode par défaut ou unique, mais le seul outil à utiliser
--end-of-optionsétaitcmd/gode Go - Les corrections de fond imposent un coût de compatibilité, car elles nécessitent de relever la version minimale de Git à 2.24.0, 2.30.0 ou 2.43.1 selon les sous-commandes ; les bibliothèques Git suppriment la frontière d’injection d’arguments, mais doivent suivre elles-mêmes les correctifs de sécurité d’upstream pour les checkout
Différence entre -- et --end-of-options
- Dans les outils Unix classiques,
--marque la fin de l’interprétation des options ; ainsirm -- -ftraite-fcomme un nom de fichier, et non comme l’option de suppression forcée - Git utilise depuis longtemps
--comme séparateur entre révisions et spécifications de chemin (pathspec)git log fooest ambigu : s’agit-il d’une branche nomméefooou d’un fichier ?git log main -- README.mddésigne les commits demainqui ont modifiéREADME.md
- À cause de cette conception, il n’existait pas de marqueur de fin d’options à l’emplacement des révisions ; dans
git log "$rev", si$revcommence par un tiret, Git l’interprète comme une option - Le commit qui a introduit
--end-of-optionsindique qu’un marqueur distinct était nécessaire pour séparer les options des révisions, car le--existant servait déjà à distinguer révisions et spécifications de chemin --end-of-optionsest documenté dans gitcli(7) et a été ajouté en novembre 2019 avec la sortie de Git 2.24.0
Utilisation correcte selon les commandes
git clone -- "$url"suit la convention POSIX pourclone: le--placé avant l’URL met fin à l’interprétation des options- Le
--final dansgit checkout "$ref" --indique que$refest une révision et non un nom de fichier, mais il n’empêche pas$refd’être d’abord interprété comme une option - Pour transmettre en toute sécurité une révision et un chemin non fiables, il faut utiliser les deux marqueurs, comme dans
git log --end-of-options "$rev" -- "$path"--end-of-optionssépare les options et la révision--sépare la révision et le chemin
- Si l’on considère ces deux marqueurs comme interchangeables, on ne bloque pas les entrées qui commencent par un tiret
Une prise en charge différente selon les sous-commandes
- La prise en charge de
--end-of-optionsn’a pas été appliquée à toutes les commandes Git d’un coup : elle a été ajoutée par sous-commande git rev-parse, qui utilise son propre parseur d’arguments, n’a commencé à le prendre en charge qu’un an après l’introduction initiale, avec Git 2.30.0git checkoutetgit resetinterprètent eux-mêmes--; l’implémentation initiale laissait--end-of-optionsdans la liste d’arguments, ce qui les amenait à le rejeter- Ce problème a été corrigé en février 2024, avec la sortie de Git 2.43.1
L’injection d’arguments peut survenir même sans shell
- Git, Mercurial et SSH proposent officiellement des options permettant d’exécuter une commande spécifiée par l’appelant
git clone --upload-pack=<cmd>spécifie le binaire côté serveur-c core.sshCommand=<cmd>sur n’importe quel appel Git modifie la commande de connexion--config=alias.<subcmd>=!<shell>dans Mercurial redéfinit la sous-commande à exécuter comme script shell arbitraire-oProxyCommand=<cmd>dans SSH spécifie la commande proxy
- Si un programme wrapper place une chaîne non fiable dans la liste d’arguments, ces fonctionnalités peuvent devenir un vecteur d’attaque
- Ce type d’échec relève de CWE-88, l’injection d’argument (argument injection), et diffère de l’injection de commandes shell
- Il peut survenir même si le programme utilise un tableau
argvetexecau lieu desystem() - Le tableau est transmis à Git sans altération, mais Git interprète les arguments commençant par un tiret comme des options
- Il peut survenir même si le programme utilise un tableau
- CVE-2019-13139 dans
docker buildest un cas utilisantos/execde Go et un tableauargv, sans passer par un shell- Le fragment
#ref:dird’une URL de contexte Git était transmis àgit fetch origin <ref>, et<ref>était interprété comme--upload-pack=<cmd>
- Le fragment
Des vulnérabilités répétées dans plusieurs systèmes de gestion de versions
- En août 2017, quatre systèmes de gestion de versions ont vu le même schéma publié le même jour
- CVE-2017-1000117 dans Git
- CVE-2017-1000116 dans Mercurial
- CVE-2017-9800 dans Subversion
- CVE-2017-12836 dans CVS
- Les quatre systèmes transmettaient le nom d’hôte de l’URL comme argument SSH, et un nom d’hôte commençant par
-oProxyCommand=était traité comme une option SSH - Selon l’analyse post-mortem de Phabricator, parmi les trois outils alors maintenus activement, seul Subversion a ajouté
--avant le nom d’hôte- Git et Mercurial ont validé le format du nom d’hôte, notamment parce que toutes les implémentations SSH ne prennent pas en charge
--
- Git et Mercurial ont validé le format du nom d’hôte, notamment parce que toutes les implémentations SSH ne prennent pas en charge
- Le code dépourvu de
--semble normal et fonctionne tant que l’argument ne commence pas par un tiret ; ce mécanisme est donc intrinsèquement non sûr
Les chemins d’exposition des gestionnaires de paquets
- Les gestionnaires de paquets reçoivent des URL Git ou des refs depuis les manifestes, les fichiers de verrouillage et les métadonnées de dépendances transitives, puis les transmettent à des sous-processus
gem 'foo', git: '...'dans un Gemfilegithub:user/repo#refdanspackage.json- Les configurations équivalentes dans
pyproject.toml,Cargo.toml,mix.exs,Package.swift,pubspec.yaml,conanfile.pyetgo.mod
- Sur les 19 gestionnaires de paquets examinés à la date du HEAD de juillet 2026, 17 exécutent le binaire Git comme chemin par défaut ou unique
- Les deux autres utilisent une bibliothèque par défaut
- Cargo utilise libgit2 et exécute un processus Git si
net.git-fetch-with-cliest activé - Poetry est passé à dulwich depuis la version 1.2.0 et peut utiliser le Git système via le réglage
system-git-client
- Cargo utilise libgit2 et exécute un processus Git si
- Nix utilise libgit2 pour lire les dépôts locaux, mais exécute un processus Git pour les fetchs, car libgit2 ne prend pas en charge les assistants git-credential
- Les outils étudiés sont Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg et Yarn
CVE confirmées dans des gestionnaires de paquets
- Parmi les vulnérabilités de ce type publiées dans des gestionnaires de paquets, on trouve notamment
- CVE-2021-43809 dans Bundler
- CVE-2021-29472 et CVE-2022-24828 dans Composer
- CVE-2022-36069 dans Poetry
- CVE-2023-5752 dans pip
- CVE-2022-21223 et CVE-2022-24440 dans CocoaPods
- CVE-2025-68119 dans Go
- Snyk, qui a découvert plusieurs vulnérabilités en 2022, a publié une étude sur l’injection d’arguments avec Git et Mercurial
- Sonar maintient une liste d’options dangereuses par binaire
État réel des défenses et correction de Go
- Parmi les 17 gestionnaires de paquets qui exécutent un processus Git, le seul outil à utiliser
--end-of-optionsétaitcmd/gode Go - En juin 2019, Go a ajouté
--avant les URL de dépôt dans le cadre d’un durcissement général - En janvier 2026, il est apparu que
--seul ne suffisait pas ; le correctif de CVE-2025-68119 a donc ajouté--end-of-optionsde manière généralisée - Le même correctif inclut aussi
HGPLAIN=+strictflags- Ce réglage limite l’interprétation initiale des options par Mercurial depuis 2017, avec la sortie de Mercurial 4.4.2
- Le commit de correction de Go indique qu’un changement plus structurel pourrait être nécessaire pour éviter que le même problème soit réintroduit, mais qu’il corrige d’abord le problème actuel
La plupart des protections ont été ajoutées après publication de vulnérabilités
- Les autres gestionnaires de paquets, lorsqu’ils protègent la liste d’arguments, utilisent principalement
--ou le rejet des tirets en début d’entrée - Le
--avant l’URL dansgit clonechez Bundler a été ajouté par le patch de CVE-2021-43809 - Le rejet des tirets initiaux dans cocoapods-downloader a été appliqué en mars 2022 via trois commits sur dix jours, à la période de publication de CVE-2022-21223
- La défense de Poetry a été ajoutée en septembre 2021, une CVE a été assignée un an plus tard, puis le projet est passé à dulwich six mois après
- vcpkg fait exception : il utilise
--depuis le premier jour du support des registres Git
Les contraintes de compatibilité imposées par la version minimale de Git
- L’avis CVE-2022-24828 de Composer identifie
--end-of-optionscomme le correctif approprié, mais Composer devant prendre en charge des versions plus anciennes de Git, il a choisi de rejeter les noms de branche commençant par un tiret - L’intégration Git de vcpkg indique Git 2.7.4 comme version minimale, tandis que
HOMEBREW_MINIMUM_GIT_VERSIONpour Linux dans Homebrew est 2.7.0, fixé en 2018 - Amazon Linux 2, qui fournissait Git 2.14.3, est arrivée en fin de vie en juin 2026 ; les distributions que suivaient ces planchers sortent donc seulement maintenant du périmètre de support
- Le statut LTS d’Ubuntu complique également une transition généralisée
- Ubuntu 18.04 fournit Git 2.17.0 et bénéficie d’un support étendu jusqu’en 2028
- Ubuntu 20.04 fournit Git 2.25.1 et bénéficie d’un support étendu jusqu’en 2030
- Git 2.25.1 accepte
--end-of-optionspourgit fetch, mais le rejette dansgit rev-parse
- Pour s’appuyer sur
--end-of-options, il faut exiger au minimum Git 2.24.0 pour la plupart des sous-commandes, 2.30.0 pourrev-parse, et 2.43.1 pourcheckoutetreset - Relever la version minimale empêche de prendre en charge les utilisateurs qui utilisent l’ancien Git fourni par leur distribution
Utiliser une bibliothèque Git plutôt qu’exécuter un processus
- libgit2, gitoxide, go-git, JGit et dulwich implémentent dans le processus les protocoles de transport Git nécessaires au clone et au fetch
- Comme il n’y a pas de frontière
argvséparée, il n’existe tout simplement pas de cible dans laquelle injecter une liste d’arguments - Jujutsu utilise gitoxide pour l’intégration Git et ne fait l’objet d’aucune CVE publique de type injection d’arguments
- Les deux avis publiés à ce jour concernent une traversée de chemin et l’absence, héritée d’une bibliothèque, d’une vérification des collisions SHA-1
- CVE-2025-21613 dans go-git se limite au transport
file://- Ce chemin est le seul chemin de code dans go-git qui exécute le binaire Git
- Embarquer sa propre implémentation de Git oblige à suivre tous les correctifs de sécurité d’upstream Git liés au checkout ; libgit2 comme JGit ont déjà connu des correctifs répétés sur ce sujet
- Ce coût est réel, mais il transforme le problème : au lieu de devoir se souvenir durablement de vérifier les arguments à chaque point d’appel, il faut appliquer un flux concret de correctifs upstream
Portée des changements proposés à Homebrew
- La PR Homebrew relève la version minimale de Git à 2.30.0 et ajoute
--end-of-optionsaux emplacements suivants- Avant les URL de
clone,remote set-urletls-remote - Avant les refs de
rev-parse
- Avant les URL de
- Les appels à
checkoutetresetne sont pas modifiés- Pour protéger aussi ces deux commandes, il faudrait Git 2.43.1, sorti en février 2024
- Cette version est plus récente que le Git fourni par plusieurs distributions actuellement prises en charge
1 commentaires
Avis sur Lobste.rs
En particulier, ce qu’on apprend avec une commande
jjs’applique naturellement aux autres commandes. La documentation degit logvoit exploser le nombre de flags selon les options de sortie, tout en mélangeant la « notation spéciale » de la syntaxe des plages de commits et des flags de filtrage supplémentaires, et les connaissances acquises se transfèrent peu aux autres commandes Git.À l’inverse, la documentation de
jj logest si concise qu’il reste de la place sur une page. Elle remplace la complexité fourre-tout de Git par trois éléments — ensembles de révisions, ensembles de fichiers et DSL de templates de sortie — utilisés de façon cohérente dans tout jj, ce qui le rend bien plus simple, composable et intuitif. Il faut tenir compte du fait que Git a accumulé vingt ans de scories, mais jj semble bien mieux placé pour les éviter.--avant les arguments fournis par l’utilisateur dans une commande, mais demander en plus ici des règles de transformation propres devient un piège beaucoup trop dangereux.--a été choisi à l’origine pour lever l’ambiguïté des arguments de fichiers. Je me demande s’il y avait un compromis de conception moins visible derrière ce choix, plutôt qu’une approche simple consistant à recevoir les fichiers comme arguments explicites.En repensant aux débats des années 1990 entre Tcl et Scheme, dans ce cas, « le pire est le mieux » avait peut-être raison. Alors que la famille des S-expressions, JSON et XML compris, ne s’est quasiment pas imposée dans l’écosystème UNIX, Tcl disposait d’une approche relativement principielle consistant à superposer des sous-langages à l’intérieur de chaînes.