,

O llm mandava sua chave da OpenAI pra outro endpoint? Rodei

Rodei 5 cenários no llm 0.37.1 e no 0.37.2 contra um endpoint falso com chave canário. O 0.37.1 mandou a chave da OpenAI pro api_base de terceiro em 3 de 5, sem erro nenhum; o 0.37.2, em 0 de 5.

Rogério Rodrigues

Rogério Rodrigues

Engenheiro de IA · mestrando em Informática em Saúde (UFSC)

Etiqueta de remessa com carimbo vermelho "entregue ao vizinho": no llm 0.37.1, 3 de 5 cenários mandaram a chave da OpenAI pro api_base de terceiro, com saída 0 e resposta ok; no llm 0.37.2, 0 de 5.

Coloquei uma chave falsa da OpenAI no ambiente, apontei o llm pra um endpoint de mentira em 127.0.0.1 e fiquei olhando o cabeçalho Authorization chegar. No llm 0.37.1, a chave da OpenAI apareceu no endpoint de terceiro em 3 de 5 cenários. Os três terminaram com código de saída 0 e a resposta normal na tela. No 0.37.2, saído em 11/11/2026, foram 0 de 5.

O código está no repositório: demos/14-llm-chave-endpoint. Roda em CPU, sem conta e sem chave de verdade.

O que é o llm e por que isso importa?

O llm é a ferramenta de linha de comando e biblioteca Python do Simon Willison pra conversar com modelos. Licença Apache-2.0. Muita gente usa ele com endpoint próprio: vLLM, LM Studio, OpenRouter, o gateway da empresa. Isso se faz num arquivo extra-openai-models.yaml, com um api_base apontando pra fora da OpenAI.

Segundo os avisos, a documentação diz que, com api_base definido, a chave da OpenAI que você configurou não vai por padrão. Dois avisos de segurança publicados em 11/11/2026 mostram dois caminhos em que ela ia:

  • GHSA-v9h7-jj4g-f862: modelo com api_base e api_key_name, quando a chave com esse nome nunca foi guardada. O llm caía no OPENAI_API_KEY do ambiente e mandava ela pro endpoint. Afeta da 0.6 até a 0.37.1.
  • GHSA-r639-rr7r-x72w: modelo com responses: true e api_base, rodado com -o chat_completions 1. O objeto que faz a chamada de Chat Completions não herdava a configuração de chave e usava a da OpenAI. Afeta da 0.32a2 até a 0.37.1.

Os dois são classificados como moderados. Ler o aviso é uma coisa. Eu queria ver o cabeçalho chegando.

Como eu rodei?

Montei um endpoint falso compatível com OpenAI, em Python puro, ouvindo só em 127.0.0.1 numa porta aleatória. Ele guarda o Authorization de cada pedido e devolve um chat.completion mínimo com “ok”.

def do_POST(self):
    tamanho = int(self.headers.get("Content-Length") or 0)
    corpo = self.rfile.read(tamanho) if tamanho else b""
    self.server.pedidos.append({
        "path": self.path,
        "authorization": self.headers.get("Authorization"),
        "model": (json.loads(corpo or b"{}") or {}).get("model"),
    })
    # ... responde um chat.completion com "ok"

O demo.py cria dois venvs, um com llm==0.37.1 e outro com llm==0.37.2. Pra cada cenário ele monta um LLM_USER_PATH temporário com o YAML e, quando o cenário pede, um keys.json. A chave canário é sk-CANARIO-OPENAI-FALSA-0000. Depois roda o mesmo comando nas duas versões:

llm -m tp --no-stream --no-log oi

O YAML do cenário A é este:

- model_id: tp
  model_name: "modelo-falso"
  api_base: "http://127.0.0.1:PORTA/v1"
  api_key_name: "terceiro"

Os cinco cenários:

  • A: api_key_name: terceiro que nunca foi guardada, OPENAI_API_KEY no ambiente.
  • B (controle): a mesma coisa, mas com a chave do terceiro guardada.
  • C (controle): api_base sem api_key_name.
  • D: responses: true com -o chat_completions 1, OPENAI_API_KEY no ambiente.
  • E: igual ao D, mas com a chave openai guardada no keys.json, como faz o llm keys set openai.

Python 3.11. O pip resolveu openai 3.50.0 nos dois venvs. Com os venvs prontos, a demo inteira levou 8,9 s.

O que chegou no endpoint?

cenário 0.37.1 recebeu 0.37.2 recebeu
A chave da OpenAI (saída 0) nenhum pedido (saída 1)
B (controle) chave do terceiro chave do terceiro
C (controle) DUMMY_KEY DUMMY_KEY
D chave da OpenAI (saída 0) DUMMY_KEY
E chave da OpenAI (saída 0) DUMMY_KEY
vazou 3 de 5 0 de 5

Os controles B e C deram igual nas duas versões. Ou seja, a correção fechou os três buracos sem mudar os dois controles.

No cenário A, o 0.37.2 nem faz o pedido. Ele para antes, com esta mensagem:

Error: No key found - add one using 'llm keys set terceiro'

