fix(db): lock down audit_events to the backend (revoke + RLS + grant order)

↗ view on GitHub · Amal · 2026-08-01 · ec34667f

WHY THIS MATTERS
This repo's threat model explicitly includes direct PostgREST access with the
public anon key - which is why schema.sql revokes anon/authenticated on every
backend-owned table. audit_events shipped with neither a revoke nor RLS, so on a
hosted Supabase deployment its default privileges leave the whole table readable
AND writable from the browser: any visitor could dump every user's email, chat
titles and prompt excerpts, or forge/delete audit rows - which defeats the whole
point of an append-only audit trail.

WHAT IS RLS / PostgREST default access
Supabase exposes every table in schema `public` over PostgREST. Whether the
browser `anon`/`authenticated` roles can touch a table is governed by two
things: (1) SQL table GRANTs (managed Supabase's default ACLs grant these roles
broad privileges on new tables), and (2) Row-Level Security. With RLS disabled
and the default grants in place, the table is wide open. The repo's convention
is defense-in-depth: `revoke all ... from anon, authenticated` removes the
grant, and `enable row level security` (with no policies) means even if a grant
slips back the rows are invisible. service_role bypasses RLS, so the backend
path is unaffected.

HOW IT WORKS
- schema.sql: audit_events now has `revoke all ... from anon, authenticated` in
  the revoke block and `enable row level security`, matching every sibling
  table.
- Grant ordering (F4): `grant ... on all tables in schema public to
  service_role` only covers tables that already exist when it runs. The table
  was defined *after* that block, so a fresh plain-Postgres install created it
  with no service_role privileges and the backend's inserts failed
  permission-denied - silently, because recordAudit swallows errors. The DDL is
  moved above the grant block so the blanket grant covers it.
- migration 20260728: adds the same revoke + RLS, plus an explicit
  `grant select, insert, update, delete ... to service_role` so a fresh apply
  works even where service_role has no default ACL for new tables. The old
  comment ("no RLS policies needed, like other app tables") inverted the
  convention and is corrected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents 2bf6f9f8
Stats 2 files changed , +48 , -22
Part of Add workspace audit history

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-ec34667f.md from inside the repo you want the change in.

⬇ Download capture-commit-ec34667f.md