Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions litellm/proxy/batches_endpoints/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,44 @@
update_batch_in_database,
)
from litellm.proxy.utils import handle_exception_on_proxy, is_known_model
from litellm.repositories.table_repositories import ManagedFileRepository
from litellm.types.llms.openai import LiteLLMBatchCreateRequest

router = APIRouter()


async def _resolve_managed_input_file_storage_url(input_file_id: str) -> "str | None":
"""Resolve a managed (unified) input_file_id to its backend storage_url.

Provider batch handlers (e.g. Vertex AI, which parses a `publishers/`
segment out of the file URI) need a real storage location; the opaque
unified token crashes them. Returns None only when there is no database or
the row has no storage_url yet, so callers fall back to the original id
(which the managed-files deployment hook can still map). Fails closed
rather than dispatch a token that cannot be resolved: 404 when no
managed-file row exists, 503 when the lookup itself errors so the caller
can retry.
"""
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
return None
Comment thread
greptile-apps[bot] marked this conversation as resolved.
try:
db_file = await ManagedFileRepository(prisma_client).table.find_first(where={"unified_file_id": input_file_id})
except Exception as e:
verbose_proxy_logger.warning("create_batch: managed file lookup failed for %s: %s", input_file_id, e)
raise HTTPException(
status_code=503,
detail={"error": "Could not resolve managed file; please retry"},
)
if db_file is None:
raise HTTPException(
status_code=404,
detail={"error": f"Managed file not found: {input_file_id}"},
)
return db_file.storage_url or None
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.


@router.post(
"/{provider}/v1/batches",
dependencies=[Depends(user_api_key_auth)],
Expand Down Expand Up @@ -224,6 +257,11 @@ async def create_batch(
)
model = target_model_names[0]
_create_batch_data["model"] = model

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.

LB path skips file ownership

High Severity

Moving the _resolve_managed_input_file_storage_url call into the unified-file branch bypasses ownership checks for load-balanced batch creations. When enable_loadbalancing_on_batch_endpoints is active and a model is specified, the ownership check is skipped, allowing cross-tenant access to managed files.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit df7fa74. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Superseded: as of the latest commit the resolve call does no ownership check on any branch, by design. Ownership for batch create is out of scope for this resolution PR (a pre-existing gap across all managed-file call types) and is tracked for a source fix in the enterprise pre-call hook in LIT-4797. The PR description was updated to say so

resolved_storage_url = await _resolve_managed_input_file_storage_url(input_file_id)
if resolved_storage_url is not None:
_create_batch_data["input_file_id"] = resolved_storage_url

if llm_router is None:
raise HTTPException(
status_code=500,
Expand Down
Loading
Loading