← All fixes
Postgres function still callable by anon after REVOKE FROM anon
#1Error
Supabase RPC behind a paywall still returns data to logged-out users even after REVOKE EXECUTE ... FROM anon.
Fix
In Postgres, EXECUTE on new functions is granted to PUBLIC by default, and anon inherits PUBLIC. Revoking from anon does nothing. Run: REVOKE EXECUTE ON FUNCTION my_fn(args) FROM PUBLIC; GRANT EXECUTE ON FUNCTION my_fn(args) TO authenticated; To stop it for future functions: ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; Then test with the anon key to confirm it now fails.
Verified in a clean sandbox
Yarrow reproduced the error from scratch, applied the fix, and the error went away.
- Passed on
- Node 24.21.0 · @electric-sql/pglite 0.5.10 · Ubuntu 26.04.1 LTS, Oct 18, 2026
- Last checked
- Oct 18, 2026. Yarrow re-runs it every 30 days to catch dependency changes.
How it was tested
1. Build the broken project
npm init -y >/dev/null && npm i --silent @electric-sql/pglite@0.5.10 cat > check.mjs <<'EOF' import { PGlite } from "@electric-sql/pglite"; import { readFileSync } from "node:fs"; const db = new PGlite(); await db.exec("CREATE ROLE anon; CREATE ROLE authenticated; CREATE FUNCTION paid_data() RETURNS text LANGUAGE sql AS 'SELECT 1::text';"); await db.exec(readFileSync("grants.sql", "utf8")); await db.exec("SET ROLE anon"); try { await db.query("SELECT paid_data()"); console.log("LEAK: anon can still call paid_data()"); process.exit(1); } catch (e) { console.log("locked for anon:", e.message); } EOF echo "REVOKE EXECUTE ON FUNCTION paid_data() FROM anon;" > grants.sql2. Test fails (the error reproduces)
node check.mjs
3. Apply the fix
cat > grants.sql <<'EOF' REVOKE EXECUTE ON FUNCTION paid_data() FROM PUBLIC; GRANT EXECUTE ON FUNCTION paid_data() TO authenticated; EOF
4. Same test passes
node check.mjs
Sandbox log
Reproduced the error, applied the fix, and the error went away. $ setup (exit 0) $ test (exit 1) LEAK: anon can still call paid_data() $ apply (exit 0) $ test (exit 0) locked for anon: permission denied for function paid_data
Keep it fixed
The test that proved this fix also works as a regression check. Add it to your project's tests so a later migration or upgrade can't quietly bring the bug back.
# Regression check from Yarrow fix #1: Postgres function still callable by anon after REVOKE FROM anon
# Fails (non-zero exit) if the bug comes back. Written for a minimal repro project,
# so point paths and files at your own project before adding it to CI.
# Files the test needs (from the repro setup):
npm init -y >/dev/null && npm i --silent @electric-sql/pglite@0.5.10
cat > check.mjs <<'EOF'
import { PGlite } from "@electric-sql/pglite";
import { readFileSync } from "node:fs";
const db = new PGlite();
await db.exec("CREATE ROLE anon; CREATE ROLE authenticated; CREATE FUNCTION paid_data() RETURNS text LANGUAGE sql AS 'SELECT 1::text';");
await db.exec(readFileSync("grants.sql", "utf8"));
await db.exec("SET ROLE anon");
try {
await db.query("SELECT paid_data()");
console.log("LEAK: anon can still call paid_data()");
process.exit(1);
} catch (e) {
console.log("locked for anon:", e.message);
}
EOF
echo "REVOKE EXECUTE ON FUNCTION paid_data() FROM anon;" > grants.sql
# The check:
node check.mjsDid it work?
No reports yet0 worked0 didn't
Tried it yourself?
Related fixes
Supabase table readable by anyone with the anon key
#2Data in a Supabase table can be fetched from the browser with only the public anon key, no login.
Generated tsvector column fails: generation expression is not immutable
#14ERROR: generation expression is not immutable
postgresVerifiedNo reports yet
Vercel CLI hangs on 'Waiting for terms acceptance in browser'
#16vercel integration add neon: Opening browser for terms acceptance. Waiting for terms acceptance in browser...