- O
--do Git não é um terminador de opções comum: ele separa revisões de especificações de caminho. Por isso, para passar revisões não confiáveis com segurança, é necessário usar--end-of-options, compatível desde o Git 2.24.0 - Em
git log --end-of-options "$rev" -- "$path", o primeiro marcador separa opções e revisões, enquanto o--final separa revisões e caminhos; eles não são intercambiáveis - Mesmo executando diretamente um array
argvsem shell, se uma entrada que começa com hífen for interpretada como opções como--upload-pack,core.sshCommandouProxyCommand, pode ocorrer injeção de argumentos CWE-88 - Entre 19 gerenciadores de pacotes analisados, 17 executavam o binário do Git como modo padrão ou único, mas a única ferramenta que usava
--end-of-optionsera ocmd/godo Go - Mitigações fundamentais têm custo de compatibilidade: dependendo do subcomando, é preciso elevar a versão mínima do Git para 2.24.0, 2.30.0 ou 2.43.1; já bibliotecas Git eliminam a fronteira de injeção de argumentos, mas precisam acompanhar diretamente as correções de segurança de checkout do upstream
Diferença entre -- e --end-of-options
- Em ferramentas Unix comuns,
--marca o fim da interpretação de opções; assim,rm -- -ftrata-fcomo nome de arquivo, não como opção de remoção forçada - Desde cedo, o Git usa
--como separador entre revisões e especificações de caminho (pathspec)git log fooé ambíguo: pode significar um branch chamadofooou um arquivo chamadofoogit log main -- README.mdsignifica commits emmainque tocaram emREADME.md
- Por causa desse design, não havia marcador de fim de opções na posição de revisão; em
git log "$rev", se$revcomeça com hífen, o Git o interpreta como opção - O commit que introduziu
--end-of-optionsexplica que, como o--existente já separava revisões de especificações de caminho, era necessário um marcador separado para distinguir opções de revisões --end-of-optionsfoi documentado em gitcli(7) e adicionado em novembro de 2019, com o lançamento do Git 2.24.0
Uso correto por comando
git clone -- "$url"segue a convenção POSIX noclone, então o--antes da URL encerra a interpretação de opções- O
--final emgit checkout "$ref" --marca$refcomo revisão, não como nome de arquivo, mas não impede que$refseja interpretado primeiro como opção - Para passar com segurança uma revisão e um caminho não confiáveis juntos, é preciso usar os dois marcadores, como em
git log --end-of-options "$rev" -- "$path"--end-of-optionssepara opções e revisões--separa revisões e caminhos
- Tratar os dois marcadores como intercambiáveis não bloqueia entradas que começam com hífen
Suporte varia conforme o subcomando
- O suporte a
--end-of-optionsnão foi aplicado a todos os comandos Git de uma só vez; ele foi adicionado por subcomando git rev-parseusa seu próprio analisador de argumentos e só passou a ter suporte no Git 2.30.0, um ano depois da introdução inicialgit checkoutegit resetinterpretam--por conta própria, e a implementação inicial deixava--end-of-optionsna lista de argumentos, fazendo com que fosse rejeitado- Esse problema foi resolvido em fevereiro de 2024, com o lançamento do Git 2.43.1
Injeção de argumentos mesmo sem shell
- Git, Mercurial e SSH têm, como recursos oficiais, opções que executam comandos especificados pelo chamador
git clone --upload-pack=<cmd>especifica o binário do lado do servidor-c core.sshCommand=<cmd>em qualquer chamada do Git altera o comando de conexão--config=alias.<subcmd>=!<shell>no Mercurial redefine um subcomando a executar como um script shell arbitrário-oProxyCommand=<cmd>no SSH especifica um comando de proxy
- Se um programa wrapper colocar strings não confiáveis na lista de argumentos, esses recursos podem se transformar em meios de ataque
- Esse tipo de falha corresponde à CWE-88, injeção de argumentos (argument injection), e é diferente de injeção de comandos shell
- Ela ocorre mesmo que o programa use um array
argveexecem vez desystem() - O array é passado intacto ao Git, mas o Git interpreta argumentos que começam com hífen como opções
- Ela ocorre mesmo que o programa use um array
- O
docker buildCVE-2019-13139 é um caso que usavaos/execdo Go e arrayargv, sem passar por shell- O fragmento
#ref:dirda URL de contexto Git era passado agit fetch origin <ref>, e<ref>era interpretado como--upload-pack=<cmd>
- O fragmento
Vulnerabilidades repetidas em vários sistemas de controle de versão
- Em agosto de 2017, no mesmo dia, o mesmo padrão foi divulgado em quatro sistemas de controle de versão
- CVE-2017-1000117 do Git
- CVE-2017-1000116 do Mercurial
- CVE-2017-9800 do Subversion
- CVE-2017-12836 do CVS
- Todos os quatro sistemas passavam o nome do host da URL como argumento do SSH, e um nome de host começando com
-oProxyCommand=era tratado como opção do SSH - Segundo a análise post-mortem do Phabricator, entre as três ferramentas mantidas ativamente na época, apenas o Subversion adicionou
--antes do nome do host- Git e Mercurial validaram o formato do nome do host, em parte porque nem todas as implementações de SSH dão suporte a
--
- Git e Mercurial validaram o formato do nome do host, em parte porque nem todas as implementações de SSH dão suporte a
- Código sem
--parece e funciona normalmente até que um argumento comece com hífen; portanto, esse mecanismo é inseguro por padrão
Caminhos de exposição em gerenciadores de pacotes
- Gerenciadores de pacotes recebem URLs Git ou refs de manifestos, arquivos de lock e metadados de dependências transitivas, e os repassam a subprocessos
gem 'foo', git: '...'no Gemfilegithub:user/repo#refempackage.json- Configurações equivalentes em
pyproject.toml,Cargo.toml,mix.exs,Package.swift,pubspec.yaml,conanfile.pyego.mod
- Entre 19 gerenciadores de pacotes analisados no HEAD de julho de 2026, 17 executavam o binário do Git como caminho padrão ou único
- Os outros dois usam bibliotecas por padrão
- Cargo usa libgit2 e executa um processo Git se
net.git-fetch-with-cliestiver ativado - Poetry migrou para dulwich a partir da versão 1.2.0 e pode usar o Git do sistema com a configuração
system-git-client
- Cargo usa libgit2 e executa um processo Git se
- O Nix usa libgit2 ao ler repositórios locais, mas executa um processo Git para fetch porque libgit2 não oferece suporte a auxiliares git-credential
- Os alvos analisados foram Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg e Yarn
CVEs confirmadas em gerenciadores de pacotes
- Vulnerabilidades publicadas desse tipo em gerenciadores de pacotes incluem estes casos
- CVE-2021-43809 do Bundler
- CVE-2021-29472 e CVE-2022-24828 do Composer
- CVE-2022-36069 do Poetry
- CVE-2023-5752 do pip
- CVE-2022-21223 e CVE-2022-24440 do CocoaPods
- CVE-2025-68119 do Go
- A Snyk, que descobriu várias vulnerabilidades de 2022, publicou uma pesquisa sobre injeção de argumentos ao usar Git e Mercurial
- A Sonar mantém uma lista de opções perigosas por binário
Estado real das defesas e a correção do Go
- Entre os 17 gerenciadores de pacotes que executam processos Git, a única ferramenta que usava
--end-of-optionsera ocmd/godo Go - Em junho de 2019, o Go adicionou
--antes de URLs de repositório como medida geral de endurecimento - Em janeiro de 2026, ficou claro que apenas
--não bastava; como correção para CVE-2025-68119,--end-of-optionsfoi adicionado de forma ampla - A mesma correção também incluiu
HGPLAIN=+strictflags- Essa configuração limita a interpretação inicial de opções do Mercurial desde 2017, quando o Mercurial 4.4.2 foi lançado
- O commit de correção do Go afirma que talvez seja necessária uma mudança mais estrutural para dificultar a reintrodução do mesmo problema, mas resolve o problema atual por ora
A maioria das defesas foi adicionada após divulgações de vulnerabilidades
- Os demais gerenciadores de pacotes, quando protegem a lista de argumentos, usam principalmente
--ou a rejeição de hífen inicial na entrada - O
--antes da URL emgit cloneno Bundler foi adicionado pelo patch da CVE-2021-43809 - A rejeição de hífen inicial no cocoapods-downloader foi aplicada em três commits ao longo de dez dias em março de 2022, coincidindo com a divulgação da CVE-2022-21223
- A defesa do Poetry foi adicionada em setembro de 2021, recebeu uma CVE um ano depois e migrou para dulwich seis meses depois
- Como exceção, o vcpkg usa
--desde o primeiro dia em que o suporte a registros Git foi escrito
Restrições de compatibilidade criadas pela versão mínima do Git
- O aviso da CVE-2022-24828 do Composer aponta
--end-of-optionscomo a correção correta, mas, como precisava dar suporte a versões mais antigas do Git, optou por rejeitar nomes de branch que começam com hífen - A integração Git do vcpkg declara Git 2.7.4 como versão mínima, e
HOMEBREW_MINIMUM_GIT_VERSIONdo Homebrew para Linux é 2.7.0, definido em 2018 - Com o fim da vida do Amazon Linux 2, que fornecia Git 2.14.3, em junho de 2026, as distribuições que esses limites mínimos acompanhavam só agora estão saindo do escopo de suporte
- O estado do suporte de longo prazo do Ubuntu também dificulta uma migração em massa
- Ubuntu 18.04 fornece Git 2.17.0 e tem suporte estendido até 2028
- Ubuntu 20.04 fornece Git 2.25.1 e tem suporte estendido até 2030
- Git 2.25.1 aceita
--end-of-optionsemgit fetch, mas o rejeita emgit rev-parse
- Para depender de
--end-of-options, a maioria dos subcomandos precisa exigir Git 2.24.0 como mínimo;rev-parseprecisa de 2.30.0; echeckoutereset, de 2.43.1 - Elevar a versão mínima impede dar suporte a usuários que usam versões antigas do Git incluídas nas distribuições
Usar bibliotecas Git em vez de executar processos
- libgit2, gitoxide, go-git, JGit e dulwich implementam, dentro do processo, os protocolos de transporte Git necessários para clone e fetch
- Como não há uma fronteira
argvseparada, não existe alvo onde injetar a lista de argumentos - Jujutsu usa gitoxide para integração com Git e não tem CVEs públicas do tipo injeção de argumentos
- Os dois avisos até agora são de path traversal e ausência herdada de uma biblioteca na verificação de colisão SHA-1
- A CVE-2025-21613 do go-git se limita ao transporte
file://- Esse caminho é o único trecho de código no go-git que executa o binário do Git
- Incluir uma implementação própria do Git exige acompanhar todas as correções de segurança de checkout publicadas pelo Git upstream, e tanto libgit2 quanto JGit já tiveram correções recorrentes relacionadas a isso
- Esse custo é real, mas a questão deixa de ser lembrar permanentemente de validar argumentos em cada ponto de chamada e passa a ser aplicar um fluxo concreto de patches upstream
Escopo das mudanças propostas ao Homebrew
- O PR do Homebrew eleva a versão mínima do Git para 2.30.0 e adiciona
--end-of-optionsnos seguintes pontos- Antes da URL em
clone,remote set-urlels-remote - Antes do ref em
rev-parse
- Antes da URL em
- Chamadas a
checkouteresetnão foram alteradas- Proteger também esses dois comandos exigiria o Git 2.43.1, lançado em fevereiro de 2024
- Essa versão é mais recente do que o Git fornecido por várias distribuições atualmente suportadas
1 comentários
Opiniões no Lobste.rs
Em especial, o conhecimento aprendido em um comando
jjse aplica naturalmente a outros comandos. A documentação dogit logmistura uma explosão de flags por opção de saída, a “notação especial” da sintaxe de intervalo de commits e ainda flags extras de filtro, e pouco desse conhecimento se transfere para outros comandos do GitJá a documentação do
jj logé tão concisa que sobra espaço na página. Ela substitui a complexidade improvisada do Git por três coisas: conjuntos de revisões, conjuntos de arquivos e uma DSL de templates de saída, usadas de forma consistente em todo o jj, o que torna tudo muito mais simples, combinável e intuitivo. É preciso considerar que o Git acumulou penduricalhos por 20 anos, mas o jj parece estar em posição muito melhor para evitar isso--antes de argumentos fornecidos pelo usuário em comandos, mas exigir ainda por cima regras próprias de transformação aqui vira uma armadilha perigosa demais--para resolver a ambiguidade dos argumentos de arquivo. Em vez da abordagem simples de receber arquivos como argumentos explícitos, será que houve algum trade-off de projeto não tão visível por trás dessa escolha?Relembrando o debate entre Tcl e Scheme nos anos 1990, talvez neste caso “quanto pior, melhor” estivesse certo. Enquanto famílias de S-expressions, incluindo JSON e XML, quase não se firmaram no ecossistema UNIX, o Tcl tinha uma abordagem relativamente mais principista de encaixar sublinguagens dentro de strings