Server
Clients
OS
Khoj version
Source snapshot e8631261400e0a04c5063e91e498b549976ffc53 from master (git describe --always: e863126). Released version range is unknown from the local checkout.
Describe the bug
Forking a public/shared conversation can create a new conversation for the recipient that still points to the original user's private Agent.
The share flow copies conversation.agent into PublicConversation, and the fork flow copies public_conversation.agent into the recipient's new private Conversation. Later chat execution accepts conversation.agent as the active agent without first checking whether the current user can access that agent.
I re-checked the document retrieval path and did not confirm direct private knowledge-base retrieval through the normal Notes search path: execute_search() has an AgentAdapters.ais_agent_accessible() guard that rejects private agents owned by another user. However, the inherited private agent is still used by chat behavior before/around retrieval, including agent persona/system prompt construction, input/output tool selection, and in some cases agent chat-model selection. That means a public conversation slug/fork can carry a security-sensitive private agent reference across users without reauthorization.
Current Behavior
Relevant local source evidence:
src/khoj/database/adapters/__init__.py:999 creates PublicConversation with agent=conversation.agent.
src/khoj/database/adapters/__init__.py:1544 creates the forked conversation for the recipient with agent=public_conversation.agent.
src/khoj/routers/api_chat.py:397 exposes the authenticated /api/chat/share/fork endpoint for a public conversation slug.
src/khoj/routers/api_chat.py:954 sets the active chat agent from conversation.agent without an accessibility check.
src/khoj/routers/helpers.py:371, :391, and :400 use the inherited agent's input_tools, output_modes, and personality for tool routing.
src/khoj/routers/helpers.py:1827 uses the inherited agent's name/personality to build the final chat system prompt.
src/khoj/database/adapters/__init__.py:1724 can use the inherited agent's chat_model for hidden agents or subscribed users.
Mitigating evidence:
src/khoj/routers/helpers.py:1491 checks AgentAdapters.ais_agent_accessible(agent, user) before normal document search.
src/khoj/database/adapters/__init__.py:760 returns False for a private agent whose creator is not the current user.
So the issue is not confirmed as direct private document retrieval in this snapshot. The confirmed issue is unauthorized retention and use of a private agent reference and its behavior/configuration across user boundaries.
Expected Behavior
Forking a shared conversation should not grant or retain access to private or otherwise inaccessible agents owned by another user.
Safe behavior would be one of:
- Do not copy private/protected agent references into
PublicConversation.
- Do not copy inaccessible agent references when forking a public conversation.
- Before chat execution, re-authorize
conversation.agent against the current user. If inaccessible, replace it with the default public agent or reject the request.
- Apply the same authorization rule anywhere agent persona, tools, output modes, chat model, memory namespace, or retrieval context are derived from
conversation.agent.
Reproduction Steps
Controlled local verification was done against the source snapshot above, using only source inspection and synthetic user/agent assumptions.
- Create user A and user B.
- As user A, create a private agent with a distinctive persona and restricted input/output tool configuration.
- As user A, create a conversation using that private agent.
- As user A, share the conversation through
/api/chat/share.
- As user B, fork that public conversation through
/api/chat/share/fork.
- Observe from
ConversationAdapters.create_conversation_from_public_conversation() that the new user-B-owned conversation can retain agent_id for user A's private agent.
- Continue the forked conversation as user B.
- Observe from
event_generator() and helper functions that the inherited agent is used for persona/system prompt construction and tool/model selection without first checking whether user B can access that private agent.
- If Notes/document retrieval is attempted, the normal
execute_search() path rejects the inaccessible private agent, so direct private knowledge-base result retrieval was not confirmed in this source snapshot.
Static verification checks passed locally:
PASS: public share stores original agent
PASS: forked conversation copies public agent to new user
PASS: share fork endpoint accepts public slug and creates private conversation
PASS: chat execution trusts conversation.agent directly for active agent assignment
PASS: tool routing uses inherited agent input/output modes and personality
PASS: final chat system prompt uses inherited private agent personality
PASS: knowledge-base search has an access-control guard
RESULT: inherited private agent reference is present; direct KB retrieval is guarded by an existing access check
Possible Workaround
Until fixed, avoid sharing conversations that use private or hidden agents. Operators can also disable public conversation sharing/forking or patch the share/fork flow to strip agents that are not public and accessible to the recipient.
Additional Information
Security impact: unauthorized cross-user retention and use of a private agent reference. This can expose or apply private agent configuration such as persona/instructions, tool availability, output modes, and potentially model selection across users. I would treat this as an authorization boundary issue, but with lower severity than direct private document disclosure because the normal document search path has an access-control guard in this snapshot.
Suggested fix:
agent = public_conversation.agent
if agent and not await AgentAdapters.ais_agent_accessible(agent, user):
agent = await AgentAdapters.aget_default_agent()
Apply equivalent checks before using conversation.agent in chat execution, tool routing, memory scoping, chat-model selection, and retrieval.
Link to Discord or Github discussion
No response
Server
Clients
OS
Khoj version
Source snapshot
e8631261400e0a04c5063e91e498b549976ffc53frommaster(git describe --always:e863126). Released version range is unknown from the local checkout.Describe the bug
Forking a public/shared conversation can create a new conversation for the recipient that still points to the original user's private
Agent.The share flow copies
conversation.agentintoPublicConversation, and the fork flow copiespublic_conversation.agentinto the recipient's new privateConversation. Later chat execution acceptsconversation.agentas the active agent without first checking whether the current user can access that agent.I re-checked the document retrieval path and did not confirm direct private knowledge-base retrieval through the normal Notes search path:
execute_search()has anAgentAdapters.ais_agent_accessible()guard that rejects private agents owned by another user. However, the inherited private agent is still used by chat behavior before/around retrieval, including agent persona/system prompt construction, input/output tool selection, and in some cases agent chat-model selection. That means a public conversation slug/fork can carry a security-sensitive private agent reference across users without reauthorization.Current Behavior
Relevant local source evidence:
src/khoj/database/adapters/__init__.py:999createsPublicConversationwithagent=conversation.agent.src/khoj/database/adapters/__init__.py:1544creates the forked conversation for the recipient withagent=public_conversation.agent.src/khoj/routers/api_chat.py:397exposes the authenticated/api/chat/share/forkendpoint for a public conversation slug.src/khoj/routers/api_chat.py:954sets the active chatagentfromconversation.agentwithout an accessibility check.src/khoj/routers/helpers.py:371,:391, and:400use the inherited agent'sinput_tools,output_modes, andpersonalityfor tool routing.src/khoj/routers/helpers.py:1827uses the inherited agent's name/personality to build the final chat system prompt.src/khoj/database/adapters/__init__.py:1724can use the inherited agent'schat_modelfor hidden agents or subscribed users.Mitigating evidence:
src/khoj/routers/helpers.py:1491checksAgentAdapters.ais_agent_accessible(agent, user)before normal document search.src/khoj/database/adapters/__init__.py:760returnsFalsefor a private agent whose creator is not the current user.So the issue is not confirmed as direct private document retrieval in this snapshot. The confirmed issue is unauthorized retention and use of a private agent reference and its behavior/configuration across user boundaries.
Expected Behavior
Forking a shared conversation should not grant or retain access to private or otherwise inaccessible agents owned by another user.
Safe behavior would be one of:
PublicConversation.conversation.agentagainst the current user. If inaccessible, replace it with the default public agent or reject the request.conversation.agent.Reproduction Steps
Controlled local verification was done against the source snapshot above, using only source inspection and synthetic user/agent assumptions.
/api/chat/share./api/chat/share/fork.ConversationAdapters.create_conversation_from_public_conversation()that the new user-B-owned conversation can retainagent_idfor user A's private agent.event_generator()and helper functions that the inherited agent is used for persona/system prompt construction and tool/model selection without first checking whether user B can access that private agent.execute_search()path rejects the inaccessible private agent, so direct private knowledge-base result retrieval was not confirmed in this source snapshot.Static verification checks passed locally:
Possible Workaround
Until fixed, avoid sharing conversations that use private or hidden agents. Operators can also disable public conversation sharing/forking or patch the share/fork flow to strip agents that are not public and accessible to the recipient.
Additional Information
Security impact: unauthorized cross-user retention and use of a private agent reference. This can expose or apply private agent configuration such as persona/instructions, tool availability, output modes, and potentially model selection across users. I would treat this as an authorization boundary issue, but with lower severity than direct private document disclosure because the normal document search path has an access-control guard in this snapshot.
Suggested fix:
Apply equivalent checks before using
conversation.agentin chat execution, tool routing, memory scoping, chat-model selection, and retrieval.Link to Discord or Github discussion
No response