Skip to content

Harden public.profiles and bootstrap it for fresh databases - #177

Merged
YurMil merged 1 commit into
mainfrom
fix/profiles-hardening
Sep 16, 2026
Merged

YurMil merged 1 commit into
mainfrom
fix/profiles-hardening

Conversation

@YurMil

@YurMil YurMil commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Summary

Reading the production definition of public.profiles (to close task 2 in dev-plans/supabase-pending-work-2026-08.md) showed two holes. Both are already fixed in production (applied 2026-09-16 and recorded as version 20260916000000); this PR brings the repo in line.

Problem Fix
Any signed-in user could set their own role to admin. update_profiles_combined allows updating your own row, and the API roles held UPDATE on every column. The INSERT policy had the same gap. A BEFORE INSERT OR UPDATE OF role trigger rejects any non-default role unless the caller is already an admin. Only anon/authenticated are checked, so the dashboard, service_role and security definer functions are unaffected.
anon could read every user's e-mail (Public profiles is using (true)). SELECT is granted per column, without email. Admins still get e-mail from get_admin_users_list() (security definer).
Supabase Preview failed on every PR touching supabase/, because profiles was never defined in the migrations. A guarded bootstrap in 20260613000001 mirrors the production table, constraints, RLS and policies. It is a no-op where the table exists.

The admin dashboard's fallback query no longer selects email. The currently deployed bundle still does; it only logs an error, and the users list still loads from the RPC.

The audit log shows no role changes, and role counts are as expected.

Verification

  • Fresh database (PGlite): every migration now applies to the bare scaffold described in the plan. 10 behaviour checks pass. Without the new migration, the self-promotion check fails, so the exploit is reproduced.
  • Production, before applying: dry run inside a rolled-back transaction, simulating JWTs. 12/12 checks passed:
    • self-promotion and insert-as-admin are blocked;
    • own profile update/upsert works, and so does first-time profile creation;
    • the comments ↔ profiles join works;
    • an admin can change roles;
    • the admin RPCs work;
    • reading email is blocked for anon and authenticated.
  • Production, after applying, through REST with the anon key:
    • select=email → 401, select=* → 401;
    • public columns → 200;
    • comments with embedded profiles → 200;
    • PATCH role → 401.
  • The security advisor reports no new findings.
  • pnpm typecheck passes.

Note: select('*') on profiles now fails for clients, so name the columns. No current code does this.

🤖 Generated with Claude Code

- Reject role changes and non-default roles on insert unless the caller is
  already an admin; previously any signed-in user could promote themselves.
- Stop exposing profiles.email to anon and authenticated; admins keep
  reading it through get_admin_users_list().
- Recreate public.profiles, guarded, in the first migration that references
  it, so migrations apply to a database built from the repo alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cadautoscript-com Ready Ready Preview Sep 16, 2026 5:47pm UTC

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-16T17:46:26.548374Z eafe6d1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@supabase

supabase Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Updates to Preview Branch (fix/profiles-hardening) ↗︎

Deployments Status Updated
Database ✅ Wed, 16 Sep 2026 17:42:30 UTC
Services ✅ Wed, 16 Sep 2026 17:42:30 UTC
APIs ✅ Wed, 16 Sep 2026 17:42:30 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations ✅ Wed, 16 Sep 2026 17:42:41 UTC
Migrations ✅ Wed, 16 Sep 2026 17:42:47 UTC
Seeding ✅ Wed, 16 Sep 2026 17:42:47 UTC
Edge Functions ⚠️ Wed, 16 Sep 2026 17:42:47 UTC

⚠️ Warning — Only Functions declared in config.toml will be automatically deployed to branches: [functions.my-slug]


View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@YurMil
YurMil merged commit b6997da into main Sep 16, 2026
8 checks passed

This branch was successfully deployed

1 active deployment
Preview — eafe6d1b Deployed Sep 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant