Repository navigation
fix(vector_stores): only list vector stores the caller was granted - #39612
yassin-berriai merged 2 commits into
Conversation
/vector_store/list returned every managed vector store with no team_id to any key, and let a dashboard session see stores created from the dashboard because every session shares the litellm-dashboard team id. Non-admin listings now show a store only when the key or one of the caller's real teams is allowlisted for it via object_permission.vector_stores, or the team owns it Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
Greptile SummaryThis PR adds security hardening to vector-store listings while preserving administrator access
Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/proxy/vector_store_endpoints/management_endpoints.py | The listing endpoint now delegates non-administrator resource filtering to the shared listing authorization helper before redaction and pagination |
| litellm/proxy/vector_store_endpoints/utils.py | The authorization helper preserves session-key grants for dashboard users with no real teams while separately evaluating each resolved team |
| tests/test_litellm/proxy/vector_store_endpoints/test_vector_store_access_control.py | Regression cases cover key grants, team grants, ownership, administrator access, and zero-team dashboard session grants |
Reviews (2): Last reviewed commit: "fix(vector_stores): keep a dashboard ses..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…e user has no teams Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@greptileai please re-review at dfb6990, the zero team dashboard session now keeps its session key grants |
TLDR
Problem this solves:
GET /vector_store/listshows every store with no team to any keyobject_permission.vector_storesgrants were never consulted for those storesHow it solves it:
object_permission.vector_storesentry, or team ownershipUser Flow
Before: a developer holding a plain key with no vector store grant can enumerate vector stores the admin never shared with them
{"vector_store_id":"vs-unscoped","custom_llm_provider":"openai"}, and does not grant it to any team or keyobject_permissiondatacontainsvs-unscopedvs-unscopedplus every store any admin created from the dashboardAfter: the same key and the same user see only what they were granted
datais[]{"key":"<key>","object_permission":{"vector_stores":["vs-unscoped"]}}, or to the developer's team with POST http://localhost:4000/team/updatevs-unscopedRelevant issues
Linear ticket
Resolves LIT-6863
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Root cause: the shared access check returns "allowed" for any store with no
team_idbefore it looks at key or teamobject_permission.vector_stores, and a dashboard session carries the sharedlitellm-dashboardteam id, which is also theteam_idstamped on every store created from the dashboard, so both fall through as "the caller's team owns it"Shared setup, run once against a proxy on
localhost:4000with Postgres andmaster_key: sk-1234. The same rows and keys are reused for both arms, nothing is recreated between themRows in
LiteLLM_ManagedVectorStoresTableafter setupEvery case below is
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer <credential>"and the output is the sorted list ofvector_store_idindata, except the last two which hitGET /v1/mcp/serverand listserver_idBefore (4b1e24e)
admin (sk-1234): control, sees everything
curl -s http://localhost:4000/vector_store/list -H 'Authorization: Bearer sk-1234'['vs-team-owned', 'vs-ui-admin-created', 'vs-unscoped']key: no team, no object_permission
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_NOTEAM"['vs-unscoped']key: team-no-perms (team owns vs-team-owned, no object_permission)
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_TEAM_NOPERMS"['vs-team-owned', 'vs-unscoped']key: team-granted (team object_permission.vector_stores=[vs-unscoped])
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_TEAM_GRANTED"['vs-unscoped']key: key-level object_permission.vector_stores=[vs-unscoped]
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_PERM_GRANTED"['vs-unscoped']dashboard session: alice (internal_user, member of team-no-perms, no object_permission anywhere)
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $ALICE_SESSION"['vs-ui-admin-created', 'vs-unscoped']MCP comparison, key: no team, no object_permission
curl -s http://localhost:4000/v1/mcp/server -H "Authorization: Bearer $KEY_NOTEAM"[]MCP comparison, dashboard session: alice
curl -s http://localhost:4000/v1/mcp/server -H "Authorization: Bearer $ALICE_SESSION"[]After (dfb6990)
admin (sk-1234): control, sees everything
curl -s http://localhost:4000/vector_store/list -H 'Authorization: Bearer sk-1234'['vs-team-owned', 'vs-ui-admin-created', 'vs-unscoped']key: no team, no object_permission
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_NOTEAM"[]key: team-no-perms (team owns vs-team-owned, no object_permission)
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_TEAM_NOPERMS"['vs-team-owned']key: team-granted (team object_permission.vector_stores=[vs-unscoped])
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_TEAM_GRANTED"['vs-unscoped']key: key-level object_permission.vector_stores=[vs-unscoped]
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $KEY_PERM_GRANTED"['vs-unscoped']dashboard session: alice (internal_user, member of team-no-perms, no object_permission anywhere)
curl -s http://localhost:4000/vector_store/list -H "Authorization: Bearer $ALICE_SESSION"['vs-team-owned']MCP comparison, key: no team, no object_permission
curl -s http://localhost:4000/v1/mcp/server -H "Authorization: Bearer $KEY_NOTEAM"[]MCP comparison, dashboard session: alice
curl -s http://localhost:4000/v1/mcp/server -H "Authorization: Bearer $ALICE_SESSION"[]Served build check for the After arm: the proxy process was started with
PYTHONPATH=/home/ubuntu/repos/litellmat 19:43:57 UTC, afterutils.pyin that tree was last written at 19:41:05 UTC, and that file containsfilter_listable_vector_storesonce. The Before arm ran against the same tree at4b1e24eae9, where that symbol does not existType
🐛 Bug Fix
Caveats (if any)
Severe
object_permission.vector_storeson the key or teamMedium
/vector_store/list(and/v1/vector_store/list) changesLow
object_permissionareFinal Attestation
Link to Devin session: https://app.devin.ai/sessions/7563ac587ff34765bacc7885ecade1ab
Open in Devin Desktop: https://app.devin.ai/desktop/session/7563ac587ff34765bacc7885ecade1ab?variant=devin
Requested by: @yassin-berriai