Skip to content

Store provider credentials as SecretStr in model configs - #1326

Merged
NathanHB merged 3 commits into
mainfrom
fix/litellm-api-key-leak
Aug 11, 2026
Merged

Store provider credentials as SecretStr in model configs#1326
NathanHB merged 3 commits into
mainfrom
fix/litellm-api-key-leak

Conversation

@NathanHB

@NathanHB NathanHB commented Aug 10, 2026

Copy link
Copy Markdown
Member

Model configs (LiteLLM, TGI) and the LLM-as-judge client held API credentials as plain str fields, so they were retained in plaintext wherever a config gets serialized (e.g. EvaluationTracker.results).

Switch these fields to pydantic SecretStr, which masks the value in reprs and default serialization, and additionally exclude them explicitly when building the results dict as a second layer. The real value is still unwrapped via get_secret_value() at the specific call sites that need it for the actual outgoing request.

Added regression tests asserting the credentials never appear in the serialized results dict.

@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Nathan Habib and others added 3 commits August 10, 2026 15:41
LiteLLMModelConfig.api_key and TGIModelConfig.inference_server_auth were
plain str fields, which meant they were retained in plaintext wherever a
model config gets serialized (e.g. EvaluationTracker.results). Switch
both to pydantic SecretStr, which masks the value in reprs and default
serialization, and additionally exclude them explicitly when building
the results dict as a second layer.

The real value is still unwrapped via get_secret_value() at the specific
call sites that need it for the actual outgoing request. Added
regression tests asserting the credential never appears in the
serialized results dict.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
JudgeLM.api_key was a plain str consumed directly by several backend
clients (OpenAI, AsyncInferenceClient, litellm). Wrap it in SecretStr on
assignment and unwrap via get_secret_value() at each usage site, for
consistency with the other model configs and to remove any reliance on
incidental string formatting to keep it out of logs or serialized
output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Unrelated cleanup so CI's Quality check is green on this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@NathanHB
NathanHB force-pushed the fix/litellm-api-key-leak branch from dc962a2 to 50ac35c Compare August 10, 2026 13:42
@NathanHB NathanHB changed the title Prevent LiteLLM api_key from leaking into pushed results Store provider credentials as SecretStr in model configs Aug 11, 2026
@NathanHB
NathanHB merged commit 932e1f2 into main Aug 11, 2026
3 of 5 checks passed
Mandark-droid added a commit to Mandark-droid/every_eval_ever that referenced this pull request Aug 12, 2026
…d and we missed

huggingface/lighteval#1326 (merged today) made LiteLLMModelConfig.api_key a
SecretStr and excluded BOTH api_key and inference_server_auth at the dump site.
This converter's redaction knew about the first and not the second: matching is
on exact names plus the suffixes _key/_token/_secret/_password/_credentials, none
of which catch a field ending in _auth. So inference_server_auth was serialised
into additional_details, which is published.

The upstream fix does not make this guard redundant. Every results file written
before it still holds the value in cleartext, and archived results files are
precisely what a converter is pointed at.

Adds the _auth suffix plus the exact names auth and inference_server_auth. The
suffix is now value-aware, because widening it turned up a false positive in the
first test written for it: requires_auth ends in _auth but a boolean cannot carry
a secret, and redacting it would delete provenance for nothing. Exact names still
redact whatever they hold; a suffix match requires a value a credential could
actually be, and anything not positively known to be harmless still redacts --
a missed credential in a published record is unrecoverable, an over-redacted
setting is not.

Two tests: one pins inference_server_auth (and that the server ADDRESS, which is
provenance rather than a credential, survives), one pins that requires_auth and
authorized_users are not swallowed. 459 passed, 20 skipped; ruff clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant