An empty table looks safe — until the data lands.
shipscan reads your code and your live app, and catches the table with no RLS that only looks safe because it's empty — the false green — before you launch. A URL-only scan can't.
Code × live — what the comparison reveals
usersprofilesordersComparing code to live is the only way to see the first two: a table with no RLS that looks safe just because it's empty (the "false green"), and a table your code "protects" that's actually wide open (a "conflict"). A URL-only scan misses both.
See who can reach your data
Not a checklist — a reach graph showing how far an anonymous visitor gets past each guard, and to which data.
Sources: CVE-2025-48757 · Supabase docs
Why a URL alone isn't enough
An empty table and a protected table look identical from the outside — both return no rows. Only your code shows whether RLS is actually on.
A live scan tells you about right now. Your code tells you about your next deploy, your staging, and every rebuild.
Supabase itself warns: don't assume an empty result means safe.
How it works
Drop in your GitHub repo
The confirmed check starts from your repo (free). You can also try it with just a URL. After launch, add your live URL to turn on the code × live comparison.
We compare code × live
We read your migrations, probe your live app, and line them up.
Get the graph + fixes
See who can reach which data, with plain-language steps to fix it.
What we check
The usual vibe-coding scanner checks, plus your code compared against your live app.
Supported backend: Supabase only, for now — checked deeply. Other backends like Firebase aren't supported yet; they're detected but their internals aren't checked.
Can anonymous visitors read your data
Whether a logged-out visitor can reach your tables, storage, RPCs, or data baked into the page.
- Exposure of data embedded in the initial HTML (__NEXT_DATA__)Data embedded in the initial HTML (__NEXT_DATA__/SSR props) is readable by anyone with no login.
- Supabase RLS live test (lists tables, counts rows readable without login)Queries tables as an anonymous visitor to see which rows are actually readable without login.
- Server functions callable without login (RPC, spec only)Server functions callable without login bypass RLS if they don't check permissions.
- Supabase Storage readable without login (anon bucket/object listing)Lists bucket objects as an anonymous visitor — public or misconfigured buckets let anyone read files.
- RLS configuration state (migrations/policies)Reads migrations/policies to see whether RLS is enabled and how policies are scoped.
- Public Storage bucketA public Storage bucket lets anyone with a URL read every file in it.
- SECURITY DEFINER functions callable without loginSECURITY DEFINER functions run with the definer's rights and bypass RLS if callable without login.
Are your API routes and authorization enforced
Whether protected API routes or service-role paths can be hit without logging in.
- Whether pages/APIs are reachable without login (measured)Probes pages/APIs to confirm whether they actually require login, not just claim to.
- service_role use in public routesservice_role bypasses RLS; using it in a public route can expose all data.
- API route auth requirementChecks whether API routes require authentication before returning data.
- Access between logged-in users (BOLA, Pro)ProWith two user tokens, tests whether one logged-in user can read another user's rows.
Things that must not be public
Whether keys, source maps, or config files that should stay private are exposed.
- Secret files left public (/.env, /.git)Files like /.env or /.git served publicly leak secrets and source history directly.
- Public source maps that reveal your source code (.js.map)Public .js.map files let anyone reconstruct your original source, even when minified.
- Committed secrets (keys/tokens/private keys)Keys/tokens committed to the repo are readable by anyone with access and scraped by bots quickly.
Transport and hardening
Whether the basics — TLS, security headers, delivery path — are set up.
- TLS/HTTPS (encrypted connection)Without HTTPS, traffic can be read or tampered with in transit.
- Security headers (HSTS, CSP, etc.)Missing HSTS/CSP/X-Frame-Options leaves the page open to downgrade, injection, and clickjacking.
- CDN/edge detectionIdentifies the edge/CDN in front of the app, which affects how headers and caching behave.
Known vulnerabilities in dependencies
Whether your lockfile pulls in dependencies with known CVEs.
- Known vulnerabilities in your dependencies (lockfile→OSV)Known CVEs in your lockfile are easy to look up and exploit.
Mapping the attack surface
The groundwork: figuring out which data, routes, and backends are reachable in the first place.
- Crawl of the site's own pages (one level deep)Finds the app's own pages to map what is reachable before the deeper checks.
- Supabase anon key discoveryFinds the public Supabase anon key in the page — the entry point an anonymous visitor uses to query data.
- Discovery of the APIs the app callsLists the APIs the front-end calls, so each can be checked for what it returns without login.
- Discovery of external backends the app connects to (from CSP)Reads the CSP to find external backends the app connects to — other data stores to consider.
- Discovery of your other sites on the domain (from public certificate records)Other hosts on your domain (from public certificate logs) are additional attack surface to review.
Not checked
Not checked: input handling (injection), business logic, server-side authorization internals, and logging/monitoring. Anything an automated external check can't see needs a separate review.
Pricing
Findings are free. shipscan only charges to unlock how to fix them and to export a report.
Every finding, in full
- Full reachability graph — who reaches which data, guard by guard
- Every finding — passive checks, live tests, code comparison, dependencies
- Why each risk matters, what happens if ignored, the standards it maps to
- What is checked and what is not — stated upfront
Everything in Free, plus the fix
- Everything in Free
- Step-by-step fixes — full steps, copyable SQL, an AI hand-off prompt
- Report export — Markdown, PDF, a shareable link, and a verifiable dated badge
- Cross-user (BOLA) test — can a logged-in user read another user’s data (coming soon)
- Re-check the same URL/repo for 30 days — fix and check again as you go
Pro on every project, billed monthly
- Everything in Pro
- On all your projects — no per-project purchase
- Fix steps and report export, on every project
- Continuous monitoring & change alerts — not built yet
The deep checks themselves (live tests, repository analysis) are free — they only require confirming you own or are authorized to test the target. Payment unlocks fix details and report export. A Launch Pass covers re-checks of the same URL/repo for 30 days from purchase, so you can fix and check again through your pre-launch cycle.
FAQ
- How is this different from other scanners?
- Most scanners only hit your live URL. shipscan also reads your repo (migrations, code) and compares the two — so it catches "guarded in code but open live" and "looks safe live but unguarded in code", which a URL-only scan can't.
- Do I have to connect my repo?
- You can try it with just a URL. But reading your code is the whole point — whether RLS and authorization are actually in place is something a URL alone can't tell. So the confirmed check needs your GitHub repo. Before launch, the repo check stands on its own; once you're live, add your URL to compare code against the running app and confirm each guard by measurement.
- Is it safe to run on my production app?
- Yes. Checks are read-only. Live tests run only with your consent and never write data.
- Does a green result mean I'm safe?
- No. It means no exposure was found in what we check. Input handling, business logic, and server-side authorization need a separate review.
- What do you do with my code and data?
- Checks are read-only and we don't store your code or results. Any token you pass (for a private repo) is used for that one scan and never saved. Shared results contain only the verdict you choose to publish.
Check your app before you launch
Enter your GitHub repo above (a URL-only try works too), or see a sample first.