Stove-pipe RAG retrieval: prevent cross-tenant document leakage #40

Closed
opened 2026-08-01 12:28:02 -07:00 by westfarn · 0 comments
Owner

Summary

Multi-tenant RAG isolation is only partial. Chat websocket/retrieval can cross company document boundaries. Align with the abc_worker stove-pipe pattern (b13cec8: immutable tenant scope per connection + conversation ownership validation).

Problem

  1. Critical — WebSocket conversation IDOR
    Client-supplied conversation_id / email drive identity. get_workspace(conversation_id) loads that conversation's company workspace with no check that the authenticated user owns the conversation. Victim company chunks can enter the LLM context.

  2. Critical — Fail-open vector search
    search_documents(..., workspace=None) sets Chroma filter: None and searches the entire shared collection.

  3. High — Soft isolation only
    One shared Chroma collection; optional workspace_id metadata filter. Deletes/signals do not purge vectors by document_id.

  4. Medium — ConversationDetailView IDOR
    GET prompts by conversation_id without user=request.user.

Acceptance criteria

  • Bind websocket turns to authenticated session/JWT user (not spoofable email alone).
  • Require conversation.user_id == authenticated_user.id before get_messages / get_workspace / RAG.
  • Introduce immutable per-connection company/workspace scope (ChatCompanyScope / equivalent).
  • Fail closed: never call Chroma with filter=None; require workspace_id (prefer also company_id in metadata).
  • Fix ConversationDetailView ownership check.
  • Guard/remove unfiltered get_retriever().
  • Adversarial test: company B user + company A conversation_id → deny, no A chunks in retrieval.

References

  • abc_worker: Pass immutable ChatTenantScope per WebSocket (Fixes #8)
  • Audit: CHAT RAG cross-data isolation review (2026-08-01)

Severity

Critical for multi-tenant confidential RAG.

## Summary Multi-tenant RAG isolation is only partial. Chat websocket/retrieval can cross company document boundaries. Align with the abc_worker stove-pipe pattern ([b13cec8](https://git.aimloperations.com/GIS/abc_worker/commit/b13cec88f9730289f245b66b95aa11ee33cb4e8e): immutable tenant scope per connection + conversation ownership validation). ## Problem 1. **Critical — WebSocket conversation IDOR** Client-supplied `conversation_id` / `email` drive identity. `get_workspace(conversation_id)` loads that conversation's company workspace with no check that the authenticated user owns the conversation. Victim company chunks can enter the LLM context. 2. **Critical — Fail-open vector search** `search_documents(..., workspace=None)` sets Chroma `filter: None` and searches the entire shared collection. 3. **High — Soft isolation only** One shared Chroma collection; optional `workspace_id` metadata filter. Deletes/signals do not purge vectors by `document_id`. 4. **Medium — ConversationDetailView IDOR** GET prompts by `conversation_id` without `user=request.user`. ## Acceptance criteria - [ ] Bind websocket turns to authenticated session/JWT user (not spoofable email alone). - [ ] Require `conversation.user_id == authenticated_user.id` before `get_messages` / `get_workspace` / RAG. - [ ] Introduce immutable per-connection company/workspace scope (ChatCompanyScope / equivalent). - [ ] Fail closed: never call Chroma with `filter=None`; require `workspace_id` (prefer also `company_id` in metadata). - [ ] Fix `ConversationDetailView` ownership check. - [ ] Guard/remove unfiltered `get_retriever()`. - [ ] Adversarial test: company B user + company A `conversation_id` → deny, no A chunks in retrieval. ## References - abc_worker: Pass immutable `ChatTenantScope` per WebSocket (Fixes #8) - Audit: CHAT RAG cross-data isolation review (2026-08-01) ## Severity **Critical** for multi-tenant confidential RAG.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ai_ml_operations/chat_backend#40