Skip to content

fix(proxy): never render credential-bearing config keys in the bug report - #42493

Merged
ryan-crabbe-berri merged 1 commit into
mainfrom
litellm_bug_report_skip_credential_keys
Sep 22, 2026
Merged

ryan-crabbe-berri merged 1 commit into
mainfrom
litellm_bug_report_skip_credential_keys

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

How it solves it:

  • String values under credential-shaped keys are never rendered
  • Key parts: key, secret, token, password, url, host, base, endpoint and friends
  • key_management_system is the one such key that holds an enum, so it is excepted
  • Booleans and numbers under those keys still render, they cannot be a secret
  • Regression test sets every credential field to a known enum word and expects no line

User Flow

Before: a proxy admin whose dev Redis password is redis gets that password printed into the prefilled GitHub issue link

  1. The admin runs the proxy with litellm_settings.cache_params.password: redis and router_settings.redis_password: simple-shuffle
  2. A client sends POST https://litellm-domain/v1/chat/completions and hits an internal LiteLLM error, getting a 500 back
  3. The proxy log prints the "This looks like a bug in LiteLLM" line with a https://github.com/BerriAI/litellm/issues/new?... link
  4. Decoding the link, the Config block reads litellm_settings.cache_params.password = redis and router_settings.redis_password = simple-shuffle
  5. Anyone who can read the proxy log, or the issue if the admin files it as is, now knows both passwords

After: the same link carries no credential fields at all

  1. The admin runs the proxy with the same config
  2. A client sends the same POST https://litellm-domain/v1/chat/completions and gets the same 500
  3. The proxy log prints the same bug report line with a link
  4. Decoding the link, the Config block has cache_params.type = local and routing_strategy = simple-shuffle but no password or redis_password line
  5. Nobody learns a password from the log or the issue

Relevant issues

Follow-up to the Veria finding on #42428: #42428 (comment)

Affected release

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Screen recording of the run below, captured at tip 4fbceeb, is in the Slack thread: https://berriaillm.slack.com/archives/C0B302ZJU05/p1789865287481439

Two proxies, one worker each, same config, real OpenAI key. Base is origin/main at the merge base, head is this PR's tip. The config holds two secrets that collide with LiteLLM enum words, a custom callback that raises a bare KeyError for the trigger-bug alias so the proxy emits the bug report link, and the real gpt-4o-mini

model_list:
  - model_name: gpt-4o-mini
    litellm_params:
      model: openai/gpt-4o-mini
      api_key: os.environ/OPENAI_API_KEY
  - model_name: trigger-bug
    litellm_params:
      model: openai/gpt-4o-mini
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  drop_params: true
  callbacks: ["secret_hooks.bug_trigger"]
  cache: true
  cache_params:
    type: local
    password: redis

router_settings:
  routing_strategy: simple-shuffle
  redis_password: simple-shuffle

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  store_model_in_db: false

Decoding the last bug report link in a proxy log down to its Config block:

grep -o "https://github.com/BerriAI/litellm/issues/new[^ ]*" "$LOG" | tail -1 \
  | python3 -c "import sys,urllib.parse; print(urllib.parse.unquote_plus(sys.stdin.read()).split('Config (true/false flags and LiteLLM-defined values only):')[1])"

Before (c835a1a)

  1. curl -s -o /dev/null -w "HTTP %{http_code}\n" -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" -d '{"model":"trigger-bug","messages":[{"role":"user","content":"hi"}],"max_tokens":2}' http://127.0.0.1:18451/v1/chat/completions
  2. HTTP 500, and the proxy log ends with This looks like a bug in LiteLLM rather than in your request. File it with one click ... https://github.com/BerriAI/litellm/issues/new?template=bug_report.yml&...
  3. Decoded Config block of that link:
general_settings.store_model_in_db = false
litellm_settings.drop_params = true
litellm_settings.cache = true
litellm_settings.cache_params.type = local
litellm_settings.cache_params.password = redis
router_settings.routing_strategy = simple-shuffle
router_settings.redis_password = simple-shuffle
model_list[*].provider = [openai]

After (4fbceeb)

  1. Same curl against http://127.0.0.1:18452/v1/chat/completions
  2. HTTP 500, same bug report line in the proxy log
  3. Decoded Config block of that link:
general_settings.store_model_in_db = false
litellm_settings.drop_params = true
litellm_settings.cache = true
litellm_settings.cache_params.type = local
router_settings.routing_strategy = simple-shuffle
model_list[*].provider = [openai]
  1. curl -s -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Say hello from the credential key demo"}],"max_tokens":20}' http://127.0.0.1:18452/v1/chat/completions | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['model'], repr(d['choices'][0]['message']['content']))"
  2. gpt-4o-mini 'Hello from the credential key demo! How can I assist you today?'

Type

🐛 Bug Fix

Caveats (if any)

Low

  • Key matching is by underscore-separated parts, so a credential key with no such part still renders if its value matches an enum word
    • every credential field in the current allowlists has one of the parts
  • The verbose walk in feat(proxy): admin-only /debug/report sharing the bug report environment #42440 renders strings through its own scalar path, so it needs the same skip once it picks up this change

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Note

Low Risk
Narrows what config is serialized into bug-report URLs and logs; behavior change is limited to redaction of credential-key strings with no impact on runtime proxy config.

Overview
Fixes a information disclosure path where proxy bug-report links could echo secrets when the secret string happened to match a LiteLLM-known enum value (e.g. password: redis).

safe_config_lines now treats config keys whose underscore-separated parts match credential-like tokens (password, secret, token, url, host, etc.) as non-renderable for string values, even when the value is in the known-values allowlist. key_management_system is explicitly allowed through so legitimate enum values like aws_secret_manager still appear. Rendering helpers _render / _render_json take the field name so this check applies across general settings, cache params, router settings, and guardrail params.

A regression test sets every credential-shaped field to a known LiteLLM token and asserts those lines never appear in the output.

Reviewed by Cursor Bugbot for commit 4fbceeb. Bugbot is set up for automated code reviews on this repo. Configure here.

Link to Devin session: https://app.devin.ai/sessions/b7cc1b2b8d9d48a8b6a62d6b3a5e01bc
Open in Devin Desktop: https://app.devin.ai/desktop/session/b7cc1b2b8d9d48a8b6a62d6b3a5e01bc?variant=devin

…port

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
devin-ai-integration Bot requested a review from a team September 22, 2026 17:02
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@greptile-apps

greptile-apps Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with focused hardening and regression coverage preserving expected diagnostic output

Summary

This PR hardens bug-report configuration rendering by suppressing known string values under credential-shaped keys while retaining safe enum and boolean diagnostics

  • Adds centralized credential-key classification with a narrow key-management exception
  • Propagates field names through scalar and list rendering
  • Adds focused regression coverage across general, LiteLLM, cache, router, and guardrail settings

Reviews (1) · Last reviewed commit: "fix(proxy): never render credential-bear..."

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4fbceeb. Configure here.

@codspeed

codspeed Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_bug_report_skip_credential_keys (4fbceeb) with main (f3c920a)

Open in CodSpeed

return key not in ENUM_KEYS_WITH_CREDENTIAL_PARTS and not CREDENTIAL_KEY_PARTS.isdisjoint(key.lower().split("_"))


def _render_json(key: str, value: JsonValue) -> str | None:
@codecov

codecov Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ryan-crabbe-berri
ryan-crabbe-berri merged commit f275be5 into main Sep 22, 2026
124 of 126 checks passed
@ryan-crabbe-berri
ryan-crabbe-berri deleted the litellm_bug_report_skip_credential_keys branch September 22, 2026 17:17
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.

3 participants