Isso é o comportamento certo. Você pediu uma chave chamada “terceiro”, ela não existe, então o programa reclama. A versão anterior “resolvia” o problema pegando a chave errada em silêncio.

Por que isso passa batido?

Porque nada dá errado na tela. No 0.37.1 os cinco cenários responderam “ok” com código de saída 0. Se o endpoint de terceiro aceita qualquer chave, como o falso da demo aceita, você nunca percebe que a sua chave da OpenAI saiu da máquina.

Confesso que eu achava que o cenário A era o seguro. Dei um nome pra chave, então a ferramenta ia usar aquele nome. Ela usava o nome quando ele existia. Quando não existia, caía no ambiente.

Pensa num carteiro que, quando não acha o destinatário, entrega a carta pro vizinho em vez de devolver. O endereço estava certo. O plano B é que era o problema.

O que não funcionou na primeira rodada?

A primeira execução travou logo no primeiro cenário e estourou o tempo limite do subprocess. Não era o llm. Era eu: o llm lê a entrada padrão quando ela não é um terminal, e o subprocess deixava a entrada aberta. Ele ficava esperando eu digitar o resto do prompt. Resolvi com stdin=subprocess.DEVNULL.

Antes de achar isso, por precaução, tirei o proxy da jogada com NO_PROXY=127.0.0.1,localhost. A máquina onde rodei passa a internet por um proxy, e eu não queria o pedido pro endpoint local desviado. Deixei no código pra quem for reproduzir dentro de uma rede de empresa.

Até onde esse teste vai?

  • Endpoint falso e chave falsa. A demo mostra o que o cliente manda. Não mostra o que um terceiro faria com a chave.
  • A demo cobre o caminho da linha de comando com modelo definido em extra-openai-models.yaml. A API Python, o modo assíncrono, os plugins e o comando llm openai endpoint ... --responses, que o segundo aviso também cita, ficaram fora desta rodada.
  • São 5 cenários montados a partir dos dois avisos. Não é varredura de todas as combinações de opção.
  • Quem só usa a OpenAI direto, sem api_base, não é afetado por essas duas falhas.

Pra quem serve e o que fazer agora?

Serve pra quem usa o llm com modelo local ou gateway e tem chave da OpenAI no ambiente ou guardada nele. Em máquina de dev isso acontece fácil: a variável OPENAI_API_KEY fica no .bashrc há anos e ninguém lembra.

O que fazer:

pip install -U "llm>=0.37.2"
llm --version

Se você usou api_base de terceiro numa versão anterior, nos cenários A, D ou E, troque a chave da OpenAI. Não dá pra saber pelo lado do cliente se o terceiro guardou o cabeçalho. Na minha humilde opinião, vale também tirar o OPENAI_API_KEY do ambiente global e passar a chave só pro processo que precisa dela.

O desafio de implementar a correção é zero: é atualizar um pacote. O custo real está no inventário. Quem tem o llm em servidor de CI, em imagem Docker ou em script de agente precisa achar todas as cópias, e isso costuma levar mais tempo que o pip install.

Um ponto bom pro mantenedor: os dois avisos têm passo a passo de reprodução, versões afetadas e o teste que prova a correção. Foi isso que tornou a demo possível sem adivinhação.

Fonte e como reproduzir

Release 0.37.2 do llm, de 11/11/2026: github.com/simonw/llm/releases/tag/0.37.2. Avisos: GHSA-v9h7-jj4g-f862 e GHSA-r639-rr7r-x72w.

Código e saída real: demos/14-llm-chave-endpoint. Três comandos:

cd demos/14-llm-chave-endpoint && pip install -r requirements.txt
python3 demo.py
python3 -m pytest tests -q -s

O teste confere que o 0.37.1 vazou exatamente nos cenários A, D e E e que o 0.37.2 não vazou em nenhum. A saída fica em results/saida.txt e results/resultado.json.

Vocês usam o llm ou outra ferramenta com api_base de terceiro? Qual delas querem que eu passe pelo mesmo endpoint falso?

English summary

I ran the same 5 scenarios on llm 0.37.1 and 0.37.2 against a fake OpenAI-compatible endpoint on 127.0.0.1 with a canary key. 0.37.1 sent the OpenAI key to the third-party api_base in 3 of 5 scenarios, all exiting 0; 0.37.2 sent it in none, and stops with “No key found” when the named key is missing. Upgrade to 0.37.2 and rotate the key if you used a custom endpoint before. Code: demos/14-llm-chave-endpoint.

Publicado em 13/11/2026. A demo faz parte do nursia-research-lab, repositório de pesquisa do PPGINFOS/UFSC com bolsa FAPESC.

Conhece alguém que precisa ler isso?

Manda pra essa pessoa. Leva dez segundos e é o que mais ajuda esse trabalho a chegar em quem usa IA com dado de verdade.

Rogério Rodrigues

Rogério Rodrigues

Engenheiro de IA há 20 anos em software, mestrando em Informática em Saúde na UFSC. Escrevo o que rodei: IA com dado de saúde, FHIR e LGPD, com código aberto.

Ficou uma dúvida? Discorda de algo? Me chama.

Ver publicações científicas