LLM 0.37.2 OpenAI Key Leak: We Tested 13 Configs on 0.37.1 and the Fix

LLM 0.37.2 fixes two paths that sent your OpenAI key to a custom api_base. We tested 13 configs: 9 of 10 risky ones leaked on 0.37.1, none on the fix.

Updated November 13, 2026. Lab run on Linux with Python 3.13.5: LLM 0.37.1 and 0.37.2 installed from PyPI, pointed at a local capture server, with canary values in place of real keys.

The LLM 0.37.2 release fixes an OpenAI key leak in Simon Willison’s LLM command line tool and Python library: two configuration paths sent your OpenAI API key to a third-party api_base instead of the key meant for that provider. LLM is an open source tool for prompting OpenAI, Anthropic, Gemini, and OpenAI-compatible models from a terminal or from Python, and it recorded 552,882 PyPI downloads in the last month. Willison shipped the fix on November 11, 2026, with advisories GHSA-v9h7-jj4g-f862 and GHSA-r639-rr7r-x72w.

Upgrading to LLM 0.37.2 closes both paths: in our November 13 lab, 9 of 10 risky configurations sent an OpenAI key to the custom endpoint on 0.37.1, and none did on 0.37.2. The upgrade does not recall a key that already went out. If you ran 0.37.1 or earlier with a custom endpoint and OPENAI_API_KEY exported, check the conditions below before you decide whether to rotate the key.

TL;DR

  • GHSA-v9h7-jj4g-f862 (Moderate, LLM 0.6 to 0.37.1): a model with api_base and an api_key_name that was never stored sent OPENAI_API_KEY to that endpoint.
  • GHSA-r639-rr7r-x72w (Moderate, LLM 0.32a2 to 0.37.1): a responses: true model run with -o chat_completions 1 sent the stored openai key or OPENAI_API_KEY.
  • In our lab, a typo in api_key_name, a streaming call, the Python async API, and a config copied to a new machine all leaked on 0.37.1.
  • The first path read only the environment variable: a key saved with llm keys set openai alone was not sent there, while the second path sent either key.
  • In our lab, every leaking call returned a normal answer with no warning. Neither advisory has a CVE ID as of November 13, 2026.

What the Two LLM 0.37.2 Advisories Say

The two LLM 0.37.2 advisories say that a custom OpenAI-compatible model could receive your OpenAI key in two ways: when its named key was missing, or when a Responses API model switched to Chat Completions. Both are rated Moderate, both were published November 11, 2026, and both are fixed only in 0.37.2.

LLM lets you add OpenAI-compatible providers, such as OpenRouter, a company gateway, or a local model server, in a file called extra-openai-models.yaml. Each entry sets api_base, the endpoint URL, and can set api_key_name, the name of a key saved with llm keys set. The LLM documentation said the configured openai key would not be sent when api_base is set.

GHSA-v9h7-jj4g-f862 covers every release from 0.6, July 2023, through 0.37.1. When the named key was missing, LLM fell back to the OPENAI_API_KEY environment variable and sent it as Authorization: Bearer to the third-party host. GHSA-r639-rr7r-x72w covers 0.32a2 through 0.37.1: the Chat delegate built for -o chat_completions 1 kept the default key setting of openai, so it picked up the stored OpenAI key or the environment variable.

GitHub advisory GHSA-v9h7-jj4g-f862 for LLM showing Moderate severity, affected versions 0.6 to 0.37.1, patched 0.37.2
GHSA-v9h7-jj4g-f862 lists every LLM release from 0.6 through 0.37.1 as affected and 0.37.2 as patched. Image: GitHub, captured November 13, 2026.

What We Found When We Tested 13 LLM Configurations

We found that 9 of the 10 risky configurations sent an OpenAI key to our fake third-party endpoint on LLM 0.37.1, and none did on 0.37.2. The three control configurations behaved the same on both versions, so the fix did not break correctly configured models.

Our lab installed both versions from PyPI with uv, pointed each model’s api_base at a local server that logged the Authorization header, and ran one prompt per case. Canary strings stood in for the OpenAI key in the environment, the OpenAI key in keys.json, and the correct third-party key, so the log showed exactly which one left the process.

