Security check for Supabase apps

One missing policy opens a whole table.

Your app ships the anon key to every browser. Paste your URL: we look for leaked service_role keys and, after you verify your domain (free), check what that key can read.

First scan is free, no signup and no code access. By scanning you confirm you own this site or are authorized to test it, and agree to the Terms and Privacy Policy.

acme-launch.app · scan report
D

One key needs rotating before launch.

Built with Supabase · Vite · Vercel

Issues
5
Hardening
7
Checks
108
  1. CriticalSupabase service key is readable in main.jsJS bundle
  2. HighSource map publishes your src/ folderSource maps
  3. MediumAnyone can send email as your domainDMARC
  4. MediumSession cookie set without SecureCookies
  5. LowNo Content-Security-Policy headerHeaders

Passive external check; application logic (SQLi, IDOR) is not tested.

checks across headers, TLS, DNS, email and supply chain
100+
secret patterns searched in your JavaScript and source maps
~40
for the first scan, no signup and no code access
$0

The anon key is public. The policies are the lock.

  1. Every browser has the key

    Your project URL and anon key ship with your JavaScript, by design. Row Level Security is the only thing between them and your tables.

  2. The table added later

    RLS gets turned on for the first tables, not always for the later ones. New Supabase projects no longer expose new tables by default, but older projects, and tables an AI tool exposed with its own grants, can still answer anyone.

  3. One key skips every rule

    A service_role key in client code bypasses RLS completely. It is the database password with a friendlier name.

Demo replay

A scan of a Supabase app, replayed.

A demo of what runs when you paste the URL of an app on Supabase. The key checks run on every scan; the table checks run after you verify the domain.

  • passnothing visible from outside
  • warnworth fixing
  • faila finding with a fix prompt
Scan log · exampleyour-app.com
  1. passstackSupabase backend found in the bundle
  2. passkeysSupabase key decoded: role anon
  3. warnheadersNo Content-Security-Policy
  4. warnemailDMARC policy is p=none
  5. failtablesorders answers the anon key: 200after verify
  6. passtablesprofiles returns no rows: protectedafter verify
CriticalAnyone can read your orders tableComes with a fix prompt in the report

What we check on a Supabase app

How Supabase apps end up open. Public checks run on every scan; Verified ones after you prove the domain is yours.

  • Tables readable without login

    Up to 25 tables your bundle or API schema references, each asked for one row with the anon key. We keep the status and column names, never the row

    After you verify

  • service_role key in the browser

    We decode every Supabase key in your JavaScript. A service_role key bypasses Row Level Security completely

    Public

  • API schema open to anon

    Whether the REST API root lists your tables and views to anyone, a map for someone looking for open tables

    After you verify

  • Other keys next to it

    About 40 key patterns, such as OpenAI, Stripe and database connection strings, in the bundle and source maps

    Public

How it works

From URL to fixed in four steps.

  1. Paste your URL

    No code, no login, no setup. The first scan takes about a minute.

  2. Read what a stranger could do

    Each issue is explained by its consequence, in plain English, ranked by severity.

  3. Fix with your AI tool

    Copy the fix prompt for the issue and paste it into your AI tool.

  4. Rescan and confirm

    Verify your domain for free to unlock the deeper checks, then rescan to see the issue gone.

Free first scan. No signup, no code access.

Your anon key is public. Here is what it can reach.

  1. 01The bundlePublic

    The key and the table names ship together

    Supabase apps call the database from the browser, so the project URL, the anon key and the table names are in your JavaScript. That is by design.

    your-app.com

    /assets/index-7b20.js

    const sb = createClient(URL, ANON_KEY)sb.from("orders").select("*")sb.from("profiles").select("name")
  2. 02The rolePublic

    We check which key it is

    We decode each Supabase key. role: anon is expected. role: service_role skips every policy, and that is critical.

    your-app.com

    decoded key payload

    {  "iss": "supabase",  "role": "anon",  "ref": "qzv…"}
  3. 03The tablesVerified

    Then we ask each table for one row

    After you verify the domain (free), we ask each table the bundle names for one row with that public key. We keep the status and column names, never the values.

    your-app.com

    Verified check

    GET /rest/v1/orders?select=*&limit=1200 OK · 1 row[{  "id": "•••",  "customer_name": "•••",  "address": "•••"}]

    Values hidden. We store the status and column names only.

  4. 04The fixPublic

    A migration your AI tool can write

    The finding comes with a prompt for the policy it needs. Apply it, rescan, and the table returns no rows to a stranger.

    your-app.com

    Fix prompt for your AI tool

    Write a migration that enables Row Level Security on "orders" and adds a select policy so a signed-in user only sees rows where user_id = auth.uid(). Do not add any policy for the anon role.

