Skip to content

[Fix] Align image URL fetch with validated HTTP client in Bedrock and token counter paths#26272

Merged
yuneng-berri merged 1 commit intolitellm_yj_apr22from
litellm_ssrf_img_url_fix
Apr 22, 2026
Merged

[Fix] Align image URL fetch with validated HTTP client in Bedrock and token counter paths#26272
yuneng-berri merged 1 commit intolitellm_yj_apr22from
litellm_ssrf_img_url_fix

Conversation

@yuneng-berri
Copy link
Copy Markdown
Collaborator

Summary

  • BedrockImageProcessor.get_image_details and get_image_details_async in factory.py were fetching image URLs via raw client.get() calls with follow_redirects=True. These are now routed through safe_get/async_safe_get, which validates the destination before connecting and handles redirects with per-hop validation.
  • _load_image_from_url in factory.py had the same pattern and is updated to use safe_get.
  • get_image_dimensions in token_counter.py used safe_get (address validation already present) but then called response.read() without a size bound. Added a Content-Length pre-check and a post-read size check using the existing MAX_IMAGE_URL_DOWNLOAD_SIZE_MB constant.

The safe_get/async_safe_get helpers and validate_url already existed in litellm_core_utils/url_utils.py and were already used by image_handling.py and base_ingestion.py; these changes bring the remaining image-fetch sites in line with that pattern.

Testing

  • Verified Bedrock image URL fetch paths now reject loopback, link-local, and RFC-1918 targets via live proxy on :4000.
  • Adjacent OpenAI text chat and public image URL handling unaffected.
  • pytest tests/test_litellm/litellm_core_utils/test_token_counter.py tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py — 232 passed, 1 skipped.

Type

🐛 Bug Fix
✅ Test

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 22, 2026

Greptile Summary

This PR routes three image-URL fetch sites (BedrockImageProcessor.get_image_details, get_image_details_async, and _load_image_from_url) through the existing safe_get/async_safe_get helpers, aligning them with the SSRF-protection pattern already in place for image_handling.py. It also adds Content-Length pre-check and post-read size guards to get_image_dimensions in token_counter.py.

The two P2 findings (post-read buffering in token_counter.py and absent size limits in the Bedrock paths) are follow-up hardening items rather than blockers.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 improvement suggestions, not blockers.

The SSRF-protection change is correct and consistent with the existing safe_get/async_safe_get pattern. The two open findings (post-read buffering in token_counter.py and missing size limits in Bedrock paths) are incremental hardening items that don't introduce regressions. All P2 — score stays at 5.

No files require special attention before merging.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/prompt_templates/factory.py Routes _load_image_from_url and both BedrockImageProcessor fetch methods through safe_get/async_safe_get for SSRF protection; no size bounds added to Bedrock paths.
litellm/litellm_core_utils/token_counter.py Adds Content-Length pre-check and post-read size guard to get_image_dimensions; post-read check still buffers the full response before the limit fires.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User-supplied image URL] --> B{safe_get / async_safe_get}
    B --> C[validate_url: DNS resolve + block-list check]
    C -->|blocked IP| D[SSRFError raised]
    C -->|allowed| E[client.get with follow_redirects=False]
    E -->|redirect| F{is_redirect?}
    F -->|yes, hop < 10| C
    F -->|too many hops| G[SSRFError: Too many redirects]
    F -->|no| H[Final response]
    H --> I1[factory.py BedrockImageProcessor - response.content buffered, no size limit]
    H --> I2[factory.py _load_image_from_url - response.content buffered, no size limit]
    H --> I3[token_counter.py get_image_dimensions - Content-Length pre-check, response.read then len check]
    I3 -->|exceeds limit| J[ValueError raised]
    I3 -->|within limit| K[Parse image dimensions]
Loading

Comments Outside Diff (1)

  1. litellm/litellm_core_utils/prompt_templates/factory.py, line 3565-3586 (link)

    P2 Bedrock image fetch paths lack a download size bound

    _post_call_image_processing accesses response.content (fully buffered) without any size limit. Both get_image_details_async and get_image_details now have SSRF protection, but a server can still serve an arbitrarily large response that gets loaded entirely into memory before the call returns. The image_handling.py pattern (Content-Length pre-check + iter_bytes streaming) would be the natural guard to add here as well.

Reviews (1): Last reviewed commit: "fix: align image URL fetch with validate..." | Re-trigger Greptile

Comment on lines 223 to +225
img_data = response.read()
if len(img_data) > max_bytes:
raise ValueError("Image response exceeds size limit")
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 Post-read size check still buffers entire response into memory

response.read() fully buffers the response body before the len(img_data) > max_bytes check fires. If the server omits Content-Length (or sends a deliberately low value), a very large image can exhaust heap before the guard triggers. The existing image_handling.py avoids this with iter_bytes(chunk_size=8192) and an in-flight byte counter. Aligning with that approach here would give a hard memory cap rather than just a post-hoc check.

@yuneng-berri yuneng-berri merged commit 11391eb into litellm_yj_apr22 Apr 22, 2026
144 of 149 checks passed
@yuneng-berri yuneng-berri deleted the litellm_ssrf_img_url_fix branch April 22, 2026 21:45
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.

1 participant