Row-Level Security Limitations for AI Agent Workloads in Supabase
AI agents bypass Postgres row-level security by violating its human-session assumptions.

Row-level security in Postgres is a filter that checks a policy condition at query time against the identity of whoever is asking. When a query hits a table protected by RLS, Postgres evaluates the calling role against the rule you've written and returns only the rows that rule permits. The design assumes that identity comes from somewhere stable: a developer logged into a session, or a user who authenticated through a login screen. That assumption held for most of the system's working life, because for most of that life it was true.
Three facts about how RLS behaves matter before anything else. First, when RLS is turned on for a table and no policy has been written for it, the table does not become partly restricted. It becomes totally inaccessible: every query returns zero rows, every write is rejected, for both the anon role and the authenticated role. Second, superusers and table owners get around RLS automatically unless FORCE ROW LEVEL SECURITY has been explicitly set on the table, and the service_role key bypasses RLS by design, no matter what policies exist. Third, Supabase's own guide to RLS has a documented comprehension problem: nearly a third of readers who land on it report finding it unhelpful. That figure says something specific. It says the human-session model the documentation assumes doesn't transfer cleanly even to the human developers it was written for.
None of this makes RLS poorly designed. A system built around the premise that a person sits behind every query, with judgment to notice when a table looks wrong or a policy looks missing, was a reasonable system to build when that premise was almost always true. The cost of that premise appears once the premise stops holding, which is what's happening now. AI agents break the human-session assumption in three distinct ways. They have no natural session boundary the way a login does. They're routinely handed elevated roles because it's easier than scoping permissions precisely. And they write SQL directly and programmatically, skipping the UI flows that, for a human developer, carried implicit safeguards baked into the product's own interface.
The four structural limitations that become dangerous when agents replace humans
None of the four limitations below are new. Every one of them has existed in Supabase since before agents wrote a line of SQL against it. What agents remove is the layer of human judgment that used to catch these gaps before they became exposures.
The first limitation concerns how RLS gets turned on in the first place. Supabase has confirmed that tables created through its web dashboard get RLS enabled automatically. Tables created through the SQL editor or the API do not, unless the project has set up an event trigger specifically to enforce it. Coding agents almost always take the API route, not the dashboard. That means every table an agent scaffolds starts out fully exposed to anyone holding the project's public key, because the automatic protection a human clicking through the dashboard would have triggered simply never fires for programmatic table creation.
The second limitation is bypass-by-role. The service_role key bypasses all RLS by design, and so do superusers and table owners unless FORCE ROW LEVEL SECURITY has been explicitly set. The convenience path of handing an agent the service key so it can "do everything it needs" removes the RLS boundary entirely, with no error and no warning that anything has changed. This is why exposing a service_role key in client-side code counts as a critical-severity finding in security audits: it grants admin-level access that no row policy, however carefully written, can intercept.
The third limitation is the deny-all trap, and it has two separate failure modes that both end badly. If RLS is enabled on a table with no matching policy, the table becomes completely inaccessible: zero rows returned, every write rejected. The predictable sequence that follows is that the application breaks, the developer (or agent) disables RLS to get things working again, and the table ends up less protected than before anyone tried to secure it. The second jaw of the trap is subtler: RLS policies apply per operation, so a read policy does nothing to restrict write operations like insert, update, or delete unless each of those is separately covered. A table can look secured, pass a casual read test, and still allow unrestricted writes, because nobody wrote the other three rules. Supabase's own agent-evaluation testing caught this pattern: agents testing against a public weather dashboard granted unauthenticated visitors write access to everything in it, because the agent left Postgres's default grants in place instead of revoking them before layering policies on top.
The fourth limitation is standing write grants, and it's structurally different from the first three because it concerns not what gets configured but who holds the credential once configuration is done. A write grant given to a hosted orchestrator differs categorically from a write grant given to a developer working in an active session. The developer's access exists only while they're at the keyboard. The orchestrator's credential is standing: it persists, and the orchestrator decides when to use it, tonight, next Tuesday, or repeatedly in a retry loop if a subtask fails and tries again. An MCP server attached to a developer's coding session runs while that developer works and stops when the session ends. A connector attached to a hosted orchestrator holds a credential with no session boundary at all, active whether or not anyone is watching.
What a large-scale scan of public databases shows about the real-world distribution of these failures
A scan of a large number of databases showed how often these structural gaps produce live exposures, and the method behind it required no sophistication. Researchers took the public key and database address that thousands of sites already publish directly in their own JavaScript, queried the standard PostgREST endpoint for a table named "users," and thousands of databases simply returned the data. No authentication bypass, no exploit, no privilege escalation. The query matched the table, and the table answered.
One evasive move that developers might assume would help turned out not to. Naming the sensitive table "customers" instead of "users" bought nothing, because the API's own hint behavior pointed researchers to the readable table regardless of what it was called. More than half of the exposed databases showed indicators of personally identifiable information, and a smaller but still meaningful share held passwords or authentication tokens.
Two examples from the scan show what that exposure means in practice. An Indian adult content platform left tens of thousands of users open, including names, email addresses, dates of birth, identity document details, payout account information, and over a hundred thousand private messages. A Philippines-based OTP service tied to a SIM farm exposed vast numbers of SMS messages, the overwhelming majority of them one-time passcodes, with a small remainder made up of ordinary texts between rideshare drivers and passengers who had nothing to do with the operation. Both cases trace back to the same mechanical gap described above: RLS either wasn't enabled, wasn't configured with a matching policy, or was bypassed through a role that never should have had standing access.
Three earlier studies that found the same pattern independently, narrowing in on AI-generated code
A security scan from one vendor is not an isolated finding, and three earlier, independently conducted pieces of research reached the same diagnosis through different methods, each narrowing the focus further toward AI-generated code as the specific accelerant.
The first is CVE-2025-48757, published by researcher Matt Palmer in May 2025, covering insufficient row-level security policies across projects generated by Lovable, a platform that builds applications directly from natural-language prompts. Palmer's follow-up statement lists hundreds of vulnerable endpoints spread across roughly one in ten of the projects he examined, a rate that points to a systematic gap in how the platform's generated code handled row security.
The second is a scan security firm Escape ran in October 2025 across thousands of public applications built with Lovable, Base44, Create.xyz, Vibe Studio, and Bolt.new. The scan went no further than an ordinary visitor could go, so every exposure it found was reachable without any special access or technique.
The third is Supabase's own internal agent evaluation, run in August 2026. Supabase tested its agents against its own RLS documentation, giving them a vibe-coder persona and no leading language about security, and found the agents granted unauthenticated visitors write access to all tables, including truncate. The public weather dashboard used in testing became the clearest demonstration of why this mattered: the documentation's critical instructions existed, but they weren't positioned where an agent's selective, partial read of the guide would land. Supabase's fix wasn't rhetorical. It moved test examples next to the code an agent would actually copy, rather than leaving them in a section that assumed a complete, careful read-through, and that structural change was what made the eval pass consistently afterward.
Across all four studies, the pattern is the same database answering whoever asked, with no sophisticated intrusion involved. The SQL was valid, the key was public, and the only missing layer in every case was RLS configured correctly from the start.
How write access changes the risk calculus
Everything described so far concerns reading data that shouldn't have been readable. Read-side failure and write-side failure behave differently; most existing RLS discussion, including each study above, focuses almost exclusively on the read side. A read-side failure is serious but bounded: the wrong rows reach the wrong reader, and after the fact, you can generally say what leaked and to whom. A write-side failure doesn't stay still. A poisoned or simply wrong row gets read by the application downstream, joined into reports, embedded into the agent's own persisted state, and trusted by whatever run comes next, because persistent write access means state accumulates across runs.
This is close to what one well-known researcher has described as the "lethal trifecta": private data, exposure to untrusted content, and a channel capable of exfiltrating or acting on both at once. Write access doesn't just add a new risk alongside the read-side exposures already documented. It compounds them, because an agent that can write is an agent whose errors propagate forward into every subsequent decision it or another process makes based on that data.
The Perplexity Computer connector, launched in August 2026, is the first mainstream database product to sell the write path itself as a launch feature for a hosted orchestrator. Through the connector, an agent can read from and write back to Postgres tables, persist state across runs, look up users, and invoke Edge Functions. "Invoke Edge Functions" in particular means arbitrary server-side code paths become callable, carrying whatever privileges those functions were written with. None of this requires a compromised key or a misconfigured policy. It's the intended feature set, and the credential backing it is standing rather than session-bound, held by an orchestrator that decides on its own schedule when to use it.
The Replit incident, in which a vibe-coding agent deleted a production database, has been documented in press coverage and referenced in academic literature as a concrete instance of what write-side failure looks like once no governance gate exists on destructive operations. The incident is also a useful corrective to an assumption many teams carry about RLS itself: a correctly configured row policy that permits inserting a row does nothing to prevent a bulk delete, a schema migration, or an Edge Function call that bypasses the row filter. RLS governs which rows a query can see or touch. It was never built to govern what kind of operation an already-authorized caller is allowed to run.
What correct RLS configuration requires when an agent is the caller
Securing a Supabase project against agentic workloads takes more than flipping RLS on and writing a policy or two. It requires designing those policies around the agent's actual identity model, because an agent does not carry auth.uid() the way an authenticated human session does, and a policy written as though it will is a policy that either blocks the agent entirely or, worse, passes it through unchecked.
A write-capable agent needs its own role, scoped to the minimum set of operations that task requires, never a role shared across multiple agents or services, and never the service role itself. Credentials issued to that role should be short-lived rather than standing: the credential's lifetime should match the length of the task, not the uptime of the orchestrator running it. Every table an agent can reach needs explicit per-operation policies, not just a read rule assumed to cover the rest, since Postgres evaluates each write operation against its own separate rules regardless of what the read policy says. Default grants need to be revoked before new policies are layered on top, since Supabase's own agent-eval testing showed that leaving default grants in place is precisely what let unauthenticated visitors write to a table that looked, on paper, already secured. And destructive operations such as truncate, bulk delete, and schema migration need a governance gate that sits outside RLS altogether, because RLS was never designed to constrain what an already-authorized caller is permitted to do once it's inside the door.


