- En Git,
--no es un terminador general de opciones, sino que separa revisiones y especificaciones de ruta, así que para pasar de forma segura una revisión no confiable se necesita--end-of-options, compatible desde Git 2.24.0 - En
git log --end-of-options "$rev" -- "$path", el marcador de adelante separa opciones y revisiones, y el--de atrás separa revisiones y rutas; no se pueden sustituir entre sí - Aunque se ejecute directamente el arreglo
argvsin shell, si una entrada que empieza con guion se interpreta como una opción como--upload-pack,core.sshCommandoProxyCommand, se produce inyección de argumentos CWE-88 - De 19 gestores de paquetes analizados, 17 ejecutaban el binario de Git como método predeterminado o único, pero la única herramienta que usaba
--end-of-optionseracmd/gode Go - La mitigación de fondo tiene un costo de compatibilidad: según el subcomando, hay que subir la versión mínima de Git a 2.24.0, 2.30.0 o 2.43.1; las bibliotecas de Git eliminan el límite de inyección de argumentos, pero a cambio hay que seguir directamente los parches upstream de seguridad en checkout
Diferencia entre -- y --end-of-options
- En las herramientas Unix comunes,
--marca el fin del análisis de opciones, así querm -- -ftrata-fcomo nombre de archivo y no como opción de borrado forzado - Git usó desde temprano
--como separador entre revisiones y especificaciones de ruta (pathspec)git log fooes ambiguo: puede referirse a una rama llamadafooo a un archivogit log main -- README.mdsignifica los commits demainque tocaronREADME.md
- Por este diseño, no había un marcador de fin de opciones en la posición de revisión, y en
git log "$rev", si$revempieza con guion Git lo interpreta como una opción - El commit que introdujo
--end-of-optionsexplica que, como--ya separaba revisiones y especificaciones de ruta, hacía falta un marcador aparte para distinguir opciones y revisiones --end-of-optionsestá documentado en gitcli(7) y se agregó en noviembre de 2019, cuando salió Git 2.24.0
Uso correcto según el comando
git clone -- "$url"hace queclonetermine de interpretar opciones antes de la URL, porque sigue la convención POSIX- El
--final degit checkout "$ref" --marca$refcomo revisión y no como nombre de archivo, pero no evita que$refse interprete primero como opción - Para pasar de forma segura una revisión no confiable y una ruta al mismo tiempo, hay que usar ambos marcadores, como en
git log --end-of-options "$rev" -- "$path"--end-of-optionssepara opciones y revisiones--separa revisiones y rutas
- Si se tratan ambos marcadores como intercambiables, no se bloquean las entradas que empiezan con guion
Momento de soporte distinto según el subcomando
- El soporte de
--end-of-optionsno llegó a todos los comandos de Git al mismo tiempo, sino que se fue agregando por subcomando git rev-parseusa su propio parser de argumentos y empezó a soportarlo recién un año después de la introducción inicial, en Git 2.30.0git checkoutygit resetinterpretan--por su cuenta, y la implementación inicial rechazaba--end-of-optionsporque lo dejaba en la lista de argumentos- Este problema se resolvió en febrero de 2024, cuando salió Git 2.43.1
Inyección de argumentos incluso sin shell
- Git, Mercurial y SSH tienen como función oficial opciones que ejecutan comandos indicados por quien llama
git clone --upload-pack=<cmd>especifica el binario del lado del servidor-c core.sshCommand=<cmd>en cualquier llamada a Git cambia el comando de conexión--config=alias.<subcmd>=!<shell>en Mercurial redefine un subcomando a ejecutar como script de shell arbitrario-oProxyCommand=<cmd>en SSH especifica el comando proxy
- Si un programa wrapper mete una cadena no confiable en la lista de argumentos, estas funciones pueden convertirse en vector de ataque
- Este tipo de fallo corresponde a CWE-88, inyección de argumentos (argument injection), y es distinto de la inyección de comandos de shell
- Puede ocurrir aunque el programa use
argvyexecen vez desystem() - El arreglo llega intacto a Git, pero Git interpreta como opciones los argumentos que empiezan con guion
- Puede ocurrir aunque el programa use
- El CVE-2019-13139 de
docker buildfue un caso que usabaos/execde Go y un arregloargv, sin pasar por shell- El fragmento
#ref:dirde una URL de contexto Git se pasaba agit fetch origin <ref>, donde<ref>se interpretaba como--upload-pack=<cmd>
- El fragmento
Vulnerabilidades repetidas en varios sistemas de control de versiones
- El mismo día de agosto de 2017 se publicó el mismo patrón en cuatro sistemas de control de versiones
- CVE-2017-1000117 de Git
- CVE-2017-1000116 de Mercurial
- CVE-2017-9800 de Subversion
- CVE-2017-12836 de CVS
- Los cuatro sistemas pasaban el hostname de la URL como argumento a SSH, y un hostname que empezara con
-oProxyCommand=se trataba como opción de SSH - Según el análisis posterior de Phabricator, de las tres herramientas activamente mantenidas en ese momento, solo Subversion agregaba
--antes del hostname- Git y Mercurial validaban el formato del hostname también porque no todas las implementaciones de SSH soportan
--
- Git y Mercurial validaban el formato del hostname también porque no todas las implementaciones de SSH soportan
- Incluso el código al que le falta
--parece normal y funciona hasta que aparece un argumento que empieza con guion, así que este mecanismo es inseguro por diseño
Ruta de exposición en gestores de paquetes
- Los gestores de paquetes reciben URLs o refs de Git desde manifests, lockfiles y metadatos de dependencias transitivas, y los pasan a subprocesos
gem 'foo', git: '...'en Gemfilegithub:user/repo#refenpackage.json- Configuraciones equivalentes en
pyproject.toml,Cargo.toml,mix.exs,Package.swift,pubspec.yaml,conanfile.pyygo.mod
- A julio de 2026, tomando HEAD como referencia, 17 de los 19 gestores de paquetes analizados ejecutaban el binario de Git como ruta predeterminada o única
- Los otros dos usan bibliotecas por defecto
- Cargo usa libgit2 y ejecuta el proceso de Git si se activa
net.git-fetch-with-cli - Poetry cambió a dulwich desde 1.2.0 y puede usar Git del sistema con la configuración
system-git-client
- Cargo usa libgit2 y ejecuta el proceso de Git si se activa
- Nix usa libgit2 para leer repositorios locales, pero ejecuta el proceso de Git para fetch porque libgit2 no soporta el helper
git-credential - Se analizaron Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg y Yarn
CVE confirmados en gestores de paquetes
- Entre las vulnerabilidades publicadas en gestores de paquetes de este tipo están estos casos
- CVE-2021-43809 de Bundler
- CVE-2021-29472 y CVE-2022-24828 de Composer
- CVE-2022-36069 de Poetry
- CVE-2023-5752 de pip
- CVE-2022-21223 y CVE-2022-24440 de CocoaPods
- CVE-2025-68119 de Go
- Snyk, que encontró varias vulnerabilidades de 2022, publicó una investigación sobre inyección de argumentos en Git y Mercurial
- Sonar mantiene una lista de opciones peligrosas por binario
Estado real de las defensas y corrección en Go
- De los 17 gestores de paquetes que ejecutan el proceso de Git, la única herramienta que usaba
--end-of-optionseracmd/gode Go - En junio de 2019, Go agregó
--antes de la URL del repositorio como endurecimiento defensivo general - En enero de 2026 se vio que
--solo no bastaba, y la corrección de CVE-2025-68119 agregó--end-of-optionsde forma amplia - La misma corrección incluyó
HGPLAIN=+strictflags- Esa configuración restringe el análisis inicial de opciones en Mercurial desde que salió Mercurial 4.4.2 en 2017
- El commit de corrección en Go indica que quizá haga falta un cambio más estructural para evitar que el mismo problema vuelva a introducirse, pero por ahora resuelve el problema actual
La mayoría de las defensas se agregaron después de divulgar la vulnerabilidad
- Los demás gestores de paquetes, incluso cuando protegen la lista de argumentos, usan sobre todo
--o el rechazo de guion inicial en la entrada - El
--antes de la URL degit cloneen Bundler se agregó con el parche de CVE-2021-43809 - El rechazo de guion inicial en cocoapods-downloader se aplicó en tres commits durante diez días en marzo de 2022, coincidiendo con la divulgación de CVE-2022-21223
- La defensa de Poetry se agregó en septiembre de 2021, recibió un CVE un año después y, seis meses más tarde, cambió a dulwich
- vcpkg es una excepción: usó
--desde el primer día en que se escribió el soporte para registros Git
Restricciones de compatibilidad impuestas por la versión mínima de Git
- El aviso de CVE-2022-24828 de Composer señala
--end-of-optionscomo la corrección correcta, pero como también debe soportar versiones viejas de Git, eligió rechazar nombres de rama que empiecen con guion - La integración de Git en vcpkg fija como versión mínima Git 2.7.4, y
HOMEBREW_MINIMUM_GIT_VERSIONpara Linux en Homebrew está fijado en 2.7.0 desde 2018 - Amazon Linux 2, que traía Git 2.14.3, terminó su vida útil en junio de 2026, y recién ahora las distribuciones que seguían ese piso están saliendo del rango de soporte
- El estado de soporte extendido de Ubuntu también dificulta una transición uniforme
- Ubuntu 18.04 trae Git 2.17.0 y tiene soporte extendido hasta 2028
- Ubuntu 20.04 trae Git 2.25.1 y tiene soporte extendido hasta 2030
- Git 2.25.1 acepta
--end-of-optionsengit fetch, pero lo rechaza engit rev-parse
- Para depender de
--end-of-options, la mayoría de los subcomandos requiere como mínimo Git 2.24.0;rev-parse, 2.30.0; ycheckoutyreset, 2.43.1 - Subir la versión mínima deja sin soporte a usuarios que siguen usando Git antiguo incluido en su distribución
Usar bibliotecas de Git en vez de ejecutar procesos
- libgit2, gitoxide, go-git, JGit y dulwich implementan el protocolo de transporte de Git necesario para clone y fetch dentro del mismo proceso
- Como no existe un límite
argvseparado, no hay un objetivo donde inyectar argumentos - Jujutsu usa gitoxide para integrarse con Git y no tiene CVE públicos de este tipo de inyección de argumentos
- Sus dos avisos actuales son por path traversal y por la falta de verificación de colisiones SHA-1 heredada de la biblioteca
- El CVE-2025-21613 de go-git está limitado al transporte
file://- Esa ruta es el único camino en go-git que ejecuta el binario de Git
- Incluir una implementación propia de Git obliga a seguir todos los parches de seguridad en checkout que publique Git upstream, y tanto libgit2 como JGit ya han tenido correcciones repetidas relacionadas con eso
- Ese costo es real, pero cambia el problema: en vez de recordar para siempre validar argumentos en cada punto de llamada, pasa a ser aplicar un flujo concreto de parches upstream
Alcance del cambio propuesto para Homebrew
- El PR de Homebrew eleva la versión mínima de Git a 2.30.0 y agrega
--end-of-optionsen estos lugares- Antes de la URL en
clone,remote set-urlyls-remote - Antes del ref en
rev-parse
- Antes de la URL en
- No cambia las llamadas a
checkoutnireset- Para proteger también esos dos comandos hace falta Git 2.43.1, lanzado en febrero de 2024
- Esa versión es más nueva que la de Git incluida en varias distribuciones actualmente soportadas
1 comentarios
Opiniones en Lobste.rs
En particular, lo aprendido en un comando de
jjse aplica de forma natural a otros comandos. La documentación degit logtiene una explosión de flags según las opciones de salida, mezcladas además con la “notación especial” de la sintaxis de rangos de commits y flags de filtrado adicionales, y hay poco conocimiento que se transfiera a otros comandos de GitEn cambio, la documentación de
jj loges tan concisa que sobra espacio incluso en una sola página. Reemplazó la complejidad tipo cajón de sastre de Git por tres cosas: conjuntos de revisiones, conjuntos de archivos y una DSL de plantillas de salida; como se usan de forma consistente en todo jj, resulta mucho más simple, componible e intuitivo. Hay que tener en cuenta que Git acumuló ruido durante 20 años, pero jj parece estar en una posición mucho mejor para evitarlo--antes de los argumentos proporcionados por el usuario, pero si además se exigen reglas de transformación propias, se convierte en una trampa demasiado peligrosa--para resolver la ambigüedad de los argumentos de archivo. Me pregunto si hubo algún compromiso de diseño no evidente para elegir esto en lugar de una forma simple de recibir archivos como argumentos explícitosSi miramos atrás al debate entre Tcl y Scheme en los años 90, quizá en este caso “lo peor es mejor” sí era cierto. Mientras que la familia de las expresiones S, incluidos JSON y XML, casi no logró arraigarse en el ecosistema UNIX, Tcl tenía un enfoque relativamente principista de estratificar sublenguajes dentro de cadenas