Skip to content

[Infra] Merge dev branch with main#25647

Merged
yuneng-berri merged 13 commits intomainfrom
litellm_yj_apr_11
Apr 14, 2026
Merged

[Infra] Merge dev branch with main#25647
yuneng-berri merged 13 commits intomainfrom
litellm_yj_apr_11

Conversation

@yuneng-berri
Copy link
Copy Markdown
Collaborator

Relevant issues

Pre-Submission checklist

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

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

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

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Screenshots / Proof of Fix

Type

🚄 Infrastructure

Changes

[Fix] Align field-level checks in user and key update endpoints
- Reject os.environ/ references supplied via /health/test_connection
  request params instead of resolving them; config-sourced values are
  already resolved before reaching the endpoint.
- Skip os.environ/ references in dynamic callback params loaded from
  per-request metadata.
- Constrain oidc/file/ to an allowed credential directory allowlist
  (defaults to /var/run/secrets and /run/secrets, overridable via
  LITELLM_OIDC_ALLOWED_CREDENTIAL_DIRS).
- Log a warning when dropping callback params that carry os.environ/
  references so operators notice the misconfiguration.
- Require absolute paths in oidc/file/ and correct the documented
  example to use the leading-slash form.
- Drop the unused return value from _reject_os_environ_references.
- Add LITELLM_OIDC_ALLOWED_CREDENTIAL_DIRS to the environment variables
  reference so the documentation test passes.
- Annotate the values variable in _reject_os_environ_references so it
  accepts both dict.values() and list iterables.
…dling

[Fix] tighten handling of environment references in request parameters
…ests

Brings the date-range branch in line with the non-date-range branch which
already hashes sk- prefixed tokens before querying. Adds coverage for
filter-combination behavior in view_spend_logs.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 13, 2026 7:01pm

Request Review

[Fix] /spend/logs: align filter handling with user scoping
@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq bot commented Apr 13, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_yj_apr_11 (df75e79) with main (d319cd8)

Open in CodSpeed

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 13, 2026

Greptile Summary

This infrastructure PR merges the dev branch into main, delivering a set of security hardening changes: OIDC file provider path-traversal protection via an allowlist, explicit rejection of os.environ/ references in request-supplied callback params and model-connection params, proxy-admin-only guards for user-role modification and bulk user updates, and an extension of the admin-only guard to cover spend field changes on keys. The spend-log filtering logic was also refactored from an elif chain to combined if statements, fixing a gap where internal users could query by api_key without their own user_id being enforced as a scope constraint.

Confidence Score: 5/5

Safe to merge; all new findings are P2 style/cleanup with no impact on correctness or security.

Every new finding in this review is P2 (one unreachable return None). The security improvements are well-tested with mock-only unit tests. Previously flagged P1 concerns from earlier threads were partially addressed (os.environ/ now raises an error) or are pre-existing gaps not introduced by this PR.

litellm/proxy/spend_tracking/spend_management_endpoints.py (dead return None at line 2406, harmless).

Important Files Changed

Filename Overview
litellm/secret_managers/main.py Adds OIDC file-provider path-traversal protection via allowlist with os.path.realpath normalization and os.path.commonpath containment check — logic is correct and well-tested.
litellm/proxy/spend_tracking/spend_management_endpoints.py Converts elif-chain filters to combined if-statements (security fix for internal users) and replaces legacy get_data() path with direct Prisma find_many(); contains one unreachable return None at line 2406.
litellm/litellm_core_utils/initialize_dynamic_callback_params.py Replaces silent os.environ/ resolution with an explicit ValueError; adds helper _is_env_reference/_raise_env_reference_error with good test coverage.
litellm/proxy/health_endpoints/_health_endpoints.py Replaces _resolve_os_environ_variables with _reject_os_environ_references (raises HTTP 400 instead of resolving); traversal and HTTPException logic are correct.
litellm/proxy/management_endpoints/internal_user_endpoints.py Adds proxy-admin-only guards for user_role modification (single and bulk) and all_users bulk update path; guards are placed correctly before DB operations.
litellm/proxy/management_endpoints/key_management_endpoints.py Extends max_budget admin guard to cover spend modification; mostly formatting changes; logic correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Request arrives] --> B{os.environ/ reference\nin request params?}
    B -- Yes --> C[Raise ValueError / HTTP 400\nclear guidance returned to caller]
    B -- No --> D{Endpoint type}

    D -- /spend/logs --> E{User role?}
    E -- INTERNAL_USER --> F[Override user_id with\nauthenticated user_id]
    F --> G[Apply ALL filters simultaneously\napi_key AND request_id AND user_id]
    E -- PROXY_ADMIN --> G

    D -- /user/update --> H{Modifying user_role\nor all_users?}
    H -- Yes --> I{Is PROXY_ADMIN?}
    I -- No --> J[HTTP 403]
    I -- Yes --> K[Proceed]
    H -- No --> K

    D -- /key/update --> L{Modifying max_budget\nor spend?}
    L -- Yes --> M{Is admin role?}
    M -- No --> N[HTTP 403]
    M -- Yes --> K

    D -- oidc/file/path --> O{Path within\nallowlist?}
    O -- No --> P[ValueError: outside\nallowed credential dirs]
    O -- Yes --> Q[Read token file]
