Skip to content

[Security] Sessions not isolated by user — any authenticated user can read all other users' sessions #596

Description

@remedywilliams

Summary

Multi-user session isolation is broken. Every authenticated user can enumerate and access all other users' chat sessions via the API and WebSocket endpoints.

Affected versions

v1.4.3 through v1.4.12 (the "real multi-user isolation" release and all subsequent versions).

Steps to reproduce

  1. Start DeepTutor with auth enabled (SQLite or PocketBase backend).
  2. Create two user accounts (e.g. admin and user2).
  3. Log in as admin, create a chat session with some messages.
  4. Log in as user2, call GET /api/v1/sessions or open the home page.
  5. Observe that admin's sessions appear in user2's session list.
  6. Any session ID from the list can be used to GET /api/v1/sessions/{id} and read full conversation history.

Expected behavior

Each user should only see their own sessions. list_sessions should return only sessions belonging to the authenticated user, and get_session/{id} should return 404 for sessions owned by another user.

Actual behavior

All session queries return every session in the database regardless of the authenticated user.

Root cause

SQLite backend (deeptutor/services/session/sqlite_store.py):

  • The sessions table has no user_id column (schema at lines 100-108).
  • list_sessions (line 1110), get_session (line 422), and create_session (line 385) never filter by or store user_id.

PocketBase backend (deeptutor/services/session/pocketbase_store.py):

  • The user_id field exists in the schema (scripts/pb_setup.py line 95) but is required: false, never populated on create, and never used in filters.
  • list_sessions (line 186), get_session (line 95) have no user_id filter.
  • All PocketBase collection RBAC rules (listRule, viewRule, etc.) are empty strings (lines 103-107), providing no access control.

API layer (deeptutor/api/routers/sessions.py):

  • Endpoints authenticate the user and set the ContextVar, but never pass user_id to the session store.

The v1.4.3 commit (452de667) added per-user filesystem isolation (workspaces, grants, sandbox scoping) but did not add data-level isolation for session records.

What needs to change

  1. Add user_id column to the SQLite sessions table (with migration for existing DBs).
  2. Populate user_id in create_session() using the current user from get_current_user() / ContextVar.
  3. Filter all session queries by user_id:
    • list_sessions: WHERE user_id = ?
    • get_session, get_session_with_messages: WHERE id = ? AND user_id = ?
    • update_session_title, delete_session, etc.: same
  4. Update SessionStoreProtocol to accept user_id in relevant methods.
  5. Set PocketBase RBAC rules: listRule/viewRule = user_id = @request.auth.id.

Severity

High — any authenticated user can read all other users' conversation history, including any sensitive content they may have shared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions