Skip to content

[Tracking] Supabase prod security and performance review, 2026-09-28 #327

Open

Description

@HMarzban

Key: SPR-00 · Type: tracking · Blocked by: none · Checked against: 3bb26de

Purpose

This issue tracks the review of the Supabase prod project on 2026-09-28. The review covered security and performance. It lists the public issues that the review filed, and holds one flag.

On 2026-09-28 at 09:29Z, the Supabase security linter flagged 7 ERROR items on prod (Postgres 15.8.1.073). The root cause is in the repo. The sweeps in packages/supabase/scripts/29-lint-hardening.sql §3, §4, §5 and §8 have no paired migration, so prod never ran them.

Two defaults keep each new prod object open:

  • Prod pg_default_acl (role postgres, schema public) gives anon and authenticated ALL on new tables and EXECUTE on new functions.
  • CREATE FUNCTION also grants EXECUTE to PUBLIC. A REVOKE ... FROM anon does not remove that grant, and anon inherits it.

The source reports are the maintainer's local reports. Each child issue copies the facts it needs, so an agent can work from the repo and the issue alone.

What was applied to prod on 2026-09-28

At about 13:38Z, the HoE (the Head of Engineering agent) applied an emergency fix to prod through the SQL connector. The maintainer approved it. It changed grants and RLS only. It changed no function body.

  • Transaction 1 (tables):
    • RLS was off on document_views_2026_03, _04, _05 and PushSentLog. Now RLS is on for every document_views partition, the parent, document_view_stats, document_views_daily, email_bounces and PushSentLog.
    • anon and authenticated lost every grant on those tables. service_role keeps its access.
    • anon lost every grant on admin_users, so the is_admin revoke below breaks no guest read.
    • anon lost table-level SELECT on public.users. It got a column grant back without email, so guests can still read username.
  • Transaction 2 (functions):
    • 27 functions became service_role only. Examples: purge_document_footprint, consume_push_queue, get_email_bounces, and the four prod-only functions.
    • is_admin was revoked from anon. authenticated keeps it.

Results after the change:

  • Security linter: 0 ERROR (was 7). Lint 0028 fell from 72 to 47 rows.
  • The built-in asserts in both transactions passed. Cron jobs 2, 8, 17, 25 and 28 ran and succeeded afterwards.
  • get_channel_aggregate_data was not touched, so guest chat still works.

Prod now differs from the repo. A fresh db reset or a self-hosted install still starts open. Any later migration that drops and re-creates one of these functions resets its grants to the open defaults. #314 ports the slice into the repo.

Other facts at the time of the review:

  • Migration history: prod history ends at 20260921120000. The repo has three later migrations: 20260922120000 (commit f6ed0a22c), 20260928120000 (commit e4113fd9d) and 20260928130000 (commit 3ef541d8a). Ship the three migrations that prod's history lacks #313 ships them.
  • Performance: all SQL together uses 35.0 s of DB time per hour, 0.97% of one core, in a 69.8-minute prod window. Realtime list_changes takes 78% of that. The database is 62 MB. Supabase bills compute by instance size, so query work saves $0.
  • Next partition: the monthly cron (1st of the month, 00:05 UTC) runs create_document_views_partitions. On 1 October it creates document_views_2027_01 with the open default grants.
    • On prod, the ensure_rls event trigger turns RLS on, so the new partition stays closed. The repo has no such trigger.
    • In the repo, create_document_views_partitions in packages/supabase/scripts/09-document-views.sql (~105) turns RLS on but revokes no grant. The first DO block (~66) does neither. Make new document_views partitions start closed #315 owns both gaps.

The flag

Flag: red. Every issue in the red list is still open.

The red list is #313 and #314. #314 covers a prod state the repo cannot rebuild, and #313 must land before it.

Update the flag when an issue closes. Move to amber only when every issue in the red list is closed.

The issues

Urgent decisions

Security repo fix

Security rulings

Platform

Performance

Records

Tracked privately

Five further security items (SPR-01, SPR-08, SPR-11, SPR-12, SPR-13) follow SECURITY.md. Each is filed here after it is fixed on prod.

Order and dependencies

Dated checks. Each is a read-only [maintainer] step:

Order:

Not yet owned

No issue owns these yet. Green needs a ruling or an owner for each:

What we decided not to do

The review measured and rejected 14 query ideas and parked 4 more. Examples: moving chat postgres_changes to broadcast, the advisor's channel_members (channel_id) index, and a Micro downgrade before memory data exists. The numbers and the reopen triggers are in #326. Do not re-propose one of these ideas without new numbers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DevOpsSecuritySecurity, access control, and data exposure

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions