Skip to content

fix(gallery): block SSRF in gallery config URL fetch (#10665)#10673

Merged
mudler merged 1 commit into
masterfrom
fix/ssrf-models-apply-10665
Jul 3, 2026
Merged

fix(gallery): block SSRF in gallery config URL fetch (#10665)#10673
mudler merged 1 commit into
masterfrom
fix/ssrf-models-apply-10665

Conversation

@localai-bot

Copy link
Copy Markdown
Collaborator

Description

Fixes #10665Unauthenticated SSRF in POST /models/apply.

When the id field is empty, the gallery worker skips the gallery lookup and fetches the attacker-supplied url gallery config directly via http.Client, with no check that the URL resolves to a public IP. In the default Docker deployment no API key is configured, so any network-reachable client can coerce LocalAI into issuing requests to internal services or cloud-metadata endpoints (e.g. 169.254.169.254), and exfiltrate a small slice of the response body through the job error message.

The live path from the report:

POST /models/apply  (empty id, attacker url)
  └─ LocalModelManager.InstallModel  (default case, no id / no gallery element)
       └─ installModelFromRemoteConfig
            └─ gallery.GetGalleryConfigFromURLWithContext  ← fetches the URL, unguarded

Fix

Guard the two config-fetch chokepoints — GetGalleryConfigFromURL and GetGalleryConfigFromURLWithContext in core/gallery/gallery.go — with utils.ValidateExternalURL, the same helper already protecting the CORS proxy and the image/video/audio download endpoints. This blocks private, loopback, link-local, unspecified and cloud-metadata addresses.

Only plain http(s):// URLs are validated. Non-network schemes (huggingface://, github:, oci://, ollama://, file://) resolve to fixed public services or local files and are not a network-SSRF vector, so they are left untouched — existing gallery installs keep working.

Placing the guard at the shared chokepoint also covers the gallery-entry install path (InstallModelFromGallery) and backend config resolution (backend_resolve.go), not just the reported endpoint.

Tests

Added core/gallery/request_test.go cases that stand up a loopback httptest server which would serve a valid gallery config: without the guard the fetch succeeds (reproducing the SSRF), with the guard it is rejected before any request leaves the process. Also covers localhost, RFC-1918 ranges and the cloud-metadata IP. The existing github: parse test still passes.

Verified locally: go test ./core/gallery/ green, go vet clean, gofmt clean, golangci-lint --new-from-merge-base=origin/master reports 0 issues.


Assisted-by: Claude:claude-opus-4-8 [Claude Code]

POST /models/apply with an empty "id" fetches the attacker-supplied
"url" gallery config directly via http.Client, with no check that the
URL resolves to a public IP. In the default Docker deployment no API key
is configured, so any network-reachable client can coerce LocalAI into
issuing requests to internal services or cloud-metadata endpoints (and
exfiltrate a small slice of the response through the job error message).

Guard the config fetch chokepoints (GetGalleryConfigFromURL and
GetGalleryConfigFromURLWithContext, which back both the /models/apply
worker and gallery installs) with utils.ValidateExternalURL, matching
the protection already applied to the CORS proxy and image/video/audio
download paths. Only plain http(s) URLs are validated; non-network
schemes (huggingface://, github:, oci://, ollama://, file://) resolve to
fixed public services or local files and are left untouched.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:claude-opus-4-8 [Claude Code]
@mudler mudler enabled auto-merge (squash) July 3, 2026 20:47
@mudler mudler added the bug Something isn't working label Jul 3, 2026
@mudler mudler merged commit 2cbb3c9 into master Jul 3, 2026
60 checks passed
@mudler mudler deleted the fix/ssrf-models-apply-10665 branch July 3, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unauthenticated SSRF in POST /models/apply

2 participants