Found while tracing every process_message_stream entrypoint for #13821 (PR #13981). Pre-existing, not introduced by that PR.
Problem
send_direct_chat_response (autobot-backend/api/chat.py) takes chat_id from the request body and streams a response into that session with no ownership validation.
The sibling endpoint on the same router does validate:
async def send_chat_message_by_id(
chat_id: str,
current_user: dict = Depends(get_current_user),
...
ownership: Dict = Depends(validate_chat_ownership), # SECURITY: Validate ownership
):
send_direct_chat_response has get_current_user but no validate_chat_ownership:
async def send_direct_chat_response(
current_user: dict = Depends(get_current_user),
request: Request = None,
message: str = Body(...),
chat_id: str = Body(...),
remember_choice: bool = Body(default=False),
):
Impact
Any authenticated user can post a direct/approval response into any chat session id, including another user's. This endpoint carries approval and denial decisions, so the reachable consequences are worse than message injection:
- resolving another user's pending command-approval interrupt on their behalf
remember_choice=True persists that decision for future turns in a session the caller does not own
- the injected message drives the workflow, so it can trigger tool execution in someone else's session
Authentication is required, so this is not anonymous — the boundary that is missing is between authenticated users, which is exactly what validate_chat_ownership exists to enforce.
Proposed fix
Add the validate_chat_ownership dependency, matching send_chat_message_by_id. If the omission is deliberate (e.g. an approval flow intended to be answerable by an operator), document why and gate it on an explicit permission rather than leaving it open to every authenticated role.
Acceptance criteria
Context
Found while tracing every
process_message_streamentrypoint for #13821 (PR #13981). Pre-existing, not introduced by that PR.Problem
send_direct_chat_response(autobot-backend/api/chat.py) takeschat_idfrom the request body and streams a response into that session with no ownership validation.The sibling endpoint on the same router does validate:
send_direct_chat_responsehasget_current_userbut novalidate_chat_ownership:Impact
Any authenticated user can post a direct/approval response into any chat session id, including another user's. This endpoint carries approval and denial decisions, so the reachable consequences are worse than message injection:
remember_choice=Truepersists that decision for future turns in a session the caller does not ownAuthentication is required, so this is not anonymous — the boundary that is missing is between authenticated users, which is exactly what
validate_chat_ownershipexists to enforce.Proposed fix
Add the
validate_chat_ownershipdependency, matchingsend_chat_message_by_id. If the omission is deliberate (e.g. an approval flow intended to be answerable by an operator), document why and gate it on an explicit permission rather than leaving it open to every authenticated role.Acceptance criteria
send_direct_chat_responserefuses achat_idthe caller does not own, or gates the cross-session case on an explicit permissionprocess_message_streamentrypoint audited for the same omission, result recorded in the PRContext
MCPDispatcher)