Lab results table showing which API key LLM 0.37.1 and 0.37.2 sent to a custom api_base across 13 configurations
Each row is one fresh LLM user directory and one prompt, run on November 13, 2026 against a local capture server. Image: AgentStack.

Four results go beyond the advisories. A typo in api_key_name leaked the OpenAI key on 0.37.1 even though the correctly spelled key was stored. The streaming CLI path and the Python async API leaked the same way as the plain CLI call. The standalone llm openai endpoint URL --responses -o chat_completions 1 command, which needs no YAML file at all, sent OPENAI_API_KEY when run without --key.

The fourth result narrows the first advisory. With no environment variable and only a key saved by llm keys set openai, the missing-name path on 0.37.1 sent nothing and stopped with an error. That error text read “No key found, add one using llm keys set thirdparty or set the OPENAI_API_KEY environment variable”, so the old version suggested the exact step that would have triggered the leak.

Why the New Endpoint Command Did Not Prevent the Leak

The new llm openai endpoint add command did not prevent the leak because its check runs only once, when the endpoint is saved. In our lab, both versions refused --key openrouter until a key with that name existed, but a saved YAML file copied to a fresh machine leaked on 0.37.1.

That copied-config case is the common way the missing-key condition arises. Dotfile repositories, container images, and onboarding scripts often carry extra-openai-models.yaml to a new laptop or CI runner, while keys.json stays behind because it holds secrets. On a host where OPENAI_API_KEY is exported for other tools, the first prompt to the custom model sent that key on 0.37.1. On 0.37.2, the same prompt stopped with “No key found”.

How the 0.37.2 Fix Changes LLM Key Handling

The 0.37.2 fix changes LLM so that a model with api_base never falls back to your OpenAI key. The fix commit 8adb0075, dated November 10, 2026, clears the environment variable fallback for these models and copies the outer model’s key settings onto the Chat delegate.

# llm/default_plugins/openai_models.py, LLM 0.37.2
if api_base:
    chat_model.needs_key = None
    chat_model.key_env_var = None      # new: no OPENAI_API_KEY fallback

# Responses.execute(), when -o chat_completions 1 is set
chat = Chat(**self._delegate_chat_kwargs())
chat.needs_key = self.needs_key        # new: delegate keeps the outer key name
chat.key_env_var = self.key_env_var    # new: and the cleared fallback

The shipped patch goes one step past the second advisory’s suggested diff, which copied only needs_key. Copying key_env_var as well matters for case G in our table, a Responses model with an unstored api_key_name run through Chat Completions, which leaked on 0.37.1 and stopped with an error on 0.37.2. The commit adds a 298-line regression file, tests/test_openai_key_leaks.py, and lists Claude Opus 5.5 as co-author.

LLM 0.37.2 GitHub release notes listing the two security fixes for custom api_base models and other changes
The 0.37.2 release notes describe both key leak fixes before the attachment and YAML fixes. Image: GitHub, captured November 13, 2026.

The fix is a behavior change. Anyone who deliberately kept a third-party key in OPENAI_API_KEY for an api_base model now gets an error and must save that key with llm keys set NAME. The updated documentation states that your OpenAI key is never sent to a custom endpoint, whether it is stored or exported.

Who Should Rotate an OpenAI Key After LLM 0.37.2

You should rotate your OpenAI key if you ran LLM 0.37.1 or earlier against a non-OpenAI endpoint you do not fully control, and one of the two leak conditions applied. Teams whose only custom endpoints were local servers on the same machine have far less exposure, since the key never crossed the network.

Timeline of the LLM OpenAI key leak from api_key_name in LLM 0.6 to the 0.37.2 fix and advisories
Release dates from the LLM changelog and November 2026 times in UTC from PyPI and the GitHub REST API, checked November 13, 2026. Image: AgentStack.

The exposure depends on who runs the receiving server. A hosted provider such as OpenRouter or Groq receives the header and may log it. A company gateway can write it to proxy logs, which is how the LiteLLM supply chain compromise started with tokens found in proxy logs. A stolen OpenAI key is billable, and our Black October LLMjacking report covers how attackers abuse stolen AI platform keys.

Neither advisory reports exploitation, and nothing in the release notes says any key was misused. The leak was silent, though. A hosted provider would normally reject an OpenAI key, so a user would likely have seen only an authentication error from that provider, while a permissive server returned a normal answer, as ours did. Check OpenAI usage for the period you ran affected versions before deciding.

