Cross-tenant document read closed on tabular review routes

manueljpconde patched a confirmed authorization gap in the tabular review feature: authenticated users could submit another user's document IDs and the backend would read and forward that content to the LLM. A new helper now validates document ownership on every affected call site before anything is read.

securitymulti-tenant

The fix adds a batched document-fetch helper that runs the existing per-document access check across all requested IDs in one query. Any miss, denial, or DB error causes the handler to fail closed with a 404 - so the response doesn't leak whether a foreign document ID exists at all.

Four call sites are covered: create, PATCH, regenerate-cell, and generate. The PATCH path got the most restructuring because document validation now runs before the metadata write and explicit cell replacement, so a denied request can't leave behind partially-written display_name or sharing fields. On the generate path, validation runs against the final document set just before the SSE stream opens, which also catches any bad rows that existed before the fix was deployed. No backfill is needed.

No schema change, no new env vars, no frontend delta. Ten new backend tests accompany the four wirings. Logging at the new call sites records counts only - no document IDs, no request content. The author noted two narrow follow-ups left out of scope: optional cleanup of pre-existing foreign-document cell rows (only if evidence surfaces post-deploy, since generate-time validation already handles them at runtime), and a defensive check on the clear-cells route.

So what Worth pulling if you have tabular review routes in your own deployment - this is a real trust boundary error, not a theoretical one. Single backend deploy, clean revert path. The generate-path validation also acts as a runtime fence against historical dirty data, which is a sensible trade-off given the no-backfill approach.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?