* refac(routers): reject external URLs in profile/model image handlers * refac(ui): centralize image URL validation in safeImageUrl helper * refac(auths): make signout POST-only * refac: gate external profile image redirect behind ENABLE_PROFILE_IMAGE_URL_FORWARDING Restore the 302 redirect for external http(s) profile image URLs in the user and model profile-image endpoints, but gate it behind a new ENABLE_PROFILE_IMAGE_URL_FORWARDING env flag (default: True). Existing deployments that rely on external profile image forwarding continue to work unchanged. Operators who want to suppress the redirect (to prevent client-side IP/UA/Referer leaks) can set the flag to False.
34 lines
939 B
TypeScript
34 lines
939 B
TypeScript
import { WEBUI_BASE_URL } from '$lib/constants';
|
|
|
|
const PLACEHOLDER_IMAGE = '/favicon.png';
|
|
|
|
/**
|
|
* Validates an image URL against an allowlist of safe patterns and returns
|
|
* the URL if trusted, or a placeholder otherwise.
|
|
*
|
|
* Allowed patterns:
|
|
* - Relative paths (starting with '/')
|
|
* - data:image/* URIs
|
|
* - Same-origin URLs (starting with WEBUI_BASE_URL)
|
|
* - Gravatar URLs (https://www.gravatar.com/avatar/)
|
|
*
|
|
* All other URLs (including arbitrary http(s):// origins) are rejected to
|
|
* prevent client-side IP/UA/Referer leaks to attacker-controlled servers.
|
|
*/
|
|
export function safeImageUrl(url: string): string {
|
|
if (!url || url === '') {
|
|
return `${WEBUI_BASE_URL}${PLACEHOLDER_IMAGE}`;
|
|
}
|
|
|
|
if (
|
|
url.startsWith(WEBUI_BASE_URL) ||
|
|
url.startsWith('https://www.gravatar.com/avatar/') ||
|
|
url.startsWith('data:') ||
|
|
url.startsWith('/')
|
|
) {
|
|
return url;
|
|
}
|
|
|
|
return `${WEBUI_BASE_URL}${PLACEHOLDER_IMAGE}`;
|
|
}
|