How to Check Your LLM Setup Now

Check your LLM setup by upgrading to 0.37.2 first, then listing every custom model that matches one of the two leak conditions. The script below reads your LLM user directory and prints models whose named key is missing or which use the Responses API without a named key.

  1. Upgrade with the tool you installed it with: pip install -U llm, uv tool upgrade llm, pipx upgrade llm, or brew upgrade llm. Homebrew already lists 0.37.2 as stable. Confirm with llm --version.
  2. Run the check below on every machine and CI runner that has an extra-openai-models.yaml file.
  3. For each model it flags, save the right provider key with llm keys set NAME, or add api_key_name to Responses models.
  4. If a flagged model pointed at a remote host while you ran 0.37.1 or earlier, revoke that OpenAI key and issue a new one.
# check_llm_keys.py: run with the same Python environment as llm
import json, pathlib, subprocess, yaml

user = pathlib.Path(subprocess.check_output(["llm", "logs", "path"], text=True).strip()).parent
cfg = user / "extra-openai-models.yaml"
keys_file = user / "keys.json"
keys = json.loads(keys_file.read_text()) if keys_file.exists() else {}
models = (yaml.safe_load(cfg.read_text()) or []) if cfg.exists() else []

for m in models:
    if not m.get("api_base"):
        continue
    name = m.get("api_key_name")
    if name and name not in keys:
        print(f"{m['model_id']}: api_key_name '{name}' is not stored (GHSA-v9h7-jj4g-f862 path)")
    if m.get("responses") and not name:
        print(f"{m['model_id']}: responses model without api_key_name (GHSA-r639-rr7r-x72w path)")

In our lab, the script flagged cases A and G for the first advisory and case D for the second, and stayed silent on the correctly configured control. It does not catch one-off llm openai endpoint URL commands, so search shell history and scripts for chat_completions 1 as well.

Choose the plain upgrade when every custom endpoint had its named key stored and you never used -o chat_completions 1. Choose the upgrade plus key rotation when the check flags a model that pointed at a remote provider or a shared gateway. Rotation does not help when the key was only ever sent to a server on your own machine, so keep it for the remote cases.

Where This Fits in LLM Gateway Security

This LLM key leak is a client-side routing bug, the same class as the token we saw sent to the wrong host in the MCP redirect vulnerability in mcp-go. Any client that holds several provider keys has to map each key to one destination, and a fallback that picks the wrong key fails silently.

Teams that route traffic through a central gateway can keep provider keys off developer machines entirely. Our LLM gateways guide compares the options, the Portkey vs LiteLLM vs OpenRouter comparison covers the three most common routers, and the LLM cost monitoring tools roundup covers spotting spend from a leaked key. For machine credentials more broadly, see our non-human identity tools guide.

FAQ

Which LLM versions are affected by the OpenAI key leak?

LLM 0.6 through 0.37.1 are affected by GHSA-v9h7-jj4g-f862, and 0.32a2 through 0.37.1 by GHSA-r639-rr7r-x72w, according to the GitHub advisories. Version 0.37.2, released November 11, 2026, fixes both. Our lab confirmed the leaks on 0.37.1 and found no OpenAI key sent by 0.37.2 in any case.

Does LLM 0.37.2 break my existing custom models?

LLM 0.37.2 breaks only custom models that relied on OPENAI_API_KEY holding a third-party key. Those models now stop with a No key found error. Save the provider key with llm keys set NAME and reference it with api_key_name. Correctly configured models behaved the same on both versions in our lab.

Is there a CVE for the LLM OpenAI key leak?

No CVE ID had been assigned to GHSA-v9h7-jj4g-f862 or GHSA-r639-rr7r-x72w when we checked the GitHub Security Advisory API on November 13, 2026. Scanners that match only CVE IDs may miss both until one is issued, so check the installed LLM version directly with llm --version.

Are LLM plugins such as llm-anthropic affected?

The advisories cover only OpenAI-compatible models defined in extra-openai-models.yaml and the llm openai endpoint command, which live in LLM’s built-in OpenAI plugin. They do not name provider plugins such as llm-anthropic or llm-gemini, which use their own key names, and we did not test plugins.