fix(supabase): scope schema.sql write policies to service_role (GH#2499) - #2500
fix(supabase): scope schema.sql write policies to service_role (GH#2499)#2500dcccrypto wants to merge 1 commit into
Conversation
The six write policies in supabase/schema.sql carried no `TO` clause. A PostgreSQL RLS policy without one applies to the PUBLIC pseudo-role, which includes `anon` — the role behind NEXT_PUBLIC_SUPABASE_ANON_KEY — so they authorise INSERT/UPDATE on markets, market_stats, trades and oracle_prices for anyone holding the public key, straight through PostgREST and around every check in the API routes. Scope, verified before changing anything: a database built from supabase/migrations/ is NOT affected. Migration 021 added service_role-only policies, and 20260402180100 dropped these six by name. The same defect had already been found and fixed three other times — bug_reports (20260402180000), ideas (20260330120000), job_applications (044) — so every instance in the migration chain is closed. What was still open is this file. As 20260402180100's own comment records, schema.sql is "the reference file sometimes run via Supabase SQL Editor", and PostgreSQL OR's permissive policies: running the old version against a migrated database would re-create the wide-open policies alongside the correct ones and silently re-open write access — exactly what that migration exists to undo. Fixed at the source so a fresh bootstrap, or a re-run, is safe. The UPDATE policies also gain `with check (true)` to match the service_role-only versions in migration 021; without it an UPDATE can read a row it may not write back. Test pins schema.sql only, and says why: historical migrations contain the original CREATEs and always will. Asserting over them would flag immutable history forever and pressure someone into editing an applied migration, which is worse than the thing being prevented. Mutation-verified: dropping `to service_role` from any one policy fails 2 tests. Suite: 2926 passed | 16 skipped, 0 failed.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughSupabase financial-table write policies now explicitly target ChangesRLS policy hardening
Estimated code review effort: 3 (Moderate) | ~15–30 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Fixes #2499.
The premise is correct. The claimed production impact is already mitigated — and I'd rather say so than let a High sit on the board unqualified.
Confirmed: all six write policies in
supabase/schema.sqllacked aTOclause, and a policy without one applies to the PUBLIC pseudo-role, which includes
anon. The report's reading of the SQL is exactly right.Not confirmed — the production exposure. The report audits
schema.sqlbutnot
supabase/migrations/, and the migration chain already closes this. Migration021_fix_rls_policies.sqlcreated*_service_onlypoliciesTO service_role, and20260402180100_drop_stale_core_table_rls_policies.sqldropped these six by name.Its header describes the identical finding, four months ahead of this report:
It also records that migration 021's own first attempt was a silent no-op — it
issued
DROP POLICYagainst names (markets_insert) that never existed — which iswhy a second migration was needed. Worth knowing: this class has bitten twice in a
way that looked fixed.
I swept the rest of the schema for the same shape rather than assuming these six
were it. Three more tables had it, and all three are also already fixed:
markets/market_stats/trades/oracle_prices20260402180100bug_reports20260402180000ideasService can update ideas20260330120000job_applicationsService can update applications044_fix_admin_users_rlsSo on any database built from the migration chain, nothing is open. The CVSS 8.6
doesn't apply to a migrated deployment.
What is still open, and is worth fixing
schema.sqlitself. As20260402180100's comment puts it, it is "the referencefile sometimes run via Supabase SQL Editor" — and PostgreSQL ORs permissive
policies. Running the old version against a migrated database re-creates the
wide-open policies alongside the correct ones and silently re-opens write access:
precisely what that migration exists to undo. A fresh environment bootstrapped from
it starts vulnerable outright.
That's a real footgun with a one-line-per-policy fix, so this PR fixes it at the
source. The UPDATE policies also gain
with check (true)to match 021'sservice_role versions — without it an UPDATE can read a row it may not write back.
Test scope, deliberately narrow
The test pins
schema.sqlonly. Historical migrations contain the originalCREATEs and always will; asserting over them would flag immutable history foreverand pressure someone into editing an applied migration — worse than the thing being
prevented. The comment in the test says this, so the next person doesn't "fix" the
gap by widening the sweep.
Mutation-verified: dropping
to service_rolefrom any single policy fails 2 tests.Suggested follow-up, not in this PR
The report includes a verification query for checking live policy state. Running it
against each deployed Supabase project is still worth doing — my analysis covers
what the repo produces, and cannot tell you whether some environment had
schema.sqlpasted into it by hand after its migrations ran. That's the onescenario where the report's original severity would hold, and it's answerable only
against the live databases.
Summary by CodeRabbit
Bug Fixes
Tests