open-webui/backend/open_webui/models
Classic298 5e7244ab8b fix: gate OAuth profile picture MIME at ingestion + storage layer
Three defense-in-depth gaps from GHSA-3wgj-c2hg-vm6q that the v0.9.5
serving-side fix didn't address:

1. _process_picture_url (utils/oauth.py): MIME was inferred from the
   URL extension via mimetypes.guess_type, the upstream Content-Type
   was discarded, and there was no allowlist. An SVG picture URL
   produced data:image/svg+xml;base64,... in the user's
   profile_image_url. Switch to the upstream Content-Type and gate it
   against PROFILE_IMAGE_ALLOWED_MIME_TYPES (the same env var the
   serving endpoint already uses); fall back to /user.png if the MIME
   isn't in the allowlist. Also pass allow_redirects=AIOHTTP_CLIENT_
   ALLOW_REDIRECTS on the aiohttp session.get — the existing
   validate_url() only checks the initial URL, redirects to internal
   targets would otherwise still be followed (same class as the
   rh5x-h6pp-cjj6 cluster, sixth call site).

2. update_user_profile_image_url_by_id (models/users.py): SQLAlchemy
   write path bypassed the Pydantic form validators, so anything
   stored via OAuth or any other non-form caller landed in the column
   unchallenged. Run validate_profile_image_url at the storage layer
   before the assignment.

3. insert_new_user (models/users.py): same gap on the new-user path
   used by Auths.insert_new_auth (OAuth signup, LDAP). Same
   storage-layer call to validate_profile_image_url, falling back to
   /user.png if the supplied value doesn't pass.

The serving-endpoint allowlist landed in v0.9.5 already broke the
exploit chain matte1782 demonstrated (browser never receives
Content-Type: image/svg+xml), but bad data was still being written
to the DB and the upstream MIME was never trusted. These three
fixes harden the ingestion + storage layers so future serving paths
or DB readers don't have to assume the column is clean.

Reported by matte1782 in GHSA-3wgj-c2hg-vm6q.

Co-authored-by: matte1782 <matte1782@users.noreply.github.com>
2026-05-10 21:52:16 +02:00
..
access_grants.py chore: format 2026-04-12 18:12:59 -05:00
auths.py chore: format 2026-04-12 18:12:59 -05:00
automations.py chore: format 2026-04-19 22:45:54 +09:00
calendar.py refac 2026-05-09 06:33:26 +09:00
channels.py Apply validate_profile_image_url to ChannelWebhookForm.profile_image_url (#24370) 2026-05-09 03:19:25 +09:00
chat_messages.py chore: format 2026-05-09 15:25:27 +09:00
chats.py chore: format 2026-05-11 02:29:13 +09:00
feedbacks.py fix: prevent mass-assignment user_id spoofing in POST /api/v1/evaluations/feedback (#24508) 2026-05-11 01:16:17 +09:00
files.py chore: format 2026-04-12 18:12:59 -05:00
folders.py fix: drop extra='allow' on FolderForm and FolderUpdateForm (#23648) 2026-04-13 16:14:00 -05:00
functions.py chore: format 2026-04-12 18:12:59 -05:00
groups.py chore: format 2026-04-12 18:12:59 -05:00
knowledge.py refac 2026-05-09 07:34:46 +09:00
memories.py refac 2026-05-09 06:23:51 +09:00
messages.py chore: format 2026-04-12 18:12:59 -05:00
models.py chore: format 2026-04-24 18:48:21 +09:00
notes.py Merge pull request #24486 from Classic298/fix/notes-is-pinned-typeerror 2026-05-09 20:56:06 +09:00
oauth_sessions.py chore: format 2026-04-24 18:48:21 +09:00
prompt_history.py refac: async db 2026-04-12 14:22:11 -05:00
prompts.py chore: format 2026-05-09 15:25:27 +09:00
shared_chats.py chore: format 2026-04-17 14:28:18 +09:00
skills.py chore: format 2026-04-12 18:12:59 -05:00
tags.py chore: format 2026-04-12 18:12:59 -05:00
tools.py chore: format 2026-04-12 18:12:59 -05:00
users.py fix: gate OAuth profile picture MIME at ingestion + storage layer 2026-05-10 21:52:16 +02:00