← Startup Signals
7 min readUpdated 2026-10-02Issue 5

The table your coding agent created does not have row level security on it.

Dashboard-created tables get row level security by default now. API-created tables do not, and API is how your coding agent works.

ByKarthik KumarCafiyn Innovations
At a glance
Research
UpGuard, dated 1 October 2026
Scanned
About 300,000 domains with indicators of Supabase usage
Found
16,326 databases exposing readable tables
PII
Over half showed indicators of some PII
Named cause
Tables created through the API do not enable RLS by default
Not a product flaw
UpGuard attributes exposure to application-side configuration

What the research says, and what it does not

UpGuard published a census rather than an incident report. It says it "gathered ~300,000 unique domains with indicators of Supabase usage" and, across that candidate set, "identified 16,326 databases exposing readable tables". Over half showed indicators of some PII, and a smaller share included passwords or authentication tokens.

It is worth being precise about whose problem this is, because the aggregator headlines were not. UpGuard attributes the exposure to how application owners configured their own projects, not to a flaw in Supabase. It also credits Supabase with having "made product changes to implement RLS by default for tables created in the Table Editor UI". Supabase's chief information security officer told TechCrunch that the company provides "secure defaults and tooling, and customers control how their own projects are configured".

So this is not a breach of Supabase, and nobody should read it that way. It is a measurement of how often a particular configuration mistake occurs in the wild, and the number is large enough to be worth ten minutes of your time.

The specific gap

The sentence in the research that should make you check your own projects is this one: "Tables created programmatically through the API, which is how coding agents interact with Supabase, do not enable RLS by default."

Read that twice. Supabase closed this for tables you create by hand in the dashboard. It remains open for tables created through the API. And creating tables through the API is exactly what happens when you ask a coding agent to add a feature that needs a new table.

That produces an uncomfortable asymmetry: the tables you made deliberately, while thinking about the schema, are protected. The tables that appeared because you asked for a feature, which you may not have reviewed line by line, are not. The second category is growing much faster than the first.

UpGuard names two further causes worth checking at the same time: policies that exist but do not adequately restrict access, and public keys used in client-side code as though they were secret keys.

RLS enabled is not the same as protected

There are three states, and only one of them is safe. Teams routinely confuse the middle one with the last.

Row level security disabled means the table is readable by whatever role reaches it. Row level security enabled with no policy means Postgres denies everything, which is safe but usually means the feature is quietly broken rather than correctly locked down. Row level security enabled with a policy that correctly scopes rows to the requesting user is the state you want.

The failure mode that produces exposure is a fourth case: RLS enabled with a policy that looks restrictive but is not, for example one that checks that a user is authenticated without checking that the row belongs to them. Every authenticated user, including one who just signed themselves up, then reads everything.

So an audit that only asks "is RLS on" will give you a clean bill of health you have not earned. You need to look at the policies.

The audit

Run this against every project, including the staging one and the prototype nobody uses, because exposure does not care how seriously you take the environment.

  • List every table in the public schema with its RLS state, by querying pg_class joined to pg_namespace for relname and relrowsecurity where nspname is public
  • For every table where RLS is off, decide whether it should be exposed at all, then either enable RLS with a policy or remove it from the API surface
  • For every table where RLS is on, read the policy and confirm it scopes rows to the requesting user, not merely to any authenticated user
  • Pay particular attention to tables you did not create by hand. Check your migration history for tables added by an agent or a code generator
  • Grep your web bundles and mobile builds for a service_role key. The anon key is designed to be public. The service role key bypasses RLS entirely, and if it shipped to clients, rotate it
  • Check your Supabase logs for high-volume anonymous reads against tables holding user records, which is what exploitation looks like

One more thing lands on 30 October

There is a related change already scheduled, and it will turn some of these quiet exposures into loud breakages. Supabase has said that new tables in the public schema will no longer be exposed to the Data API by default, "enforced on all projects on 2026-10-30". The dashboard control is called "Automatically expose new tables and functions".

Crucially, the mechanism is grants rather than RLS. The changelog is explicit: "Without an explicit GRANT, a role will not have access to the created table." That means a migration which creates a table and enables RLS and adds a policy, which is the responsible thing to do, will still produce a table your client cannot read after 30 October unless you also grant.

The failure will present as an empty result rather than a permissions error, which is the worst kind. Add the grant statements to your migration boilerplate now, alongside the RLS and policy statements, and if you create tables at runtime, that code path needs an explicit exposure step.

The underlying lesson about agents and schemas