Example data. Values in the table check are hidden.

Inside the report

A finding, and the prompt that closes the table.

Every issue comes with a prompt for the tool you build with: Lovable, Bolt, Cursor, Claude and more. Apply it, then rescan to see the finding gone.

Supabase security guide
your-app.comExample

Critical

Anyone can read your orders table

The table answers the public anon key without login, so every order, with names and addresses, is readable by anyone.

GET /rest/v1/orders?select=*&limit=1 with the anon key

Fix prompt for your AI tool

My Supabase table "orders" can be read by anyone with the anon key. Write a migration that enables Row Level Security on "orders" and adds a select policy so a signed-in user only sees rows where user_id = auth.uid(). Do not add any policy for the anon role. Check every other table the app uses the same way.

An example finding. Table checks run on your own domain after you verify it.

Compared

Not instead of your other checks. Next to them.

WhatShypsetSupabase Security AdvisorManual pentest
Where it looksYour live site, from outsideYour project, from insideYour app, by a security tester
Headers, TLS, DNS, emailChecked on every scanNot coveredUsually covered
Works for any stack or hostYesOnly the Supabase projectYes
Logged-in logic (IDOR, per-user rules)Not testedLints RLS policies; doesn’t test logicYes
Cost and timeFirst scan free, about a minuteFree, inside the toolQuoted per project, days to weeks

Run the built-in scan too: it sees the inside. Shypset shows what a stranger sees.

One URL, the whole outside view.

checks across headers, TLS, DNS and email
100+
API key patterns in your JavaScript
40+
free scans a month with an account
3
lines of your code we need to see
0

A scan takes about a minute. Table and file checks run after free domain verification.

Pricing

Your first scan is free, no signup needed. Upgrade when you want the full report and fix prompts for every issue.

Free

For your first check before launch

$0/ month

 

  • 3 scans per month
  • 1 free rescan of each scan within 7 days
  • Fix prompt for your top issue
  • Security grade and severity breakdown
  • OWASP Top 10 and CWE mapping
  • 7-day history

Pro

For founders shipping AI-built apps

$15/ month

Billed monthly, $180 a year

  • Up to 200 scans a month (fair use)
  • 1-year history
  • Full report for every issue
  • Fix prompts for Cursor, Lovable, Claude and more
  • HTML and Markdown export
  • “Scanned by Shypset” badge for your verified domain
  • Weekly monitoring with email alerts (verified domains)

Agency

For agencies and freelancers. Coming soon

Soon

 

  • More domains for client projects
  • Reports you can hand to clients
  • Need more today? Contact us

Launch Pass

$9 one-time

One domain for 30 days: the full report and every fix prompt, plus 3 rescans. The public report and badge are not included.

Questions

Yes, if every exposed table has Row Level Security with correct policies. The key is not a secret; the policies are the security.

The table check asks for one row to see whether access is open, then discards it. We store the status and column names only.

No. We test what an anonymous visitor can reach. Policies that let one signed-in user read another user’s rows need a logged-in test, which we do not run.

The first scan is free, with no signup and no code access. A free account gives you 3 scans a month. The full report and a fix prompt for every issue come with Pro or a one-time $9 Launch Pass.

Without verification we only read what any visitor’s browser can, plus public records such as DNS. Deeper checks, like asking your database tables for a row, run only after you verify that the domain is yours. Verification is free.

Your policies guard the data. We check they actually do.

Paste a URL and get the report in about a minute. First scan is free, no signup and no code access.

Ask the team behind the scanner.

Real people read every message. Tell us what you need and we will answer by email.

Questions
About a scan, a finding, or how a check works.
Agency and team needs
More domains, reports for clients, invoicing.
Security concerns
A vulnerability in Shypset, or a request not to scan your domain.

Prefer email? Write to info@softdoes.com.

Contact form

Do not paste passwords or API keys. A link to the report is enough.