HIPAA compliance evidence for the database layer on Supabase: RLS, functions, triggers and audit logs
Supabase signs a BAA and has a High Compliance setting for projects that store PHI, which handles the platform. The database layer built on it is a separate matter. RLS policies, functions, triggers and audit tables are
Supabase signs a BAA and has a High Compliance setting for projects that store PHI, which handles the platform. The database layer built on it is a separate matter. RLS policies, functions, triggers and audit tables are written by the project team, and a HIPAA review expects evidence that they protect PHI. Supabaseβs shared responsibility model puts them on the customerβs side.
What Supabase covers
Encryption at rest and in transit, the BAA, and project settings. The HIPAA guide asks for these to be on:
- Point in Time Recovery
- SSL enforcement
- network restrictions
- connection logging
- MFA on every account with access
The Security Advisor warns when any of them is off, and the linter flags patterns like a table in public with RLS off. Neither one runs the policies. They cannot show whether a nurse at one clinic can read another clinicβs visits, or whether a deleted record left a row in the audit table.
What the project owner has to show
A HIPAA review asks three questions about the database:
- who can reach which patient rows - 164.312(a)(1)
- is every change to that data recorded - 164.312(b)
- can anyone alter that record afterwards - 164.312(c)(1)
164.308(a)(8) adds that this is evaluated again whenever the environment changes. On Supabase, the answers live in SQL the team wrote. A screenshot of the policies does not show what the database does when authenticated calls one of those functions. Only running it does.
Producing the evidence with unitautogen hipaa
unitautogen hipaa runs against the local Supabase stack, which is built from the migrations, so no patient data is involved:
bash
supabase start
unitautogen --supabase hipaa --schema public
It generates the tests and the rows they need, runs them as each role the app uses, and rolls the rows back. The tests are written to supabase/tests/seccover/security/, where supabase test db picks them up. The report goes to supabase/tests/seccover/hipaa/public/.
What it checks
- Access. Functions, views and triggers that run as their owner bypass RLS. Each one is called as two different tenants, with data on both sides, and marked as holding or leaking. Open policies, column grants and PHI leaking through error messages are checked too.
- Audit log. For every PHI table, and for each of insert, update and delete: did the change leave an audit row, and does that row record who made it. Connection logging does not answer this. It records who connected, not which rows changed.
- Tampering. For each audit table and each client role: can that role edit, delete or forge rows. A common hole is a security definer function exposed through .rpc() that writes to the audit table, letting any logged-in user write anything into it.
- Identity. Policies depend on auth.uid(), auth.jwt() or a session setting. The report shows whether a client session can change what those return.
What to fix
The report opens with a ranked βWhat to fixβ list. There is one row per object, with:
- the priority: fix now or review
- the HIPAA section
- what was found
- the change that closes it
- a link to the evidence
When many functions are callable only through PostgreSQLβs default PUBLIC grant, the report says so once above the list and gives the single fix:
sql
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC;
Every finding is backed by a pgTAP test that can be committed and re-run. Each result is mapped to its 164.312 section, and the report is stamped with a SHA-256 of every file behind it, so any later edit shows.
what to do on every migration
Schema changes reach Supabase as migrations in a PR. Add the hipaa command to the CI job that already runs supabase test db:
bash
supabase start
supabase test db
unitautogen --supabase hipaa --schema public
Every PR then produces a new report, dated and stamped, kept as a build artifact. 164.308(a)(8) asks for an evaluation each time the environment changes. Each of these reports is that evaluation for the database.
Scope
The report gives evidence for RLS policies, functions, triggers and audit logs.
It does not cover:
encryption, backups and network settings - Supabaseβs part
More on unitautogen hipaa: https://unitautogen.com/postgresql/unitautogen/hipaa/
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.