This is not really a story about Supabase. It is a story about what happens when the thing that writes your schema is not the thing that thinks about your threat model.

A coding agent asked to add a feature will create the table the feature needs, and it will be right about the columns. Whether that table should be readable by an unauthenticated visitor is not a question the feature request contains, so it does not get answered.

The durable fix is to make the safe thing the default in your own repository rather than relying on remembering: a migration template that includes RLS, a policy and a grant; a test that fails if any table in the public schema has RLS disabled; a review step on schema changes specifically. Any of those three turns this from a recurring risk into a solved one.

What to do

  1. 1Query pg_class for relrowsecurity across the public schema in every project, including staging and prototypes.
  2. 2Read the policies, not just the RLS flag. A policy that only checks authentication exposes every row to every signed-up user.
  3. 3Check which tables were created by an agent or code generator rather than by hand.
  4. 4Grep client bundles and mobile builds for a service_role key, and rotate it if you find one.
  5. 5Add GRANT statements to your migration template before the 30 October Data API change.
  6. 6Add a test that fails when any public-schema table has RLS disabled, so this stays fixed.

Common questions

Was Supabase breached?

No. UpGuard published research measuring how many Supabase-backed applications expose readable tables, and attributes the exposure to application-side configuration rather than a Supabase product flaw. It credits Supabase with having enabled RLS by default for tables created in the Table Editor UI.

Why do agent-created tables lack row level security?

UpGuard notes that tables created programmatically through the API do not enable RLS by default, and that the API is how coding agents interact with Supabase. The default applies to tables created in the dashboard Table Editor.

Is enabling RLS enough?

No. RLS with no policy denies everything, which usually means a broken feature. RLS with a policy that only checks that the requester is authenticated exposes every row to any user who signs up. The policy has to scope rows to the requesting user.

Is the anon key meant to be public?

Yes, the anon key is designed to be used in client code and is protected by row level security. The service_role key bypasses RLS and must never ship to a client. If it has, rotate it.

What changes on 30 October 2026?

Supabase will stop exposing new public-schema tables to the Data API by default on all projects. The mechanism is grants, not RLS: without an explicit GRANT a role has no access, so migrations need grant statements alongside their RLS and policy statements.

How would I know if a table was read?

Check Supabase logs for high-volume anonymous reads against tables holding user records. Exposure through a missing policy looks like ordinary API traffic, not like an attack.

Sources

Read against the primary documentation rather than secondary coverage. Where a figure comes from a provider's own docs, it is quoted as published on 2026-10-02.

Related

More from Signals

