Capgo - Unauthenticated Cross-Tenant Metrics Poisoning via upsert_version_meta RPC
Description
Capgo before 12.128.2 contains an authorization bypass vulnerability in the public.upsert_version_meta SECURITY DEFINER function exposed via PostgREST RPC, allowing unauthenticated attackers to insert arbitrary rows into version_meta for any app_id. Attackers can exploit this by calling the RPC endpoint with a public anon key to poison storage metrics, causing persistent false data in dashboards and triggering incorrect alerts across victim applications.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected products
1Patches
Vulnerability mechanics
Root cause
"The PostgreSQL function `public.upsert_version_meta` is created as `SECURITY DEFINER`, granted to the `anon` role, and performs no authorization checks, allowing unauthenticated attackers to write arbitrary rows into `version_meta` for any `app_id`."
Attack vector
An unauthenticated attacker sends a POST request to `/rest/v1/rpc/upsert_version_meta` using the public Supabase anon key (the `sb_publishable_*` key) with attacker-controlled `p_app_id`, `p_version_id`, and `p_size` JSON parameters. The function is granted to the `anon` role and is `SECURITY DEFINER`, so it bypasses row-level security on `version_meta` and inserts the data without any authorization checks [ref_id=1]. This enables cross-tenant writes to any victim application's storage metadata.
Affected code
The vulnerability is in the Supabase PostgreSQL function `public.upsert_version_meta(p_app_id, p_version_id, p_size)` exposed as a PostgREST RPC endpoint. The function is defined as `SECURITY DEFINER` and is granted EXECUTE to the `anon` role ([ref_id=1]). It performs no authorization checks, allowing any caller to insert rows into `public.version_meta` for arbitrary `app_id` and `version_id` values without verifying ownership.
What the fix does
The advisory recommends removing the `EXECUTE` privilege on `public.upsert_version_meta` from the `anon` and `authenticated` roles, restricting it to `service_role` only. It also recommends adding explicit authorization inside the function: derive the caller's identity via `public.get_identity('{write,all}'::public.key_mode[])`, verify that the caller has rights to `p_app_id`, and confirm that `p_version_id` belongs to `p_app_id`. Additionally, the advisory suggests rejecting negative `p_size` values or enforcing strict bounds [ref_id=1]. The patch thus closes the privilege escalation path by removing public access and adding ownership checks.
Preconditions
- networkThe attacker must have network access to the Supabase project's PostgREST endpoint and know the project URL.
- inputThe attacker must possess the public Supabase anon key (`sb_publishable_*`), which is typically exposed in client-side applications.
Reproduction
The advisory includes a PoC that demonstrates the exploit [ref_id=1]. An attacker runs a shell script that calls `curl` against the RPC endpoint with the anon key and attacker-chosen `p_app_id`, `p_version_id`, and `p_size` values. The PoC shows the function returns `true` on the first insert (whether positive or negative size) and `false` on a duplicate insert, confirming arbitrary write capability for any app ID.
Generated on Jun 20, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2- github.com/Cap-go/capgo/security/advisories/GHSA-g4hx-x8gc-x6rwmitrevendor-advisory
- www.vulncheck.com/advisories/capgo-unauthenticated-cross-tenant-metrics-poisoning-via-upsert-version-meta-rpcmitrethird-party-advisory
News mentions
0No linked articles in our index yet.