Problem
backend/src/routes/documents.ts — the POST /single-documents/download-zip endpoint accepts an unbounded document_ids: string[]:
const { document_ids } = req.body as { document_ids?: string[] };
if (!Array.isArray(document_ids) || document_ids.length === 0)
return void res.status(400).json({ detail: "document_ids is required" });
It then fetches all matching documents from the DB, performs per-doc access checks, loads every file's bytes from R2 concurrently (Promise.all), and buffers the full ZIP in memory before sending. An authenticated user with many large documents could exhaust backend memory.
Fix
- Cap
document_ids to a reasonable maximum (e.g., 50) — return 400 if exceeded.
- Consider streaming the ZIP with
archiver or similar rather than buffering the entire archive.
Problem
backend/src/routes/documents.ts— thePOST /single-documents/download-zipendpoint accepts an unboundeddocument_ids: string[]:It then fetches all matching documents from the DB, performs per-doc access checks, loads every file's bytes from R2 concurrently (
Promise.all), and buffers the full ZIP in memory before sending. An authenticated user with many large documents could exhaust backend memory.Fix
document_idsto a reasonable maximum (e.g., 50) — return 400 if exceeded.archiveror similar rather than buffering the entire archive.