Skip to main content

case-attachments has no tenant isolation

The private case-attachments bucket holds all confidential material: case evidence, legal deliverables, the final dossier ZIPs, and portal uploads. Its storage policies gate only on bucket_id — there is no per-company (or per-case) path check.

Impact: any authenticated user of any tenant can read or write any file in the bucket, given (or guessing) the path. Paths are predictable ({case_id}/{uuid}.{ext}, dossiers/{case_id}/{export_id}.zip) and case_ids leak through the app. This is a cross-tenant confidential-data breach and it directly contradicts the product's "end-to-end encryption" claim (intent vs reality).

By contrast, company-logos and user-avatars do enforce a first-path-segment check — so the pattern to fix this exists in the same codebase, just wasn't applied to the sensitive bucket.

Rebuild implication

Enforce a per-company path prefix in the storage policy (mirror the company-logos is_company_admin((storage.foldername(name))[1]::uuid) pattern), or serve all downloads through an authorization-checking endpoint. Consider encryption at rest with per-tenant keys if the E2E promise is to be honoured. Adopt one path convention.