Storage buckets
Four Supabase Storage buckets:
| Bucket | Public? | Holds | Policy |
|---|---|---|---|
case-attachments | private | everything case-related: evidence, deliverables, dossier ZIPs (dossiers/{case_id}/{export_id}.zip), portal uploads ({case_id}/{uuid}.{ext}) | authenticated INSERT/SELECT gated only on bucket_id — no per-company path check |
company-logos | public | company logos at {company_id}/logo.{ext} | write requires is_super_admin() OR is_company_admin(first-path-segment); anyone reads |
user-avatars | public | {user_id}/avatar.{ext} | write scoped to auth.uid(); anyone reads |
email-assets | public | the email logo | anyone reads; authenticated INSERT |
The critical problem
case-attachments — which holds all confidential case evidence, legal deliverables, and
the final dossiers — has no tenant path isolation. Any authenticated user of any company
can read or write any path. This is the single most severe data-isolation hole in the PoC,
and it directly contradicts the "Encriptação ponta-a-ponta" marketing claim.
See storage has no tenant isolation.
Inconsistent pathing
File paths are ad-hoc and inconsistent: some are tenant-scoped
({companyId}/{caseId}/deliverables/{phase}/{uuid} in InquiryReportDrawer), most are
only case-scoped ({caseId}/{uuid} in checklist/portal uploads). No convention.
Signed-URL expiries also vary by call site: 60s (dossier download, evidence vault), 300s (deliverables), 3600s (portal items).
Rebuild implication
One private bucket with enforced per-company path prefixes in the storage policy (or a proxied download endpoint that checks authorization), a single path convention, and consistent, short signed-URL lifetimes. If the E2E-encryption claim is to be real, encrypt at rest with per-tenant keys.