{"slug": "58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git", "title": "58 of 154 public Supabase apps had a working service_role key in git", "summary": "A security scan of 154 publicly accessible Supabase-backed apps found that 58 had a working service_role key exposed in their git history, according to a developer who published the findings. The service_role key bypasses Row-Level Security entirely, allowing anyone who finds it to read or delete data through the REST API. The developer attributed the leaks to AI coding tools scaffolding .env files with real credentials, dev-style filenames that evade .gitignore patterns, and keys persisting in git history after deletion.", "body_md": "If you build with Lovable, Bolt, Cursor, or any AI coding tool on top of Supabase, this week's scan has a number you should see: **58 of 154 public apps shipped a working `service_role` key inside their git history.**\n\nThat key is the master key for the database. It bypasses Row-Level Security entirely — the same RLS you turned on in the dashboard does not apply to it. Anyone who finds it can run `DELETE FROM \"user\"` over the REST API and the database believes it.\n\nI did not read a single row of anyone's data. I verified the key decodes to a real project (`iss`, `ref`, and a `service_role` role claim), checked that the project is live, and moved on. That is enough to know the exposure is real.\n\nThree causes, in the order I actually saw them:\n\n**AI tools scaffold `.env` files with real keys.** Your AI agent opens the Supabase dashboard to \"wire up the database,\" copies the project URL and keys into `.env`, and commits it because the app \"won't build\" otherwise. The placeholders in memory get replaced by real values — then `.gitignore` is never created for `.env`.\n\n**The file that ships is named like a dev file.** `.env.txt`, `env.local`, `.env.development` — these often bypass the `.gitignore` pattern `*.env` because the name is subtly different.\n\n**History keeps the key after the file is deleted.** Deleting `.env` in a new commit is not a fix. The key stays in every previous commit unless you rewrite history.\n\nPaste this into supabase.com/dashboard → your project → SQL Editor. It shows every object a `service_role` (or anon) key can reach that your RLS policies do not actually protect:\n\n```\nselect n.nspname, c.relname, c.relrowsecurity\nfrom pg_class c\njoin pg_namespace n on n.oid = c.relnamespace\nwhere c.relkind in ('r', 'p')\n  and n.nspname = 'public'\n  and not c.relrowsecurity\norder by n.nspname, c.relname;\n```\n\nEvery row of that result is readable and writable by anyone holding the public anon key that ships in your browser bundle. If `relrowsecurity` is false, Row-Level Security is not even on for that table.\n\nFor git: `git log --all --oneline -- .env` shows every commit the file touched. If you see more than the one \"deleting it\" commit, the key is still in history.\n\n`git filter-repo` or BFG).`.env`, create `.gitignore` entry `*.env*` before first launch.\"\nEvery check in that list — RLS coverage, open policies, `USING (true)` traps, write-without-read policy conflicts, and the git-history secret scan — I've packaged as a free, read-only, 60-point pre-launch check that runs against your own DB in about 30 seconds and uploads nothing:\n\n**github.com/cekuu35/supabase-rls-leak-demo**\n\nThe scan behind this post ran the same evidence standard: decode the JWT, confirm `role == \"service_role\"`, confirm the project is live. No data was read, no exports were made, nothing was saved. If you want a second pair of eyes on your schema after rotating — policy conflicts, unisolated joins, service_role exposure paths — that is the read-only review I do as paid work, and the email + Gumroad link are in my profile.", "url": "https://wpnews.pro/news/58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git", "canonical_source": "https://dev.to/cekuu35/58-of-154-public-supabase-apps-had-a-working-servicerole-key-in-git-gie", "published_at": "2026-09-24 09:12:40+00:00", "updated_at": "2026-09-24 09:30:42.889108+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools", "ai-safety"], "entities": ["Supabase", "Lovable", "Bolt", "Cursor", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git", "markdown": "https://wpnews.pro/news/58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git.md", "text": "https://wpnews.pro/news/58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git.txt", "jsonld": "https://wpnews.pro/news/58-of-154-public-supabase-apps-had-a-working-service-role-key-in-git.jsonld"}}