OpenAI removes o1, o3-mini, o4-mini and GPT-4 Turbo on 23 October. The replacements it names run from 5.5 times the price to 97 percent less.
Eleven models and six fine tuned models go dark in four days. Here is OpenAI's own mapping, what each landing costs on a typical month of traffic, and the places where the named replacement is not the cheapest or cannot be used at all.
OpenCode says a breach hit 1.4 percent of Console users who linked an AI provider. Rotate those keys before the root cause is published.
OpenCode has confirmed the breach and has not said how it happened or what was taken. Here is what the vendor said, who is exposed, the order to work in, and how to limit the damage next time.
GitHub Actions workflow lockfiles are in public preview. Here is how to turn them on, check them in CI, and what they do not protect.
A tag can move after you reviewed it. A lockfile makes the runner use the commit you reviewed. Here is the setup, the CI check, the policy, and what a lockfile cannot tell you.
Next.js 16.4.1 and 15.5.28 fix two critical remote code execution flaws. For one of them there is no workaround.
Two of the three fixes were postponed from September. Here is what each flaw needs in order to be reachable, the one workaround that exists, and what the announcement leaves out.
A poisoned npm release that punishes you for revoking the stolen token. The order of your response is the whole defence.
Most incident guidance starts with revoking the leaked token. Here that is the trigger. What StepSecurity found, the order it recommends, and why a valid provenance attestation did not help.
Claude Haiku 5.5 costs a tenth of Haiku 4.5 per token. One token past 100,000 it costs half.
The rate card is a tenth of Haiku 4.5. The footnote is a threshold that moves the price fivefold and that a tokenizer change pushes you toward.
Gemini 4 Argon launched at $2 and $10 and goes to $4 and $20 on 1 December. Price your product on the second number.
Every tier on the pricing page doubles on the same date. What changes, what the number includes, and what to do if you are building on the launch rate.
Claude Haiku 5.5 is not a model id swap. Five things that worked on Haiku 4.5 now return a 400.
The model id is the easy part. The requests that now fail, the response parsing that changes, and the details that fail quietly.
Next.js shipped 16.3.7 and 16.3.8 a day apart. Only one of them is the security release.
Two patch releases a day apart, one of them a decoy if you are not reading carefully. And the 15.5 line does not get the high severity fix at all.
The EU AI output marking rule is not a December deadline. For most teams it started in August.
Almost every summary of this gives one date. There are two, and which one applies to you depends on when you shipped, not on how big you are.
Sonnet 5.5 cut the rate by a third and the token count went up by about 30 percent.
A third off the per-token rate is not a third off the bill when the same text now costs more tokens. Here is the arithmetic, and the five ways your code can break.
Your AI gateway holds every provider key you have. A 9.9 just landed in LiteLLM.
An AI gateway concentrates every upstream key into one process. That is the point of it, and it is why a privilege escalation there is worse than it looks.
A 9.5 in next/og. Whether it reaches you depends on one line in your OG route.
The advisory rates it 9.5, but it only fires if your OG image route puts attacker-controlled text into SVG. Here is how to find out which side of that line you are on.
OpenAI and Anthropic both cut prices on 22 September. One of them costs you a refactor.
Two frontier price cuts in one day is a good week for anyone paying a token bill. The migration cost is not evenly distributed, and one of the two prices comes with an expiry hedge.
A package install reads your home directory. This week two of them did it on purpose.
The implant did nothing clever. It read the files your shell reads, in the place your credentials live, and sent them somewhere else. That is the whole attack, and it works.
GitHub is switching off SHA-1 SSH keys. Find out now whether yours is one of them.
The keys at risk are the old ones, on the machines nobody logs into, in the pipelines that have run untouched for three years. Those are also the ones that will fail loudest.
Every third-party script tag is a standing grant to run code on your visitors.
A marketing form, a chat widget, an analytics snippet. Each one is permission for someone else's server to execute whatever it sends, on every page it sits on, forever.
1.8 million Android apps were scanned for hardcoded keys. Here is how to check yours.
Finding secrets in shipped apps is now a cheap, automated pipeline. Anything compiled into an APK, an IPA or a JavaScript bundle should be treated as already published.
OpenAI's Agents API will hold your agent state. What breaks if you ever need to leave.
Two major providers moved into the agent state layer in the same week. A managed harness is a real shortcut, as long as you keep a copy of what it holds.
Meta's Muse agent can now check out at your store. What merchants should change.
Agent purchases are arriving through wallets and single-use cards, not through merchants adopting a new protocol. Many merchants will receive them without choosing to.
What Brevo's SSO incident teaches about multi-tenant SAML and hijacked sending accounts.
One incident, two lessons: why email authentication did not help, and the tenant-scoping mistake anyone shipping SSO can make.
Cloudflare now sorts bots into Search, Training and Agent. What it means for your site and your agent.
The same change lands on two audiences. Site owners can now accidentally block search; agent builders will find more doors closed.
OpenAI has announced ten dated deadlines between 23 October 2026 and 1 April 2027. Here is the calendar.
Ten dated deadlines in six months, every one announced in public. The failure mode is never the notice. Updated 19 October with the entries added since September.
Nvidia bought Hugging Face. Your open-weight fallback is now a vendor relationship.
The commitments say nothing breaks. The structural change is that "we can always self-host" is now a dependency on one vendor rather than on a neutral commons.
The Videos API goes away on 24 September and OpenAI names nothing to move to.
Every other deprecation on OpenAI's page offers a migration target. This one does not, which makes it a different kind of problem.
Whisper shuts down on 26 February. The risk is not the deadline.
Five months is a generous window. Whisper is also typically wired into a background job that fails into a queue rather than into a user's face.
The Assistants API is gone. Your app may not have told you yet.
It shut down on 26 August 2026. The replacement is not a drop-in, and the most common migration mistake changes your model's behaviour without throwing an error.
One Copilot code review costs 13 requests. Here is the rest of the maths.
Copilot now bills on usage rather than seats. The headline change is simple. The multipliers underneath it are where teams get surprised.
Row-level security is on by default now. That is not the same as being protected.
Lovable fixed the default. The gap that actually leaks data was never the default, it was the policy, and no platform can write that for you.
Your model comparison is measuring the wrong number.
Every price list is per token. Your bill is per finished piece of work. Those two numbers can rank models in opposite orders.
An agent with production log access is a new identity. Treat it like one.
Always-on agents that read production logs are arriving fast. The access-control question they raise is old and well understood, which is the good news.

Open the tool.

Ten production categories, three minutes, and a ranked list of what to fix first.

Check your own stack