fix(files): constrain cloud storage file paths (VERIA-45, VERIA-59) - #27019
fix(files): constrain cloud storage file paths (VERIA-45, VERIA-59)#27019yuneng-berri merged 7 commits into
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR hardens cloud storage paths for Vertex AI, Bedrock, and GCS logging by introducing a shared Confidence Score: 5/5PR is safe to merge; only P2 findings remain after previous review threads addressed the critical issues. No new P0/P1 issues found. The two remaining comments are P2 operability suggestions (global env var escape hatch for litellm/litellm_core_utils/cloud_storage_security.py — operability gap for global legacy-ID opt-in
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/cloud_storage_security.py | New shared security module: bucket/object validation, trusted-credential helpers, and URL-encoding utilities. Well-structured with MappingProxyType enforcement and idempotent encoding via unquote→quote pattern. |
| litellm/llms/bedrock/files/handler.py | Adds bucket-validation and managed-prefix enforcement for Bedrock S3 file retrieval. _get_configured_s3_bucket_name correctly reads from the MappingProxyType snapshot first; called with litellm_params_dict (passed as optional_params) from main.py. |
| litellm/llms/vertex_ai/files/handler.py | Threads litellm_params through afile_content and _extract_bucket_and_object_from_file_id, fixing the legacy-flag silent-ignore noted in previous threads. Service-account config now pinned from gcs_logging_config. |
| litellm/llms/vertex_ai/files/transformation.py | Enforces managed-prefix validation in _parse_gcs_uri and uses server-generated object names for uploads. Removes the litellm_metadata.gcs_bucket_name override path. Bucket sourced via _get_configured_bucket_name helper only. |
| litellm/llms/bedrock/files/transformation.py | Uses build_managed_cloud_object_name for upload paths, URL-encodes S3 object keys, and decodes keys extracted from HTTPS response URLs before constructing s3:// URIs. Correct use of safe="/" for S3 path-style URLs. |
| litellm/integrations/gcs_bucket/gcs_bucket_base.py | Centralizes URL encoding of GCS object names in download/delete/upload methods via encode_gcs_object_name_for_url. Delegates bucket/prefix splitting to split_configured_cloud_bucket_name. |
| litellm/files/main.py | Adds _add_trusted_model_credentials_to_litellm_params helper and calls it in retrieve, delete, and content paths so that _litellm_internal_model_credentials flows through to provider validation. |
| litellm/proxy/openai_files_endpoints/common_utils.py | Adds include_internal_credentials flag to prepare_data_with_credentials; creates an immutable MappingProxyType snapshot of server credentials for security-sensitive code paths. |
Reviews (6): Last reviewed commit: "trust only server legacy file flag" | Re-trigger Greptile
|
Pushing back on both P1 findings — both safeguards are already in the PR: P1 #1 (asymmetric bucket lookup): The asymmetry between handler and upload path is intentional defense-in-depth. The handler explicitly refuses to trust P1 #2 (no feature flag for managed-prefix enforcement): The PR ships an opt-in flag for exactly this — |
|
Both pushbacks check out against the code: P1 #1 — acknowledged. P1 #2 — acknowledged. One small inconsistency worth noting: the Vertex AI handler calls Withdrawing both P1 flags. |
| if "gcs_log_id" in _metadata: | ||
| object_name = _metadata["gcs_log_id"] | ||
| safe_log_id = sanitize_cloud_object_component( | ||
| _metadata.get("gcs_log_id"), fallback="" | ||
| ) | ||
| if safe_log_id: | ||
| object_name = f"{current_date}/custom-{uuid.uuid4().hex}-{safe_log_id}" |
There was a problem hiding this comment.
Backwards-incompatible
gcs_log_id behavior change without a feature flag
Previously gcs_log_id in metadata was used as the exact GCS object path. This PR changes it to a sanitized hint incorporated into a new randomized path ({date}/custom-{uuid}-{safe_hint}). Any deployment that reads GCS log objects by a predictable gcs_log_id-derived path (e.g., external tooling, dashboards, or audit pipelines that construct GCS paths from known gcs_log_id values) will silently stop finding those objects after this change.
Per the project's backwards-compatibility rule, breaking behavior changes should be gated behind a server-side flag (e.g. litellm.enforce_safe_gcs_log_paths or an env var), so existing deployments can opt in on their own schedule. The analogous file retrieval path has allow_legacy_cloud_file_ids for exactly this reason.
Rule Used: What: avoid backwards-incompatible changes without... (source)
|
Update on the open findings:
All findings here are about the strictness of the deny path, not the deny path being incorrect. Holding the line; the maintainer can decide if a feature flag is warranted for the gcs_log_id case. |
|
The resolved findings are accepted — the reasoning for all three stands up:
On the one remaining open thread — The security argument is valid for raw/unsanitized values. But there's a narrower framing that avoids re-opening the injection vector: the current code calls A flag like
...would restore predictable naming for deployments using safe log IDs without re-enabling path injection, because That said — the developer has explicitly flagged this for the maintainer to decide, and that's the right call. If the maintainer is comfortable with the migration cost for external tooling, the strict path is defensible. If a flag is added, the above scoping (re-enable predictable naming over sanitized value, not raw value) is the safe boundary. Maintaining the finding as unresolved pending maintainer decision. |
…odex/cloud-storage-file-guard # Conflicts: # litellm/llms/vertex_ai/files/handler.py # litellm/llms/vertex_ai/files/transformation.py
07807f7
into
BerriAI:litellm_internal_staging
…guard fix(files): constrain cloud storage file paths (VERIA-45, VERIA-59)
…dy bouncer Two cleanups: * ``LiteLLMSkillsHandler.create_skill`` raised ``HTTPException`` for identity-less callers, importing FastAPI from a ``litellm/llms/`` module — that violates the project rule that FastAPI lives only under ``proxy/``. Switch to ``ValueError`` (the same shape the rest of the handler uses for not-found/forbidden) and update the test. * The proxy-auth body bouncer derived its observability ban list from ``_supported_callback_params`` only, missing ``_request_blocked_callback_params`` (where ``gcs_bucket_name`` and ``gcs_path_service_account`` live). Two recently-merged sibling PRs (BerriAI#27019 added the deny list, BerriAI#27081 added the test asserting these are rejected at the request body root) crossed without folding them together. Union the GCS deny list into the bouncer's derivation so the single source of truth covers both code paths.
Summary
Tightens the cloud-storage-backed file and logging paths for Vertex AI, Bedrock, and GCS logging.
gcs_log_idas a sanitized hint instead of an exact path, and does not derive GCS bucket/service-account callback config from request kwargs or metadata.Behavior changes
Test plan
uv run pytest tests/test_litellm/llms/vertex_ai/files tests/test_litellm/llms/bedrock/files tests/test_litellm/integrations/gcs_bucket tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py -q— 82 passuv run ruff check litellm/litellm_core_utils/cloud_storage_security.py litellm/llms/vertex_ai/files litellm/llms/bedrock/files litellm/integrations/gcs_bucket litellm/litellm_core_utils/initialize_dynamic_callback_params.py tests/test_litellm/llms/vertex_ai/files tests/test_litellm/llms/bedrock/files tests/test_litellm/integrations/gcs_bucket tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.pyuv run black --checkon touched filesType
🐛 Bug Fix
✅ Test