The lookup-prompt-by-command endpoint's only frontend wrapper,
getPromptByCommand, is dead: nothing imports or calls it, the path is
referenced nowhere else, and the route handler has no internal caller
(slash-command resolution happens client-side from the loaded prompt
list). Removes the route handler, its section header, and the dead
wrapper.
The shared Prompts.get_prompt_by_command data-layer method is kept: it
is still used by create/update prompt validation (prompts.py:175, 275,
340). PromptAccessResponse and AccessGrants are untouched.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_process_picture_url validated the initial picture URL with validate_url()
but then aiohttp followed 3xx redirects without re-validating the target,
so a validate_url-passing public URL could 302 to an internal address and
the body was base64-stored in the user's profile_image_url. This is the
sixth call site of the CVE-2026-45401 redirect-bypass cohort; the other
five already pass allow_redirects=AIOHTTP_CLIENT_ALLOW_REDIRECTS. Apply
the same.
validate_url() resolves DNS to check IPs but discards the result; the
HTTP client resolves again independently. Between those two lookups an
attacker can swap the DNS record from a public IP to an internal one
(DNS rebinding).
Push the IP-is-global check into the actual connection layer so the
validated resolution is the one used for the TCP connect:
- aiohttp (_fetch): _SSRFSafeResolver wraps DefaultResolver and rejects
non-global IPs at resolve time (zero TOCTOU window).
- requests (_scrape): _SSRFSafeAdapter mounts custom urllib3 connection
classes whose _new_conn resolves, validates, and connects to the
validated IP in one shot (zero TOCTOU window).
Both paths respect ENABLE_RAG_LOCAL_WEB_FETCH (skip validation when on).
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
has_access_to_file granted access whenever the file was attached to a
shared chat the user could read, ignoring the requested access_type. A
read-only shared-chat recipient therefore satisfied write and delete
checks and could delete or mutate the chat owner's attached file. Gate
the shared-chat branch on read access, matching the channels branch
directly above it.
Co-authored-by: oxsignal <oxsignal@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SafePlaywrightURLLoader validated only the initially submitted URL and
then let the browser follow HTTP redirects and client-side navigations
without re-checking them, so a public URL could redirect into the
internal network (cloud metadata, RFC1918, loopback). Intercept
document-type requests, re-run validate_url on each, and apply the same
redirect policy as the requests loader (blocked unless
AIOHTTP_CLIENT_ALLOW_REDIRECTS). Sub-resource requests pass through
unchanged so page rendering performance is unaffected.
Co-authored-by: POV9en <POV9en@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
generateFollowUps in src/lib/apis/index.ts is dead: it appears only at
its own definition, nothing imports or calls it, and it targets a
non-existent path (/api/v1/tasks/follow_ups/completions, plural) while
the real route is /tasks/follow_up/completions (singular). Follow-up
suggestions are generated server-side in the chat-completion middleware
and delivered over the chat:message:follow_ups websocket event, so this
wrapper was never on the live path.
Removes only the dead wrapper. The backend POST /tasks/follow_up/completions
endpoint is intentionally kept: it is a member of the actively-used
/tasks/*/completions family (title, tags, emoji, queries, moa) and its
handler delegates to the core generate_follow_ups function.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
update_event only verified write access on the event's source calendar.
CalendarEventUpdateForm accepts a new calendar_id which the model layer
applies unconditionally, so a user with write access to their own calendar
could move (inject) an event into any other user's calendar. Mirror the
destination check create_event already performs.
The __main__ block called search_bing() with 4 positional arguments, but
the function requires 5 (subscription_key, endpoint, locale, query,
count). Running `python -m open_webui.retrieval.web.bing` raised a
TypeError and, before failing, silently misrouted every argument. Read
the key/endpoint from environment variables, matching config.py defaults.
Closes#24765
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bulk-clear-chat-tags endpoint's only frontend wrapper,
deleteTagsById in src/lib/apis/chats/index.ts, is dead: nothing imports
or calls it, the path is referenced nowhere else, and the route handler
has no internal caller. Removes the route handler, the dead wrapper, and
the now-orphaned Chats.delete_all_tags_by_id_and_user_id model method
(its sole caller was this route). The shared
Chats.delete_orphan_tags_for_user method is untouched.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POST /utils/markdown rendered a markdown string to HTML server-side. Its
only frontend wrapper, getHTMLFromMarkdown in src/lib/apis/utils/index.ts,
is dead: nothing imports or calls it, the route is hit by no other code
path, and the path string appears nowhere else in the repo (no direct
fetch, no test, no docs). Markdown is rendered client-side in the UI, so
this endpoint was redundant.
Fully self-contained removal: the endpoint, its MarkdownForm model, the
now-orphaned 'import markdown' in the utils router (used only here), and
the dead getHTMLFromMarkdown wrapper. Nothing else depends on any of them.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GET /evaluations/feedbacks/all returned the entire feedback table in a
single response (flagged as a Medium OOM risk for admins in
open-webui#22206). Its only frontend wrapper, getAllFeedbacks in
src/lib/apis/evaluations/index.ts, is dead: nothing imports or calls it
anywhere in the codebase. The endpoint is a redundant view-only twin of
GET /evaluations/feedbacks/all/export, which is what the admin Feedbacks
UI actually uses.
Removes the endpoint, the now-unused FeedbackResponse import in the
evaluations router, and the dead getAllFeedbacks frontend wrapper. The
shared Feedbacks.get_all_feedbacks data-layer method is kept, since the
live /feedbacks/all/export endpoint still uses it.
Ref: open-webui#22206
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These 19 exported wrappers are dead: each appears exactly once in the
codebase (its own definition), nothing imports or calls any of them, and
none has a corresponding backend route. They are leftovers from settings
that were consolidated server-side into /auths/admin/config,
/openai/config, /ollama/config and /api/config:
- index.ts: getModelFilterConfig, updateModelFilterConfig,
getCommunitySharingEnabledStatus, toggleCommunitySharingEnabledStatus,
getModelConfig, updateModelConfig (+ orphaned GlobalModelConfig type)
- auths: getSignUpEnabledStatus, toggleSignUpEnabledStatus,
getDefaultUserRole, updateDefaultUserRole, getJWTExpiresDuration,
updateJWTExpiresDuration
- openai: getOpenAIUrls, updateOpenAIUrls, getOpenAIKeys, updateOpenAIKeys
- ollama: updateOllamaUrls
- prompts: restorePromptFromHistory
- folders: updateFolderItemsById (+ orphaned FolderItems type)
Shared types (ModelConfig/ModelMeta/ModelParams) and all live wrappers
are untouched. Removal is import-safe: nothing referenced these.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reported by bwgabrielsusai on #24719: granting a user only
`workspace.skills` doesn't show the Workspace menu, and visiting
`/workspace` directly bounces them to `/`.
The per-route guard in `/workspace/+layout.svelte` already covered
skills, but two earlier gates in the chain didn't:
* `Sidebar.svelte` case 'workspace' OR'd models/knowledge/prompts/tools
to decide menu visibility — skills was missing, so the entry never
rendered for skills-only users.
* `/workspace/+page.svelte` redirect chain picked the first available
section — skills was missing, so the fallback `goto('/')` fired.
Adding skills to both.