ulysses/routes
Mahdi Salmanzade 271489a10c
fix(research): owner-scope endpoint resolution
POST /api/research/start (require_privilege "can_use_research" — a normal
user, not admin) resolves an endpoint two ways and feeds the row's *decrypted*
api_key + base_url into research_handler.start_research(llm_endpoint=,
llm_headers=):

  1. body.endpoint_id  -> query(ModelEndpoint).filter(id == endpoint_id,
                          is_enabled == True).first()
  2. no endpoint + nothing configured -> query(ModelEndpoint).filter(
                          is_enabled == True).first()

Neither was owner-scoped. ModelEndpoint is a per-user resource (core/database.py:
non-null owner = private, "the model picker only shows the endpoint to that
user"). So a research-privileged user (or a chat-scoped token) could pass another
user's PRIVATE endpoint_id — or fall through to their first-enabled row — and run
research against that owner's endpoint: spending their API key / quota and
reaching whatever internal base_url they configured (SSRF).

This is the same multi-tenant owner-scoping class already fixed for
companion/models, the /api/v1/chat session gate (#870), and the /api/v1/chat
first-enabled fallback (#1045, _first_enabled_endpoint). These two sinks on the
research path were missed.

Extract `_owned_enabled_endpoint(db, owner, endpoint_id=None)` which scopes via
the shared owner_filter helper (own rows + legacy null-owner shared rows),
matching webhook_routes._first_enabled_endpoint and session_routes._owned_endpoint.
Used for both sinks. A scoped miss on the explicit-id path returns the existing
404 ("Endpoint not found or disabled"), so endpoint existence isn't revealed. A
null/empty owner stays a no-op (single-user / legacy mode).

Add regression tests pinning both lookups (cross-owner rejected, own-row
allowed, legacy shared-row allowed, disabled-skipped, fallback never borrows,
null-owner no-op).
2026-06-03 23:19:28 +01:00
..
__init__.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
admin_wipe_routes.py Admin: wipe gallery albums with images 2026-06-02 20:35:57 +09:00
api_token_routes.py Revert "Codex Agent integration: HTTP surface + plugin bundle + Settings UI" 2026-06-03 22:47:00 +09:00
assistant_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
auth_routes.py fix(auth): case-insensitive owner migration on username rename (#1183) 2026-06-02 23:18:15 +09:00
backup_routes.py fix: backup import drops a user's memory when its text matches another user's (#1743) 2026-06-03 13:29:14 +09:00
calendar_routes.py fix(calendar): avoid double-encrypting CalDAV password 2026-06-03 22:59:40 +01:00
chat_helpers.py fix: auto-naming for 24h time format (#1374) 2026-06-03 14:14:34 +09:00
chat_routes.py fix: rewriting a message is lost on reload due to a non-existent DB column (#1729) 2026-06-03 13:31:19 +09:00
cleanup_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
compare_routes.py fix(compare): owner-scope endpoint key lookup 2026-06-03 23:17:12 +01:00
contacts_routes.py fix(contacts): parse Apple/iCloud item-grouped vCard EMAIL/TEL properties (#1438) 2026-06-03 14:24:04 +09:00
cookbook_helpers.py Cookbook: scoring fixes, UI polish, false-finished + stale-state bug fixes 2026-06-03 16:32:20 +09:00
cookbook_routes.py Merge remote-tracking branch 'origin/main' into visual-pr-playground 2026-06-03 16:49:10 +09:00
diagnostics_routes.py Fix email-thread HTML injection, attachment path traversal, and missing authz (#475) 2026-06-01 22:20:17 +09:00
document_helpers.py fix: _resolve_user_upload_path crashes on a non-dict resolve_upload result (#1715) 2026-06-03 13:34:33 +09:00
document_routes.py fix: document library language facet undercounts text documents (#1758) 2026-06-03 13:28:38 +09:00
editor_draft_routes.py Ignore invalid editor draft payloads (#1533) 2026-06-03 14:07:03 +09:00
email_helpers.py fix(email): guard _decode_header against unknown MIME charset (#1354) 2026-06-03 14:24:20 +09:00
email_pollers.py Reconnect after a failed SEARCH ALL so the email poller doesn't desync IMAP (#1613) (#1748) 2026-06-03 13:28:53 +09:00
email_routes.py fix: SMTP envelope recipients split on commas inside display names (#1464) 2026-06-03 14:23:58 +09:00
embedding_routes.py Ignore non-object embedding endpoint config (#1260) 2026-06-03 14:12:41 +09:00
emoji_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
font_routes.py Keep compact font family names together (#1263) 2026-06-03 14:24:30 +09:00
gallery_helpers.py fix: gallery records raw instead of display dimensions for EXIF-rotated photos (#1667) 2026-06-03 14:23:04 +09:00
gallery_routes.py fix: validate client-supplied image _endpoint to prevent SSRF (gallery proxies) (#1718) 2026-06-03 13:34:17 +09:00
history_routes.py fix: history DB fallback returned hidden (compaction) messages to the client (#1726) 2026-06-03 13:30:11 +09:00
hwfit_routes.py Merge remote-tracking branch 'origin/main' into visual-pr-playground 2026-06-03 16:49:10 +09:00
mcp_routes.py Fix email-thread HTML injection, attachment path traversal, and missing authz (#475) 2026-06-01 22:20:17 +09:00
memory_routes.py fix(memory): owner-scope memory route session access 2026-06-03 23:13:56 +01:00
model_routes.py Reapply "Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus" 2026-06-03 22:47:00 +09:00
note_routes.py fix: fire-reminder endpoint crashes with NameError on _gcu (#1250) 2026-06-03 01:02:25 +09:00
personal_routes.py fix: personal-docs path confinement used abspath, allowing symlink escape (#1728) 2026-06-03 13:29:57 +09:00
prefs_routes.py Ignore non-object prefs JSON (#1257) 2026-06-03 14:12:45 +09:00
preset_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
research_routes.py fix(research): owner-scope endpoint resolution 2026-06-03 23:19:28 +01:00
search_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
session_routes.py fix: archive browser model filter is suffix-only and drops matching models (#1709) 2026-06-03 13:34:54 +09:00
shell_routes.py Add a 'Rebuild llama.cpp' Cookbook action to force a fresh GPU build (#1787) 2026-06-03 13:28:19 +09:00
signature_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
skills_routes.py fix: skill test-task / precision helpers crash on a non-dict skill (#1638) 2026-06-03 08:59:24 +09:00
stt_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
task_routes.py Polish email tasks and window controls 2026-06-01 20:56:46 +09:00
tts_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
upload_routes.py fix: use safe .get for id lookup in uploads.json to prevent KeyError (#1465) 2026-06-03 14:12:00 +09:00
vault_routes.py Keep Bitwarden unlock password off argv (#1311) 2026-06-03 02:13:51 +09:00
webhook_routes.py Reapply "Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus" 2026-06-03 22:47:00 +09:00