You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security(api): chat-knowledge routes authenticate the caller and never check they own the chat — 11 routes, 0 ownership checks #17722
api/chat_knowledge.py's eleven routes are all scoped to a {chat_id} or {session_id} taken from the URL, and not one of them checks that the caller owns it. Measured: grep -cE 'owns_|current_user|user_id ==|_owner' over the module returns 0.
POST /context/create GET /knowledge/pending/{chat_id}
POST /files/associate GET /context/{chat_id}
POST /files/upload/{chat_id} GET /chat/sessions/{session_id}/facts
POST /knowledge/add_temporary POST /chat/sessions/{session_id}/facts/preserve
POST /knowledge/decide
POST /compile
POST /search
So any caller who reaches the route can read another user's chat facts, associate files with another user's chat, upload into it, or preserve/decide its pending knowledge.
#16375 is the authentication gate — and gating this router (that work is in flight) takes it from anonymous to any authenticated user. That is a real improvement and it is not this fix. Authentication answers "is there a caller"; this issue is "is it their chat".
Worth stating plainly because the gating PR could otherwise be read as closing it: after that PR, an authenticated user can still address any chat_id they can name. The hole narrows from the internet to the user table.
The shape, which is a known one here
This is the same seam as #17014's remaining gap — an authenticated route that never calls the ownership check, invisible to a green suite — and as #17054's desktop socket, where enforce_ws_authentication asked only whether the caller was somebody. The pattern: a route takes a resource id from the URL, authenticates the caller, and never joins the two.
api/ws_security.py already has the vocabulary for the join — owns_chat_session(websocket, session_id, user) — so the concept exists in this codebase for the WebSocket surface and not for these REST routes.
Acceptance criteria
Every route taking a {chat_id} or {session_id} verifies the authenticated caller owns it, refusing with 404 rather than 403 where disclosure matters (an id the caller may not see should not be confirmed to exist)
The check is one helper, not eleven inline copies — owns_chat_session's REST equivalent, so the two surfaces cannot drift
The {session_id} facts routes are checked against the same owner store the chat routes use, or the difference is recorded
Not claimed
I have not established whether an ownership store exists for chat_id in a form these routes can query, nor whether session_id and chat_id resolve to the same owner. Both need answering before the helper can be written, and neither is answerable from the route bodies.
Still open, and the commit that names this issue did not do this work — flagging it because it reads as if it did.
git log --grep="#17722" returns one commit:
security(api): gate the chat-knowledge router, which was reachable anonymously (#16375) (#17724)
That is #16375's work: authentication. Its body says so — "Eleven routes, eight of them mutating, and no credential required for any… the first tranche of #16375." This issue names the next problem: those eleven routes now authenticate and still never check the caller owns the chat.
Authentication landed; ownership did not. A search of api/chat_knowledge.py for an ownership check finds only the module docstring describing its absence (:11, :20), which is the gap documented rather than closed.
So all four criteria stand, including AC2's requirement that the check be one helper rather than eleven inline copies, and AC3's cross-user negative control parametrised over every route.
Recorded so the naming commit does not read as delivery. The two issues are adjacent and sequential — can anyone call this? then can this caller call it for that chat? — and only the first is done.
Activity
security(api): gate the chat-knowledge router, which was reachable an…
mrveiss commentedon Oct 3, 2026
Still open, and the commit that names this issue did not do this work — flagging it because it reads as if it did.
git log --grep="#17722"returns one commit:That is #16375's work: authentication. Its body says so — "Eleven routes, eight of them mutating, and no credential required for any… the first tranche of #16375." This issue names the next problem: those eleven routes now authenticate and still never check the caller owns the chat.
Authentication landed; ownership did not. A search of
api/chat_knowledge.pyfor an ownership check finds only the module docstring describing its absence (:11,:20), which is the gap documented rather than closed.So all four criteria stand, including AC2's requirement that the check be one helper rather than eleven inline copies, and AC3's cross-user negative control parametrised over every route.
Recorded so the naming commit does not read as delivery. The two issues are adjacent and sequential — can anyone call this? then can this caller call it for that chat? — and only the first is done.