Loading

Reviews (3): Last reviewed commit: "raise ValueError on os.environ/ referenc..." | Re-trigger Greptile

Comment on lines +54 to +61
if _is_env_reference(_param_value):
verbose_logger.warning(
"Dropping callback param '%s': os.environ/ references "
"in request-supplied parameters are not resolved. "
"Configure this value server-side instead.",
param,
)
continue
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.

P2 Silent drop of os.environ/ references breaks existing integrations

Previously, os.environ/ references in request-supplied callback params were resolved server-side; they are now silently dropped (only a verbose_logger.warning is emitted). Any user who passed {"langfuse_public_key": "os.environ/LANGFUSE_KEY"} in their request body will silently lose their observability integration with no error response and potentially no visible log, depending on their logging configuration. Per the project's backwards-compatibility rule, this should either raise a clear error or be gated behind a feature flag.

Consider raising an HTTPException or propagating an error back to the caller so the behavior change is visible, rather than continuing silently.

Rule Used: What: avoid backwards-incompatible changes without... (source)

Comment on lines +1134 to +1142
# Only proxy admins can modify user_role
if (
user_request.user_role is not None
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN.value
):
raise HTTPException(
status_code=403,
detail="Only proxy admins can modify user roles.",
)
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.

P2 No unit tests for new user-role authorization guard

The new user_role-modification check (and the corresponding bulk-update guard at line ~1543) is security-sensitive — a regression here could allow privilege escalation. Per the project's CLAUDE.md requirement to always add tests for new features, please add unit tests covering at least: (1) non-admin attempting to modify user_role is rejected with 403, and (2) proxy admin is permitted to change user_role.

Comment on lines +1964 to 1979
# Admin-only: only proxy admins, team admins, or org admins can modify max_budget or spend
if (
data.max_budget is not None and data.max_budget != existing_key_row.max_budget
) or (
data.spend is not None
and data.spend != getattr(existing_key_row, "spend", None)
):
if prisma_client is not None:
hashed_key = existing_key_row.token
await _check_key_admin_access(
user_api_key_dict=user_api_key_dict,
hashed_token=hashed_key,
prisma_client=prisma_client,
user_api_key_cache=user_api_key_cache,
route="/key/update (max_budget)",
route="/key/update (max_budget/spend)",
)
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.

P2 Admin check for spend modification is skipped when prisma_client is None

The guard that enforces admin-only access for changing spend (and max_budget) is nested inside if prisma_client is not None:. If no database is connected, the check is silently bypassed and the update proceeds. This inherits the same gap that existed for max_budget before this PR, so it is pre-existing; however, extending the guard to cover spend makes the omission more significant. Consider raising early if prisma_client is None when an admin-level field is being modified.

with open(oidc_aud, "r") as f:
# Load token from a file within an allowed credential directory.
safe_path = _resolve_oidc_file_path(oidc_aud)
with open(safe_path, "r") as f:
…ck params

Previously these were silently dropped with a verbose warning, which
could break observability integrations without surfacing a clear error.
Now raises ValueError with remediation steps (configure server-side
or pass the resolved value) so callers get immediate, actionable feedback.
@yuneng-berri yuneng-berri merged commit 8427534 into main Apr 14, 2026
102 of 108 checks passed
@yuneng-berri yuneng-berri deleted the litellm_yj_apr_11 branch April 14, 2026 00:28
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