Supabase RPC Leak Hits Capgo: What It Means

Two Capgo CVEs show how Supabase RPC functions can leak user data even with RLS in place. Here's what vibe-coded apps need to check today.

Barret4 min read

Two new CVEs this week show a leak pattern that has nothing to do with a stolen key. It's about a database function that quietly ignores the rules everyone assumes are protecting them. If your app uses Supabase RPC calls, this is worth ten minutes of your time today.

TL;DR

  • Capgo (Cap-go/capgo) shipped two Supabase RPC functions callable by the public anon key that leaked PII and usage metrics — CVE-2026-56226 and CVE-2026-56284.
  • The bug wasn't a leaked key. It was a SECURITY DEFINER function that never checked the caller's UUID against the requested UUID.
  • Lovable added data export/removal, code-download restrictions, and beta project monitoring — all governance tools worth turning on.
  • Vercel pushed scoped, short-lived tokens (Connect) and identity-gated deployments (Passport) instead of long-lived secrets — the same fix, one layer up.

The Capgo leak: a function, not a key

Capgo, before version 12.128.2, exposed two Postgres RPC functions through Supabase's PostgREST layer: get_orgs_v6(userid uuid) and get_total_metrics(org_id). Both were marked SECURITY DEFINER, meaning they run with the privileges of the function's owner, not the caller. Both were granted to the anon role, meaning anyone with the public publishable key — the key that's supposed to be public — could call them.

The flaw: neither function checked that the UUID you passed in matched your own session. Send any user's UUID to get_orgs_v6 and you get back their organization memberships, roles, subscription and trial metadata, and their management email. Send any org UUID to get_total_metrics and you get MAU, bandwidth, and install counts, plus confirmation the org exists at all. Full detail: CVE-2026-56226 and CVE-2026-56284.

This matters because it doesn't look like a classic leak. The anon key was doing exactly what anon keys do. Row Level Security might have been configured correctly on every table. The hole was in a function that bypasses RLS by design, because that's what SECURITY DEFINER is for — and nobody added the ownership check that was supposed to sit inside it.

Why RLS checks aren't enough

When an AI builder wires up a Supabase backend, RLS policies get the attention. RPC functions often don't, because they feel like backend logic rather than "the database." But an RPC function granted to anon and marked SECURITY DEFINER is a backdoor around every RLS policy you've written. If it takes a user ID or org ID as a parameter and doesn't verify the caller owns it, it will answer for anyone.

If your app calls Supabase RPC functions from the client, check three things: is the function granted to anon or only to authenticated, is it SECURITY DEFINER or SECURITY INVOKER, and does the function body verify auth.uid() matches the requested ID before returning anything. If you can't answer these from memory, that's the gap.

Platforms are building in the guardrails

Lovable shipped several changes this cycle that push toward the same fix from the platform side: export or remove Lovable Cloud data gives you a clean way to audit and control what's stored, restricting code downloads limits who can pull your source, and project monitoring in beta has Lovable review your code and recent errors on a schedule, flagging issues before you notice them yourself.

Vercel moved in the same direction from the infrastructure side. Vercel Connect replaces long-lived provider tokens sitting in environment variables with scoped, short-lived tokens fetched at runtime — the token can't be stolen and reused indefinitely because it doesn't exist until the moment it's needed. Vercel Passport puts real identity checks (Okta, Auth0, any OIDC provider) in front of deployments instead of relying on a shared link. Both are the infrastructure version of the same lesson: don't trust a broad grant that never expires and never checks who's asking.

FAQ

Does this mean my Supabase anon key is a security risk?

No. The anon key is meant to be public — it ships in your frontend bundle by design. The risk is never the key itself. It's what the key is allowed to call: which tables have RLS, and which RPC functions are granted to anon without checking the caller.

How do I check if I have a SECURITY DEFINER problem like Capgo's?

In the Supabase SQL editor, list your functions and check their security type and grants. Any function granted to anon that's SECURITY DEFINER and accepts a user or org ID as a parameter needs a line that checks auth.uid() (or equivalent) against that parameter before it returns data.

Is Row Level Security enough to protect my app?

RLS protects direct table access through PostgREST's default routes. It does not protect RPC functions marked SECURITY DEFINER, because those functions run with elevated privileges specifically to bypass RLS for their intended purpose. Every such function needs its own manual authorization check.

The bottom line

The Capgo CVEs are a reminder that "I set up RLS" is not the same as "my backend is authorized correctly." RPC functions are a second surface, and they don't inherit RLS protection automatically. Go check yours. And take the five minutes to turn on the governance features your platform already shipped — monitoring, data export controls, code-download restrictions — they exist because this exact failure mode keeps happening.

Find your gaps before an attacker does.

Is My Site Hackable? scans your deployed app for the exact issues in this article — exposed keys, missing RLS, open buckets — and tells you what's real and what's a false alarm.

Run a free scan →