Commit graph

113 commits

Author SHA1 Message Date
Timothy Jaeryang Baek
50413f3482 refac 2026-08-31 10:47:49 -04:00
Timothy Jaeryang Baek
aeb126b95d refac 2026-08-30 23:46:02 -04:00
Classic298
2d18727ab8
perf: build info log messages lazily so raising the log level actually saves work (#27837)
Some checks failed
Python CI / Ruff Format (3.11) (push) Has been cancelled
Python CI / Ruff Format (3.12) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args: free_disk:false name:main suffix:]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args: free_disk:false name:main suffix:]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Has been cancelled
Create and publish Docker images with specific build args / merge (map[name:cuda suffix:-cuda]) (push) Has been cancelled
Create and publish Docker images with specific build args / merge (map[name:cuda126 suffix:-cuda126]) (push) Has been cancelled
Create and publish Docker images with specific build args / merge (map[name:main suffix:]) (push) Has been cancelled
Create and publish Docker images with specific build args / merge (map[name:ollama suffix:-ollama]) (push) Has been cancelled
Create and publish Docker images with specific build args / merge (map[name:slim suffix:-slim]) (push) Has been cancelled
Create and publish Docker images with specific build args / notify-helm-charts (push) Has been cancelled
Create and publish Docker images with specific build args / copy-to-dockerhub (, main) (push) Has been cancelled
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda, cuda) (push) Has been cancelled
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda126, cuda126) (push) Has been cancelled
Create and publish Docker images with specific build args / copy-to-dockerhub (-ollama, ollama) (push) Has been cancelled
Create and publish Docker images with specific build args / copy-to-dockerhub (-slim, slim) (push) Has been cancelled
Raising GLOBAL_LOG_LEVEL to WARNING buys quieter output but not less work: 241 INFO call sites interpolate their payload into an f-string before the logging call gets to drop it. The heaviest is get_doc, which logs every chunk id and metadata dict in a collection, so on the full-context retrieval path that is the entire knowledge base, once per chat request.

That one line at WARNING, CPython 3.12:

| knowledge base | payload | before   | after   |
| -------------- | ------- | -------- | ------- |
| top-k of 3     | 1.2 kB  | 3.8 us   | 0.07 us |
| 500 chunks     | 201 kB  | 583.6 us | 0.08 us |
| 5000 chunks    | 2.0 MB  | 5.8 ms   | 0.15 us |

