mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-01 13:27:20 +02:00
🪪 fix: Enforce Conversation Ownership Checks in Remote Agent Controllers (#12263)
* 🔒 fix: Validate conversation ownership in remote agent API endpoints Add user-scoped ownership checks for client-supplied conversation IDs in OpenAI-compatible and Open Responses controllers to prevent cross-tenant file/message loading via IDOR. * 🔒 fix: Harden ownership checks against type confusion and unhandled errors - Add typeof string validation before getConvo to block NoSQL operator injection (e.g. { "$gt": "" }) bypassing the ownership check - Move ownership checks inside try/catch so DB errors produce structured JSON error responses instead of unhandled promise rejections - Add string type validation for conversation_id and previous_response_id in the upstream TS request validators (defense-in-depth) * 🧪 test: Add coverage for conversation ownership validation in remote agent APIs - Fix broken getConvo mock in openai.spec.js (was missing entirely) - Add tests for: owned conversation, unowned (404), non-string type (400), absent conversation_id (skipped), and DB error (500) — both controllers
This commit is contained in:
parent
951d261f5c
commit
381ed8539b
6 changed files with 218 additions and 7 deletions
|
|
@ -26,7 +26,7 @@ const { createToolEndCallback } = require('~/server/controllers/agents/callbacks
|
|||
const { findAccessibleResources } = require('~/server/services/PermissionService');
|
||||
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
|
||||
const { getMultiplier, getCacheMultiplier } = require('~/models/tx');
|
||||
const { getConvoFiles } = require('~/models/Conversation');
|
||||
const { getConvoFiles, getConvo } = require('~/models/Conversation');
|
||||
const { getAgent, getAgents } = require('~/models/Agent');
|
||||
const db = require('~/models');
|
||||
|
||||
|
|
@ -151,8 +151,6 @@ const OpenAIChatCompletionController = async (req, res) => {
|
|||
}
|
||||
|
||||
const responseId = `chatcmpl-${nanoid()}`;
|
||||
const conversationId = request.conversation_id ?? nanoid();
|
||||
const parentMessageId = request.parent_message_id ?? null;
|
||||
const created = Math.floor(Date.now() / 1000);
|
||||
|
||||
/** @type {import('@librechat/api').OpenAIResponseContext} — key must be `requestId` to match the type used by createChunk/buildNonStreamingResponse */
|
||||
|
|
@ -178,6 +176,23 @@ const OpenAIChatCompletionController = async (req, res) => {
|
|||
});
|
||||
|
||||
try {
|
||||
if (request.conversation_id != null) {
|
||||
if (typeof request.conversation_id !== 'string') {
|
||||
return sendErrorResponse(
|
||||
res,
|
||||
400,
|
||||
'conversation_id must be a string',
|
||||
'invalid_request_error',
|
||||
);
|
||||
}
|
||||
if (!(await getConvo(req.user?.id, request.conversation_id))) {
|
||||
return sendErrorResponse(res, 404, 'Conversation not found', 'invalid_request_error');
|
||||
}
|
||||
}
|
||||
|
||||
const conversationId = request.conversation_id ?? nanoid();
|
||||
const parentMessageId = request.parent_message_id ?? null;
|
||||
|
||||
// Build allowed providers set
|
||||
const allowedProviders = new Set(
|
||||
appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue