[PR #23389] [MERGED] fix: replace brittle profile_image_url allowlist with safe-scheme validation #50226

Closed
opened 2026-04-30 02:51:18 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/23389
Author: @Classic298
Created: 4/3/2026
Status: Merged
Merged: 4/12/2026
Merged by: @tjbck

Base: devHead: fix/profile-image-url-validation


📝 Commits (8)

  • 87d666f fix: replace brittle profile_image_url allowlist with safe-scheme validation
  • 9af9635 Merge branch 'dev' into fix/profile-image-url-validation
  • a3a385c fix: harden profile image URL validation per review feedback
  • 3d579f2 fix: use structural validation instead of prefix checks
  • 90c6764 fix: validate hostname not netloc, fix misleading comment
  • 8dd0153 fix: constrain relative paths to known-safe prefixes
  • a8f7b74 fix: use exact matches and anchored regex, eliminate all prefix wildcarding
  • 3da3b6b fix: exclude query/fragment delimiters from user-ID regex segment

📊 Changes

1 file changed (+61 additions, -20 deletions)

View changed files

📝 backend/open_webui/utils/validate.py (+61 -20)

📄 Description

Fix: profile_image_url validation rejects OWUI's own paths, blocks user profile saves

Closes #23387

Problem

The profile_image_url validator used a hardcoded allowlist of two static paths and one Gravatar prefix. This rejected URLs that OWUI itself generates (like /api/v1/users/{id}/profile/image) and external OAuth avatar URLs, making it impossible to save user profiles from the admin panel.

Solution

Rewrote the validator in validate_profile_image_url to use a defense-in-depth approach with proper URL parsing:

  • Known static assets: Exact-match frozenset for /user.png, /favicon.png, /static/favicon.png — no wildcard prefix matching.
  • OWUI profile image route: Anchored regex that accepts only /api/v1/users/{id}/profile/image with the user-ID segment restricted to [^/?#]+ (no path traversal, no query/fragment injection).
  • External HTTP(S) URLs: Validated via urlparse with case-insensitive scheme matching and a non-empty hostname requirement (rejects bare schemes like https:// and port-only netlocs like http://:80/path).
  • Base64 data URIs: Regex-validated structure requiring ;base64, boundary and restricting to safe raster formats (png/jpeg/gif/webp). SVG is explicitly excluded because it can carry embedded scripts.

What's rejected

  • Arbitrary relative paths (prevents authenticated GET triggers against internal endpoints)
  • Scheme-relative URLs (//host/path)
  • Dangerous URI schemes (javascript:, file:, ftp:)
  • SVG data URIs
  • Malformed URLs (no host, no hostname, missing structure)
  • Query/fragment injection in the profile image route

Testing

30 test cases covering all accept/reject scenarios pass, including edge cases for path traversal, query injection, case sensitivity, and malformed inputs.

Contributor License Agreement

Note

Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/23389 **Author:** [@Classic298](https://github.com/Classic298) **Created:** 4/3/2026 **Status:** ✅ Merged **Merged:** 4/12/2026 **Merged by:** [@tjbck](https://github.com/tjbck) **Base:** `dev` ← **Head:** `fix/profile-image-url-validation` --- ### 📝 Commits (8) - [`87d666f`](https://github.com/open-webui/open-webui/commit/87d666fa3cf7a11a09dc23534e0f14ffeaafe33a) fix: replace brittle profile_image_url allowlist with safe-scheme validation - [`9af9635`](https://github.com/open-webui/open-webui/commit/9af96357511f3853491549a80b97799ddd477ecd) Merge branch 'dev' into fix/profile-image-url-validation - [`a3a385c`](https://github.com/open-webui/open-webui/commit/a3a385c4d93dcbc102d13f941de84f29c2553824) fix: harden profile image URL validation per review feedback - [`3d579f2`](https://github.com/open-webui/open-webui/commit/3d579f23e0c94f7a33bc2363d051051f7fd6d3e8) fix: use structural validation instead of prefix checks - [`90c6764`](https://github.com/open-webui/open-webui/commit/90c676481d4ecba9df8fca3ff3c40d7e04166a18) fix: validate hostname not netloc, fix misleading comment - [`8dd0153`](https://github.com/open-webui/open-webui/commit/8dd015368cd8777f1c364442c102f1c90e68870b) fix: constrain relative paths to known-safe prefixes - [`a8f7b74`](https://github.com/open-webui/open-webui/commit/a8f7b743ff92882ff600d1c9294d0cb4e9f77bc7) fix: use exact matches and anchored regex, eliminate all prefix wildcarding - [`3da3b6b`](https://github.com/open-webui/open-webui/commit/3da3b6b929bd9c003a05f03d6a59a6a4298181a7) fix: exclude query/fragment delimiters from user-ID regex segment ### 📊 Changes **1 file changed** (+61 additions, -20 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/utils/validate.py` (+61 -20) </details> ### 📄 Description ## Fix: profile_image_url validation rejects OWUI's own paths, blocks user profile saves Closes #23387 ### Problem The profile_image_url validator used a hardcoded allowlist of two static paths and one Gravatar prefix. This rejected URLs that OWUI itself generates (like /api/v1/users/{id}/profile/image) and external OAuth avatar URLs, making it impossible to save user profiles from the admin panel. ### Solution Rewrote the validator in validate_profile_image_url to use a defense-in-depth approach with proper URL parsing: - **Known static assets**: Exact-match frozenset for /user.png, /favicon.png, /static/favicon.png — no wildcard prefix matching. - **OWUI profile image route**: Anchored regex that accepts only /api/v1/users/{id}/profile/image with the user-ID segment restricted to [^/?#]+ (no path traversal, no query/fragment injection). - **External HTTP(S) URLs**: Validated via urlparse with case-insensitive scheme matching and a non-empty hostname requirement (rejects bare schemes like https:// and port-only netlocs like http://:80/path). - **Base64 data URIs**: Regex-validated structure requiring ;base64, boundary and restricting to safe raster formats (png/jpeg/gif/webp). SVG is explicitly excluded because it can carry embedded scripts. ### What's rejected - Arbitrary relative paths (prevents authenticated GET triggers against internal endpoints) - Scheme-relative URLs (//host/path) - Dangerous URI schemes (javascript:, file:, ftp:) - SVG data URIs - Malformed URLs (no host, no hostname, missing structure) - Query/fragment injection in the profile image route ### Testing 30 test cases covering all accept/reject scenarios pass, including edge cases for path traversal, query injection, case sensitivity, and malformed inputs. ### Contributor License Agreement <!-- 🚨 DO NOT DELETE THE TEXT BELOW 🚨 Keep the "Contributor License Agreement" confirmation text intact. Deleting it will trigger the CLA-Bot to INVALIDATE your PR. Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA. --> - [X] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. > [!NOTE] > Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-30 02:51:18 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#50226