The lazy form log.info('query_doc:result %s %s', result.ids, result.metadatas) hands the payload to record.getMessage(), which the InterceptHandler only reaches once a record has passed the level check. Output at INFO is byte-identical. Two sites that already built their message eagerly, one str concat and one % operator, move to the same lazy form.
2026-08-02 15:39:10 -05:00
Classic298
52cfb02c72
perf: build debug log messages lazily so disabled debug logs cost nothing (#27834)
GLOBAL_LOG_LEVEL defaults to INFO, so every log.debug(...) in the backend is discarded, but the message is built first: 187 call sites interpolate their payload into an f-string before the logging call runs, so the work happens on every request and the result is thrown away. The worst one sits in process_chat_payload and stringifies the whole request body, full conversation history included, once per chat completion.

That one line with DEBUG disabled, CPython 3.12:

| conversation | payload | before   | after   |
| ------------ | ------- | -------- | ------- |
| 4 messages   | 1.2 kB  | 3.4 us   | 0.07 us |
| 20 messages  | 17 kB   | 24.8 us  | 0.07 us |
| 60 messages  | 123 kB  | 216.6 us | 0.07 us |

The lazy form log.debug('form_data: %s', form_data) hands the payload to record.getMessage(), which the InterceptHandler only reaches once a record has passed the level check. With DEBUG enabled the emitted lines are byte-identical, f'{x=}' sites included: those map to %r. MistralLoader._debug_log callers get the same treatment, since that wrapper already forwards *args.
2026-07-31 19:09:01 -05:00
Timothy Jaeryang Baek
bb0f898b43 refac 2026-07-31 17:41:14 -04:00
Timothy Jaeryang Baek
429f2df50c refac 2026-07-23 21:29:33 -04:00
Classic298
8becf9443e
fix: only admins may switch the global Automatic1111 checkpoint from image generation (#27244)
The Automatic1111 branch of the image generation route called set_image_model whenever the request carried a model field. On this backend set_image_model is not request-scoped: it persists image_generation.model to the global configuration and posts the new sd_model_checkpoint to the shared server, because Automatic1111 holds a single checkpoint instance-wide. A non-admin holding features.image_generation could therefore change the instance-wide image model and the shared backend checkpoint for every user by sending a model on an ordinary generation request, even though the setting is otherwise managed only through the admin-only image configuration route and the frontend never sends this field.

Gate the switch on an admin caller. A non-admin now generates on the currently configured checkpoint and the model field no longer mutates global state; admins retain per-request model switching here and through the image configuration route. Image editing is unaffected, as it selects its model per request without writing global configuration.
2026-07-23 12:23:24 -04:00
Timothy Jaeryang Baek
517cd8d102 refac 2026-06-29 13:03:14 -05:00
Timothy Jaeryang Baek
2d3035a112 refac 2026-06-29 10:54:53 -05:00
Timothy Jaeryang Baek
39837e0a3a refac 2026-06-29 10:51:52 -05:00
Timothy Jaeryang Baek
0883638027 refac 2026-06-29 05:47:21 -05:00
Timothy Jaeryang Baek
396d9ac181 refac 2026-06-29 05:27:51 -05:00
G30
6ea591491e
perf(images): offload validate_url() DNS resolution with asyncio.to_thread (#25825)
validate_url() calls socket.getaddrinfo() for SSRF protection, which
blocks the event loop for 100-700ms per DNS lookup. This affects:

- Image generation (get_image_data) — every external image URL
- Image editing (load_url_image) — every external image URL
- OAuth profile pictures (_process_picture_url) — every login
- Webhook delivery (post_webhook) — every notification
- Image base64 conversion (get_image_base64_from_url) — chat images

Wrap all 5 async call sites in asyncio.to_thread() so DNS resolution
runs in the thread pool. The event loop remains free to serve other
requests during the lookup.

Benchmark (3 domains, 3 trials averaged):
- BEFORE: max jitter 479ms, 1 blocked ping per trial
- AFTER:  max jitter 1ms, 0 blocked pings (324x improvement)

Co-authored-by: Tim Baek <tim@openwebui.com>
2026-06-29 02:31:24 -05:00
Timothy Jaeryang Baek
e3ba698453 refac 2026-06-25 17:34:41 -04:00
Timothy Jaeryang Baek
b5c43968db refac 2026-06-25 03:31:45 +01:00
Timothy Jaeryang Baek
91762ed807 refac
Some checks are pending
Python CI / Ruff Format (3.11) (push) Waiting to run
Python CI / Ruff Format (3.12) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / copy-to-dockerhub (, main) (push) Blocked by required conditions
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / merge (map[name:cuda suffix:-cuda]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:cuda126 suffix:-cuda126]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:main suffix:]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:ollama suffix:-ollama]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:slim suffix:-slim]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda, cuda) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda126, cuda126) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-ollama, ollama) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-slim, slim) (push) Blocked by required conditions
2026-06-23 00:25:21 +02:00
Classic298
e038bab66d
Authorize POST /api/v1/images/edit (enforce ENABLE_IMAGE_EDIT + image-gen permission) (#26009)
The direct image-edit route was the only image-edit surface with no authorization: it ran
on get_verified_user alone, while POST /generations enforces ENABLE_IMAGE_GENERATION +
features.image_generation and the built-in edit_image tool enforces ENABLE_IMAGE_EDIT +
features.image_generation. A verified non-admin user could therefore reach the configured
image-edit provider (spending IMAGES_EDIT_OPENAI_API_KEY) even when the administrator had
globally disabled image editing (ENABLE_IMAGE_EDIT=False) or denied the user image
generation.

Split the route from the shared impl (mirroring generate_images/image_generations): the new
/edit wrapper enforces ENABLE_IMAGE_EDIT and the per-user image-generation permission, then
delegates to image_edits(). The internal callers (the edit_image tool and the chat
middleware) already gate themselves and call image_edits() directly, so they are unaffected.

Co-authored-by: jagstack <52110932+jagstack@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 03:05:41 +02:00
Timothy Jaeryang Baek
5cdcdbaeec refac 2026-06-17 02:52:35 +02:00
Classic298
de8ea08f5c
Route user-supplied image-URL fetches through an SSRF-safe session (DNS rebinding, CWE-918) (#25960)
The connection-layer DNS-rebinding guard (_SSRFSafeResolver / _SSRFSafeAdapter, PR #24759) was
mounted only on SafeWebBaseLoader. Two user-reachable image fetches validate the URL then fetch
it through the shared get_session() pool with the default resolver, so a TTL-0 rebinding answer
that passed validate_url reaches an internal address at connect:

- get_image_base64_from_url (utils/files.py): user image_url on every chat completion.
- load_url_image (routers/images.py, POST /api/v1/images/edit): user-supplied image field.

Add get_ssrf_safe_session() (a one-off aiohttp session mounting _SSRFSafeResolver) and use it
for both fetches, so the connect-time IP is re-validated and a rebound loopback / RFC1918 /
metadata address is rejected. The shared pool is left untouched for the admin-configured
image-generation callers, which legitimately reach internal hosts.

Co-authored-by: dhyabi2 <32069256+dhyabi2@users.noreply.github.com>
Co-authored-by: geo-chen <2404584+geo-chen@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 23:36:53 +02:00
Timothy Jaeryang Baek
6fce92aa12 chore: format 2026-06-01 13:56:55 -07:00
HW
bddadafa07
fix(images): pass content_type=None to r.json() to accept non-standard MIME types (#24838)
aiohttp's ClientResponse.json() validates the Content-Type header against
'application/json' by default and raises ContentTypeError for any other
value — including 'application/x-ndjson', which Ollama returns for its
OpenAI-compatible /v1/images/generations endpoint.

Pass content_type=None to skip this check while keeping all other parsing
behaviour unchanged.  The fix covers image generation (openai, gemini,
automatic1111 engines) and image editing (openai, gemini engines).
2026-06-01 10:33:24 -07:00
Timothy Jaeryang Baek
8aa2a42dc7 refac 2026-05-14 02:40:20 +09:00
Timothy Jaeryang Baek
7dcd932ad7 refac 2026-05-14 02:34:19 +09:00
Timothy Jaeryang Baek
6d0295588e refac: modernize type annotations (PEP 604 / PEP 585) 2026-05-12 17:10:15 +09:00
Classic298
2d9939ed49
chore: add validate_url() to get_image_data() for cohort consistency hardening (#24518)
* chore: add validate_url() to get_image_data() for cohort consistency hardening

`get_image_data()` in `backend/open_webui/routers/images.py` fetches the
URL returned by the configured image generation API directly via
`session.get(data)` without first calling `validate_url()`. The sibling
`load_url_image()` in the same file (called from /images/edit) calls
`validate_url(data)` first — that gate was added under
GHSA-jgx9-jr5x-mvpv. The two functions handle structurally identical
input (an attacker-or-server-supplied URL string) and should enforce the
same SSRF gate as a matter of code hygiene.

In the current call graph, the URL passed to `get_image_data()` comes
from the admin-configured image generation API's response, so an
exploitable SSRF chain additionally requires admin-side trust delegation
(misconfigured/untrusted upstream image API, or a custom
OpenAI-compatible server that reflects user input into response URLs).
That makes the missing call a defense-in-depth gap rather than a
vulnerability per SECURITY.md Rule 9 — a position the GHSA-h7cc-wwjp-5xqh
advisory is being closed under. This change is hardening: it brings the
two image-fetch helpers into alignment so any future caller that begins
passing user-influenced URLs into `get_image_data()` is gated by the
same private-IP / loopback / metadata-IP filter the rest of the
codebase enforces.

Surface raised by brodmart in GHSA-h7cc-wwjp-5xqh.

Co-authored-by: brodmart <brodmart@users.noreply.github.com>

* chore: trim comment

---------

Co-authored-by: brodmart <brodmart@users.noreply.github.com>
2026-05-11 00:59:32 +09:00
Timothy Jaeryang Baek
2e71b3fbb8 chore: format 2026-05-09 21:07:08 +09:00
Timothy Jaeryang Baek
df42d96c95 refac 2026-05-09 21:05:49 +09:00
Classic298
8854541508
fix: prevent redirect-based SSRF in web-fetch and image-load call sites (#24491)
validate_url() in retrieval/web/utils.py only validates the initial URL.
The HTTP clients used downstream (sync requests, sync requests via the
parent WebBaseLoader._scrape, aiohttp via load_url_image) followed 3xx
redirects by default and did not re-validate the redirect target against
the private-IP / metadata-IP block list. An authenticated user could
submit a public URL that 302-redirected to an internal address (RFC1918,
127.0.0.1, 169.254.169.254, etc.) and the redirected response was returned
to them, enabling SSRF reads of internal services and cloud metadata.

Three call sites needed allow_redirects=False to match the policy already
enforced on the async _fetch() path:

- SafeWebBaseLoader: override requests_kwargs in __init__ so that the
  inherited synchronous _scrape() path passes allow_redirects=False to
  self.session.get() (the parent WebBaseLoader uses requests' default
  allow_redirects=True).
- get_content_from_url (retrieval/utils.py): pass allow_redirects=False
  on the streamed requests.get(...) call.
- load_url_image (routers/images.py, image-edits endpoint): pass
  allow_redirects=False on the aiohttp session.get(...) call.

Reports consolidated under GHSA-rh5x-h6pp-cjj6:
- GHSA-rh5x-h6pp-cjj6 (tenbbughunters / Tenable) - sync _scrape
- GHSA-5vxg-6gmv-m2qr (YLChen-007) - load_url_image
- GHSA-hf76-c83f-63w2 (tempcollab) - aiohttp _fetch (already fixed)
- GHSA-h55f-h5fh-mvm4 (sneaXOR) - get_content_from_url
2026-05-09 21:01:45 +09:00
Timothy Jaeryang Baek
f1ef09ddc8 refac 2026-04-17 11:48:17 +09:00
Timothy Jaeryang Baek
128cf41fce refac 2026-04-17 11:12:42 +09:00
Timothy Jaeryang Baek
5dae600ce7 chore: format 2026-04-14 17:27:31 -05:00
Timothy Jaeryang Baek
869cf9e848 refac 2026-04-13 14:33:23 -05:00
Timothy Jaeryang Baek
2ddcb30b9a refac 2026-04-13 14:29:27 -05:00
Timothy Jaeryang Baek
de27a12151 refac 2026-04-12 14:39:23 -05:00
Timothy Jaeryang Baek
27169124f2 refac: async db 2026-04-12 14:22:11 -05:00
Skyzi000
09dccdd42f
fix: use unique client_id per ComfyUI WebSocket connection (#23592)
Using user.id as client_id causes WebSocket deadlocks when the same
user generates images concurrently (e.g., multi-model chat). ComfyUI
routes messages by clientId, so shared IDs mean only one connection
receives the completion — others hang forever.

Generate a unique UUID per request, matching ComfyUI's own examples.
2026-04-11 14:55:04 -06:00
Timothy Jaeryang Baek
ade617efa8 refac 2026-03-24 04:49:48 -05:00
Timothy Jaeryang Baek
de3317e26b refac 2026-03-17 17:58:01 -05:00
Timothy Jaeryang Baek
f6bd54fb1f refac 2026-02-22 18:55:24 -06:00
Timothy Jaeryang Baek
ef036529b5 chore: format 2026-02-17 01:11:56 -06:00
Timothy Jaeryang Baek
9be45f49e4 refac 2026-02-16 14:13:49 -06:00
Timothy Jaeryang Baek
f376d4f378 chore: format 2026-02-11 16:24:11 -06:00
Timothy Jaeryang Baek
ecf3fa2feb refac 2026-02-03 23:36:15 -06:00
Timothy Jaeryang Baek
ce50d9bac4 refac 2026-01-28 01:14:22 +04:00
7. Sun
33020d826f
perf: parallelize image loading in image_edits endpoint (#20911)
Use asyncio.gather() to load multiple images concurrently instead of
sequentially, significantly reducing latency for multi-image edit
operations.
2026-01-28 00:35:25 +04:00
G30
4b4743b497
feat: enforce permissions in backend (#20471)
* feat: enforce image generation permissions in backend

* feat: enforce web search permissions in backend

* feat: enforce audio (tts/stt) permissions in backend
2026-01-08 02:48:35 +04:00
Timothy Jaeryang Baek
927a765641 refac 2026-01-06 03:24:08 +04:00
G30
bf42535d12
feat: add support for passing 'steps' in image generation requests (#20337) 2026-01-03 18:11:13 +04:00
Timothy Jaeryang Baek
5d1459df16 refac 2025-12-29 01:20:04 +04:00
Timothy Jaeryang Baek
0a78323406 fix/refac: image action button 2025-12-22 09:56:44 +04:00