Skip to content

merge main#25524

Merged
Sameerlite merged 25 commits intolitellm_fix-websearch-interception-loggingfrom
main
Apr 10, 2026
Merged

merge main#25524
Sameerlite merged 25 commits intolitellm_fix-websearch-interception-loggingfrom
main

Conversation

@Sameerlite
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:

Type

🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test

Changes

yuneng-berri and others added 25 commits April 8, 2026 17:21
…hardening

Port security fixes from litellm_v1.82.3.dev.6:
- Use secureStorage (sessionStorage wrapper) instead of raw storage for tokens
- Add URL validation for stored worker URLs to prevent open redirects
- Add same-origin checks before redirecting to stored return URLs
- Harden Dockerfile.health_check with non-root user and exec-form HEALTHCHECK
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Dockerfile.health_check: HEALTHCHECK now verifies the script is intact
  instead of unconditionally exiting 0
- secureStorage.ts: replace deprecated escape/unescape with
  encodeURIComponent/decodeURIComponent; don't delete legacy values on
  decode failure so in-flight flows can time out naturally
- OAuth callback: add same-origin check before redirecting to stored
  return URL
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Remove the silent try/catch from setSecureItem so OAuth hooks can
surface actionable "enable storage" guidance instead of a cryptic
"state lost" error after the round-trip. Add a local try/catch in
ChatUI where the storage write is non-critical.
…loyment best practices (#25439)

- New doc page covering all signed image variants, verification commands,
  CI/CD enforcement (K8s Sigstore Policy Controller, GCP Binary Authorization,
  AWS/EKS, GitHub Actions), digest pinning, and safe upgrade patterns
- Added to sidebar under Setup & Deployment
- Cross-linked from the existing deploy.md cosign section

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>
…_model_test

fix(test): mock headers in test_completion_fine_tuned_model
feat(mcp): add per-user OAuth token storage for interactive MCP flows
[Fix] UI: improve storage handling and Dockerfile consistency
…st overrides

Raise vitest testTimeout from 10s to 30s and drop per-test timeout overrides
across UI unit tests. Group CreateUserButton and TeamInfo tests under nested
describe blocks to make the most flaky suites easier to scan.
…et_model_query_param

fix(responses-ws): append ?model= to backend WebSocket URL
Remove leftover 10000ms per-test timeout in add_model_tab.test.tsx that was
missed in the initial sweep. The test now inherits the 30000ms global.
MCP_PER_USER_TOKEN_DEFAULT_TTL and MCP_PER_USER_TOKEN_EXPIRY_BUFFER_SECONDS
were added in #25441 but not documented, causing test_env_keys.py to fail.
…_env_vars

[Docs] Add missing MCP per-user token env vars to config_settings
[Test] UI - Unit tests: raise global vitest timeout and remove per-test overrides
Unify UI and API token authorization through the shared RBAC path
and backfill missing routes in role-based route lists.
refactor: consolidate route auth for UI and API tokens
@github-advanced-security
Copy link
Copy Markdown
Contributor

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

import { render, screen, waitFor, fireEvent, act } from "@testing-library/react";
import MCPServerEdit from "./mcp_server_edit";
import * as networking from "../networking";
import NotificationsManager from "../molecules/notifications_manager";
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
5 out of 7 committers have signed the CLA.

✅ michelligabriele
✅ yuneng-berri
✅ joereyna
✅ csoni-cweave
✅ ryan-crabbe-berri
❌ ishaan-berri
❌ krrish-berri-2
You have signed the CLA already but the status is still pending? Let us recheck it.

@Sameerlite Sameerlite merged commit bec448d into litellm_fix-websearch-interception-logging Apr 10, 2026
44 of 101 checks passed
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 10, 2026

Greptile Summary

This PR merges main into the litellm_fix-websearch-interception-logging branch, incorporating several feature branches: a UI/API token RBAC consolidation (UI tokens now go through the same _is_api_route_allowed path as API tokens), per-user MCP OAuth token server-side storage with Redis caching and token refresh, frontend security hardening (CodeQL alert fixes, sessionStorage-only access token storage via a new secureStorage.ts utility), and Token Validation Rules / Storage TTL fields in the MCP server UI.

Confidence Score: 5/5

Safe to merge; all findings are P2 UI inconsistencies and naming clarity with no correctness or data-integrity regressions.

The RBAC refactor correctly migrates all previously allowed routes through global_spend_tracking_routes and internal_user_routes. Token refresh and Redis cache paths have proper error handling and fallback. The skip_byok_guard usage is safe in context. UI issues (M2M field visibility, secureStorage naming) do not affect backend behavior.

ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx — token validation fields shown for M2M OAuth in create flow (inconsistent with edit form)

Important Files Changed

Filename Overview
litellm/proxy/_types.py RBAC route consolidation: UI tokens now use the same _is_api_route_allowed path; global_spend_tracking_routes properly added to internal_user_routes, ui_routes kept for JWT backwards-compat but no longer drives auth logic.
litellm/proxy/auth/auth_checks.py Removes _is_ui_route / _is_allowed_route shims; common_checks now calls _is_api_route_allowed directly for all token types including UI dashboard tokens.
litellm/proxy/_experimental/mcp_server/db.py Adds refresh_user_oauth_token (POSTs to token endpoint) and skip_byok_guard parameter to store_user_oauth_credential to save the redundant DB lookup during refresh.
litellm/proxy/_experimental/mcp_server/oauth2_token_cache.py Adds MCPPerUserTokenCache (Redis-backed, NaCl-encrypted via user_api_key_cache) and helper _compute_per_user_token_ttl; singleton mcp_per_user_token_cache exported for use in server/mcp_server_manager.
litellm/proxy/_experimental/mcp_server/server.py Extends _get_user_oauth_extra_headers_from_db with Redis fast-path, auto-refresh on expiry, and Redis warm-up after DB lookup; also skips pre-emptive 401 for needs_user_oauth_token servers.
litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py Adds _validate_token_response (dot-notation field checks), _extract_user_id_from_request (cache-only user_id lookup), _store_per_user_token_server_side, and wires them into exchange_token_with_server.
ui/litellm-dashboard/src/utils/secureStorage.ts New shared utility that base64-encodes values before sessionStorage write; centralises storage access and eliminates duplicated inline setItem/getItem calls across OAuth hooks.
ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx Adds Token Validation Rules (JSON textarea) and Token Storage TTL (number input) fields unconditionally in the OAuth section — shown for both M2M and interactive flows, inconsistent with the edit form.

Sequence Diagram

sequenceDiagram
    participant Client
    participant MCP as MCP Server Endpoint
    participant Redis as Redis Cache
    participant DB as Prisma DB
    participant TokenEP as OAuth Token Endpoint

    Client->>MCP: list_tools or call_tool
    MCP->>Redis: get per-user token
    alt Cache hit
        Redis-->>MCP: decrypted access_token
        MCP-->>Client: forward with Authorization header
    else Cache miss
        MCP->>DB: get_user_oauth_credential
        DB-->>MCP: credential payload
        alt Valid token
            MCP->>Redis: store encrypted token with TTL
            MCP-->>Client: forward with Authorization header
        else Token expired with refresh_token
            MCP->>TokenEP: POST refresh_token grant
            TokenEP-->>MCP: new access_token
            MCP->>DB: store refreshed credential
            MCP->>Redis: update cache
            MCP-->>Client: forward with new Authorization header
        else No valid token
            MCP->>Redis: delete stale entry
            MCP-->>Client: null - re-auth required
        end
    end
Loading

Reviews (1): Last reviewed commit: "Merge pull request #25473 from BerriAI/l..." | Re-trigger Greptile

Comment on lines 152 to 199
<TextInput placeholder="https://example.com/oauth/register" className={fieldClassName} />
</Form.Item>
<Form.Item
label={
<FieldLabel
label="Token Validation Rules (optional)"
tooltip='JSON object of key-value rules checked against the OAuth token response before storing. Supports dot-notation for nested fields (e.g. {"organization": "my-org", "team.id": "123"}). Tokens that fail validation are rejected with HTTP 403.'
/>
}
name="token_validation_json"
rules={[
{
validator: (_: any, value: string) => {
if (!value || value.trim() === "") return Promise.resolve();
try {
JSON.parse(value);
return Promise.resolve();
} catch {
return Promise.reject(new Error("Must be valid JSON"));
}
},
},
]}
>
<Input.TextArea
placeholder={'{\n "organization": "my-org",\n "team.id": "123"\n}'}
rows={4}
className="font-mono text-sm rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
/>
</Form.Item>
<Form.Item
label={
<FieldLabel
label="Token Storage TTL (seconds, optional)"
tooltip="How long to cache each user's OAuth access token in Redis before evicting it (regardless of the token's own expires_in). Leave blank to derive the TTL from the token's expires_in, or fall back to the 12-hour default."
/>
}
name="token_storage_ttl_seconds"
>
<InputNumber
min={1}
placeholder="e.g. 3600"
className="w-full rounded-lg"
style={{ width: "100%" }}
/>
</Form.Item>
{oauthFlow && (
<div className="rounded-lg border border-dashed border-gray-300 p-4 space-y-2">
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 Token validation fields shown for M2M OAuth in create flow

token_validation_json and token_storage_ttl_seconds are rendered unconditionally in OAuthFormFields (outside the {oauthFlow && ...} block), so they appear for both M2M and interactive flows. The edit form correctly wraps them in {!isM2MFlow && ...}. For M2M servers needs_user_oauth_token is false so the backend ignores these values, but showing them in the create UI is misleading.

Comment on lines +1 to +34
function encode(value: string): string {
// btoa cannot handle characters outside Latin-1, so we percent-encode first.
return btoa(
encodeURIComponent(value).replace(
/%([0-9A-F]{2})/g,
(_, p1) => String.fromCharCode(parseInt(p1, 16))
)
);
}

function decode(encoded: string): string {
return decodeURIComponent(
atob(encoded)
.split("")
.map((c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0"))
.join("")
);
}

export function setSecureItem(key: string, value: string): void {
window.sessionStorage.setItem(key, encode(value));
}

export function getSecureItem(key: string): string | null {
try {
const raw = window.sessionStorage.getItem(key);
if (raw === null) return null;
return decode(raw);
} catch {
// Corrupted or non-encoded legacy value — return null without deleting
// so that in-flight flows (e.g. OAuth) can time out naturally.
return null;
}
}
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 Module name implies encryption but only provides base64 encoding

encode/decode use btoa/atob — standard base64, trivially reversible by any script with access to sessionStorage. The name secureStorage and function names setSecureItem/getSecureItem may lead future callers to assume at-rest encryption. The real security gain here is the correct use of sessionStorage over localStorage. A short clarifying comment at the top of the file (or a rename to sessionStorageUtils) would prevent future misuse.

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.

10 participants