Skip to content

fix(security): add URL safety check to image_ref fetch in openai image_gen (SSRF) - #56035

Open
AlexFucuson9 wants to merge 1 commit into
NousResearch:mainfrom
AlexFucuson9:fix/image-gen-ssrf-url-validation
Open

fix(security): add URL safety check to image_ref fetch in openai image_gen (SSRF)#56035
AlexFucuson9 wants to merge 1 commit into
NousResearch:mainfrom
AlexFucuson9:fix/image-gen-ssrf-url-validation

Conversation

@AlexFucuson9

Copy link
Copy Markdown
Contributor

Problem (P1 Security — SSRF)

The _load_image_bytes() function in plugins/image_gen/openai/__init__.py fetches arbitrary URLs passed via the ref parameter from model tool calls without any URL safety validation:

resp = requests.get(ref, timeout=60)  # ref is model-supplied, no validation

A model-supplied URL can point to:

  • Cloud metadata endpoints (http://169.254.169.254/latest/meta-data/...) — leaks AWS/GCP/Azure credentials
  • Internal network addresses (http://10.0.0.1:8080/admin) — scans/exfiltrates internal services
  • Localhost services (http://127.0.0.1:6379/) — accesses Redis, databases, etc.

This is a server-side request forgery (SSRF) vulnerability through the gateway process.

Fix

Add is_safe_url() check from tools.url_safety before fetching, matching the pattern already used in:

  • tools/vision_tools.py:212 — image URL validation
  • tools/web_tools.py:100 — web search URL validation
  • tools/skills_hub.py:40 — skill URL validation
  • plugins/platforms/slack/adapter.py:1646 — Slack attachment validation
  • plugins/platforms/mattermost/adapter.py:500 — Mattermost attachment validation

Scope

Only the openai image_gen plugin fetches user-supplied URLs via ref. Other image_gen plugins (krea, xai, openrouter) only call their respective API endpoints with server-controlled URLs — no SSRF risk.

Testing

  • python3 -m py_compile plugins/image_gen/openai/__init__.py — OK
  • The is_safe_url function blocks private IPs (10.x, 172.16-31.x, 192.168.x), link-local (169.254.x), localhost, and metadata endpoints

Ref: #54553 (previous SSRF fix attempt, not merged)

…e_gen

The _load_image_bytes() function fetches arbitrary URLs passed via
the `ref` parameter (from model tool calls) without any URL safety
validation. A model-supplied URL pointing to cloud metadata endpoints
(e.g. http://169.254.169.254/...) or internal network addresses
allows server-side request forgery (SSRF) through the gateway.

Add is_safe_url() check from tools.url_safety before fetching,
matching the pattern already used in vision_tools.py, web_tools.py,
skills_hub.py, and platform adapters (Slack, Mattermost).

Ref: NousResearch#54553 (previous SSRF fix attempt, not merged)
@alt-glitch alt-glitch added type/security Security vulnerability or hardening P2 Medium — degraded but workaround exists comp/plugins Plugin system and bundled plugins tool/vision Vision analysis and image generation provider/openai OpenAI / Codex Responses API sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data duplicate This issue or pull request already exists labels Jul 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #54553 — that earlier open PR adds the identical is_safe_url() SSRF guard to the same _load_image_bytes() branch in plugins/image_gen/openai/__init__.py (only the error-message string differs). Consolidating on #54553 as the canonical fix. Related SSRF work on the provider-returned-URL path lives in #45537 / #44743 (issue #44728), a different code site.

@teknium1 teknium1 left a comment

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.

Thanks for addressing the unguarded OpenAI image-reference fetch; current main still has the direct requests.get(ref, timeout=60) path at plugins/image_gen/openai/__init__.py:134-140.

Problems

  • The new initial is_safe_url(ref) check does not cover redirects. requests.get() follows redirects by default, so a public URL can redirect to a metadata/private address. tools/vision_tools.py:405-417 documents this exact bypass and re-validates every redirect target.
  • The PR changes no tests. tests/plugins/image_gen/test_openai_provider.py:127-186 currently covers local-file and data-URI loading only, not URL or redirect SSRF behavior.

Suggested changes

  • Validate every redirect hop (or use a redirect-safe downloader) rather than only the initial URL.
  • Add mocked direct-unsafe-URL and public-to-private-redirect regression tests.

Automated hermes-sweeper review.

if lower.startswith(("http://", "https://")):
from tools.url_safety import is_safe_url

if not is_safe_url(ref):

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.

requests.get() follows redirects by default, so this only validates the first hop. A public URL can still redirect to a metadata/private address; validate every redirect target before issuing its request, as tools/vision_tools.py:405-417 does.

@teknium1 teknium1 added the sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users label Jul 15, 2026
zapabob added a commit to zapabob/hermes-agent-windows that referenced this pull request Aug 23, 2026
## Summary
- Harden `_load_image_bytes` so redirect hops are re-checked with `is_safe_url` (`allow_redirects=False`).
- Close the gap where a pre-check alone still followed a malicious `Location` into metadata/private hosts.
- Add focused regression tests.

## Salvage / credit
Incomplete prior fixes NousResearch#54553 / NousResearch#56035 (pre-check without redirect hop validation).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists provider/openai OpenAI / Codex Responses API sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/vision Vision analysis and image generation type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants