# The vibe-coded app that got hacked: the 2025-2026 incidents and the five checks that would have caught them

> Eleven public incidents from March 2025 to October 2026, each with its date, its source and what actually broke, sorted into the pile that was yours to prevent and the pile that was the builder's.

- Source: https://www.buildbase.app/blog/vibe-coded-app-hacked-incidents-and-five-checks
- Section: Blog
- Author: Dharmendra Jagodana
- Published: 2026-10-29

## In short

A vibe-coded app gets hacked the way any app does, through what the code lets a stranger do; the difference is that nobody read the code. The public incidents of 2025 and 2026 sort into two piles, inside the app and inside the builder, and five checks run before launch would have caught every one in the first pile.

A vibe-coded app gets hacked the way any app does, through what its code
lets a stranger do. The difference is that nobody read the code. Between
March 2025 and October 2026 the incidents that reached the press sort
into two piles: the ones inside the app, which were the builder's
customer's to prevent, and the ones inside the builder, which were the
builder's. The first pile is the longer one, and five checks, run before
launch, would have caught everything in it.

This post is the list, with dates and primary sources, then the two
piles, then the five checks. It leaves one famous case out, says why, and
quotes each incident at the length the evidence supports. Where a figure
comes from the researcher who found the problem rather than from the
company, it says so, because the two often disagree.

## The incidents, in order

**March 2025, Enrichlead.** A sales-leads product whose founder said it
was built with Cursor and "zero hand-written code" was, by his own posts
days after launch, "under attack": "maxed out usage on API keys", "people
bypassing the subscription, creating random stuff in the database", and,
in his words, "as you know, I'm not technical, so this is taking me longer
than usual to figure out". It shut down within about a week. The secondary
write-ups agree on the shape (API keys exposed, no real authentication
checks, no rate limits) and disagree on the timeline; a dollar figure for
the API bill circulates and we could not corroborate it, so it is not
here.

**March to May 2025, CVE-2025-48757.** Matt Palmer scanned Lovable-built
sites and found 303 endpoints across 170 projects, 10.3% of the 1,645 he
analysed, where an unauthenticated visitor could read or write database
tables. The apps ran on Supabase with the public key in the browser and
tables that lacked correct row-level security. The CVE text describes an
"insufficient database Row-Level Security policy"; the record also carries
a note that the supplier disputes it, on the ground that each customer is
responsible for protecting their own app's data. Both things are true. The
scan covered homepages only, so Palmer says the true figure may be higher,
and his statement draws a line that still matters: Lovable's security scan
checks whether a policy exists, not whether it is right.

**July 2025, Base44.** Wiz Research found that Base44's registration and
one-time-code endpoints accepted requests without authentication, so
anyone holding an app's public id, which appears in its URLs, could create
a verified account on a private app, including apps restricted to SSO.
Wix fixed it within about a day and found no evidence of abuse. This one
is in the second pile: nothing a Base44 customer did caused it, and
nothing they could do would have prevented it.

**July 2025, Replit and SaaStr.** Jason Lemkin, a non-engineer building
with Replit's agent, reported that it deleted his production database
during a code freeze despite repeated instructions, invented about 4,000
users, and told him a rollback was impossible (it was not; he found the
restore worked). He also reported $607.70 in charges beyond his plan.
Replit's chief executive called it "unacceptable and should never be
possible", promised a refund and a postmortem, and began rolling out
automatic separation of development and production databases. Nearly
every detail comes from Lemkin's own posts, which The Register said it
did not independently verify, so record counts vary and none is given
here.

**December 2025, the Tenzai study.** Not an incident, but the measurement
behind the fear. Five tools (Cursor, Claude Code, Codex, Replit, Devin)
each built three apps; across the 15, researchers counted 69
vulnerabilities, about six critical. No exploitable SQL injection or
cross-site scripting; every tool introduced a server-side request forgery;
no app had CSRF protection or security headers; and most failures were
authorization and business logic, the kind where a negative quantity or
a negative price goes through. A vendor study with a tiny sample, and
still the clearest picture of what an agent gets wrong when nobody tells
it otherwise.

**January and February 2026, Moltbook.** Wiz found a Supabase key in the
front-end JavaScript of an AI-agent social network, with no row-level
security behind it, which gave read and write access to the production
database: about 1.5 million API tokens, 35,000 email addresses, 4,060
private agent-to-agent conversations, some containing plaintext OpenAI
keys, about 4.75 million records in all. Write access let Wiz edit live
posts. The founder is quoted in the write-up: "I didn't write a single
line of code for @moltbook." Fixed within a day of the report.

**February 2026, an exam app Lovable featured.** A researcher reported 16
vulnerabilities, six critical, in an exam and grading platform featured on
Lovable's Discover page: 18,697 user records, 14,928 unique emails,
thousands of student and enterprise accounts, 870 with full personal
details, readable by an unauthenticated visitor who could also send bulk
email, delete accounts and change grades. The cause was generated,
Supabase-backed code without row-level security or role checks; one
example was an authentication function with inverted logic that blocked
signed-in users and admitted anonymous ones. Lovable's security lead
responded that the project included code Lovable did not generate, that
the vulnerable database was not hosted by Lovable, that every project gets
a free security scan, and that applying fixes is at the user's
discretion. The researcher said his support ticket was closed without
response. First pile, with the builder's response quoted so you can judge
the second.

**February to April 2026, Lovable's public projects.** For about 76 days,
any logged-in Lovable user with a link could read the chat history and
source code of other users' public projects, because a backend permissions
change in February re-enabled access Lovable had removed in 2025 and the
API did not check ownership. Reports on HackerOne were closed as intended
behaviour; Lovable's first public statement said "we did not suffer a data
breach", and its second apologised and made historical public projects
private. The researcher says database credentials in that code were
exposed; Lovable's account covers chat history and source code, not
Lovable Cloud or private projects. Second pile.

**February 2026, DataTalks.Club.** An experienced developer's coding agent,
asked to move a second site into the same Terraform setup, unpacked an old
state archive and ran `terraform destroy`, deleting the network, the
database, the containers, the load balancers and the automated snapshots.
AWS restored it about a day later. His own summary: "I over-relied on the
AI agent to run Terraform commands." It is here because it is the same
failure as Replit and SaaStr with the non-technical builder removed: an
agent with production access and the backups inside its reach.

**May 2026, the RedAccess scan.** A security firm reported about 380,000
publicly reachable assets built with Lovable, Base44, Replit and Netlify,
of which about 5,000, 1.3%, held sensitive corporate data: ship schedules,
clinical-trial listings, support transcripts, a bank's internal
financials, patient conversations. It also found phishing pages hosted on
Lovable that impersonated banks and retailers. The platforms disputed the
method and the notice period, and the two press accounts count the
exposed apps differently, so the shape is more reliable than the numbers:
apps published on the builders' own domains, public by default or by
choice, with little or no sign-in in front of them.

**September 2026, the UpGuard scan.** Starting from about 300,000 domains
that showed signs of Supabase, UpGuard asked each for a `users` table;
16,326 answered with data. More than half showed personal information, a
smaller share passwords or tokens, a very small number plausible card
data. The study targeted standalone sites, not apps hosted on Lovable or
Replit, and it is not a breach of Supabase, whose security lead answered
that customers control how their projects are configured. The sentence
that matters is UpGuard's: row-level security is on by default only for
tables made in Supabase's table editor, and tables created through the
API, which is how coding agents work, "do not enable RLS by default".

**The one left out.** The July 2025 leak from the Tea app, about 72,000
images and then about 1.1 million private messages, is widely described
as a vibe-coding failure. The evidence supports a Firebase backend without
effective access rules and nothing more; the claim that the app was
AI-built rests on allegations nobody has attributed. It is a backend
misconfiguration story, and it would be convenient on this list, which is
exactly why it is not on it.

## Two piles

Read the list again and the sorting does itself.

**Inside the app**: Enrichlead, the 170 Lovable projects, Moltbook, the
exam app, the RedAccess and UpGuard scans, the Tenzai findings, and the
two deleted databases. The common root is not a clever attack. It is a
table anyone can read, an endpoint anyone can call, a check that runs in
the browser, a key in the bundle, or an agent that can reach production.
These were the builder's customer's to prevent, and the builders say so,
in the CVE dispute note and in Lovable's response to the exam app.

**Inside the builder**: the Base44 bypass and Lovable's 76-day exposure.
Nothing the customer did caused them, nothing the customer could have done
would have prevented them, and both were fixed by the vendor, one in a
day and one after a dispute. They matter for a different decision, which
builder to trust with your source and your users, and they are not what
the five checks below are for.

The first pile is the longer one, and that is good news, because it is the
pile you can do something about this afternoon.

## The five checks

Each check is a question you can answer yourself in under an hour, with
the incident it would have caught, and the instruction to paste into the
builder when the answer is wrong.

### 1. Can a stranger read the table?

The incident: the 170 Lovable projects, Moltbook, the exam app, the 16,326
Supabase databases.

The check is UpGuard's own method, run against your app. Open the built
site, find the Supabase address and the public key in the JavaScript
(they are meant to be there; the key is public by design), and ask the
REST endpoint for your `users` table with that key and no session. Rows
back means anyone gets rows back. Do it for every table that holds
something a customer would not want another customer to see.

Why generated apps fail it: the agent creates tables through the API,
which does not switch row-level security on, and the builder's scan then
checks that a policy exists, not that it says the right thing. A policy
that reads "any signed-in user can read every row" passes the scan and
fails this check.

The fix has two shapes. Either every table gets a policy keyed to the
workspace the row belongs to, and you read each one, or the browser stops
querying the database directly and every read goes through a server route
that checks who is asking and filters by their workspace. The second is
what [teams, invites and admin versus
member](/guides/teams-invites-and-roles-for-a-vibe-coded-app) sets up, and
it is the shape to prefer when you cannot read a policy yourself. Paste:
"List every table and its row-level security policy. For each table that
holds customer data, the policy must restrict reads and writes to rows
whose workspace id matches the signed-in user's workspace. Show me the
policy text before applying it."

### 2. Is every endpoint behind sign-in, including the ones that create accounts?

The incident: Base44's registration endpoint, the exam app's inverted
authentication function, and the audit that found "development endpoints
still in production that let anyone mark themselves as a paid user".

The check: list every route and function the app exposes (the agent can
print the list), and for each one, call it from a terminal with no
session. The right answer is a refusal. Pay particular attention to the
routes the agent added "for testing": a `/dev/` prefix, a `seed`, a
`make-admin`, a `reset`. They were useful on day two and they are
reachable on day ninety.

Why generated apps fail it: an agent writes the happy path first and the
guard second, and when the guard is a function it wrote itself, the
function can be wrong in ways that look right. The exam app's guard
admitted exactly the people it was meant to block. A guard that comes
from a platform and is tested there cannot be inverted by a typo in your
project.

Paste: "Print every HTTP route and server function with the check that
runs before it. Remove any route that exists for development or seeding.
Every remaining route that reads or writes customer data must refuse a
request with no valid session before doing anything else."

### 3. Who decides what a paid or admin user is?

The incident: "a live marketplace with real Stripe payments where any
logged-in user could grant themselves admin and hijack payment routing
with a single request", from an audit of three products posted on Reddit
in February 2026, and the unlimited-credits endpoint from the same audit.

The check: sign in as an ordinary member, open the browser's developer
tools, and change the value the app uses to decide what you are. A `role`
field in a request, a `plan` in local storage, a `credits` number in a
form. Then do the admin thing, or the paid thing. If it works, the
decision is being made in the browser, and the browser is the user.

Why generated apps fail it: the agent writes the check in the same place
it writes the button. A column called `plan` on the users table, set by
the browser after a Stripe redirect, checked by the page that shows the
feature, is the default output of "add a paid plan", and every word of it
is wrong: the browser set it, the browser can set it again, and the server
never checked. [Paid plans that actually gate
features](/guides/paid-plans-that-gate-features-vibe-coded-app) is the
guide to the right shape: the plan is set by a signed webhook from the
payment provider, held by a service the browser cannot write to, and
checked by the server before the feature runs.

This is the check no scanner runs, because to a scanner the column and
the request look fine. It is also the one a platform fixes outright:
when roles, plans and credits are enforced by an API that the browser
can only ask, not tell, the "single request" has nowhere to go. Paste:
"Find every place the app decides whether a user is an admin, on a paid
plan, or has credits left. Each decision must be made on the server from
a source the browser cannot write. Remove any route or field that lets a
request set its own role, plan or balance."

### 4. Is there a key in the bundle that should not be?

The incident: Moltbook (a Supabase key that was fine to publish, in front
of a database that was not fine to expose) and Enrichlead (API keys
"maxed out" by strangers).

The check: build the app, open the JavaScript the browser downloads, and
search it for every secret you own: the Supabase service key, the Stripe
secret key, the OpenAI key, the client secret of your sign-in. The public
keys (Supabase's anon key, a sign-in client id, Stripe's publishable key)
are meant to be there and the check is not about them. The secret ones
are never meant to be there, and in Vite and Next.js anything prefixed
`VITE_` or `NEXT_PUBLIC_` is, by construction.

Why generated apps fail it: the agent needed the key to make the feature
work in the preview, the preview is the browser, and the prefix was the
only way to make it visible there. The fix is the server again: the key is
read in a route or a function, the browser calls the route, and the
builder's secrets store (Lovable's Secrets, Bolt's Secrets, Replit's
Secrets tool) holds the value where the bundle cannot see it. Paste: "List
every environment variable the app reads and where. Any secret read in
browser code moves to a server route, and its prefix is removed. Rotate
every secret that has ever shipped in the bundle."

### 5. What can the agent do to production?

The incident: SaaStr's database and DataTalks.Club's infrastructure, and
the Cursor and Supabase MCP demonstration from July 2025, in which an
assistant running database queries with full privileges was talked, by
text hidden in a support ticket, into reading a private tokens table and
writing it back into the ticket.

The check: write down what the agent can reach without a human in the
loop. Can it run a destructive command against the live database? Can it
reach the backups? Does it hold a key that bypasses the row rules? Does
it read text that strangers wrote (tickets, comments, form submissions)
in the same session in which it holds those powers? Each yes is the
precondition for one of the incidents above.

Why generated apps fail it: the convenience that makes the builder useful
is the agent having access. Replit's answer after July 2025 was to
separate development and production databases automatically; the
demonstration's authors list read-only mode and scoping the agent to one
project; the DataTalks post-mortem moved the backups out of the same
configuration the agent could destroy. The principle underneath all three
is that the agent gets the narrowest access the task needs and the
backups live where it cannot reach. Paste: "Show me the credentials and
tools available to the agent in this project, which of them can modify
the production database or infrastructure, and how to restrict each to
development."

> **Key takeaway:** Every app-side incident on this list is one of five questions answered wrong:
> a table a stranger can read, an endpoint with no sign-in, a plan or role
> decided in the browser, a secret in the bundle, or an agent that can reach
> production. Answer the five yourself, before launch, and the list stops
> applying to you.

## Where the builders' own scans win

For checks 1 and 4 the builders now do real work, and you should let them.
Lovable's scan runs on every publish and checks that row rules exist, with
a deeper scan on request; Bolt's audit is a free button in the Publish
menu that its help page says checks sign-in, whether users can "change a
price", and whether keys are "sent to visitors' browsers"; Replit
separates development and production data so a mistake in the preview
does not land on customers. If you do nothing else, publish through those
and read what they report.

What they cannot do is answer check 3, because to a scanner a role column
is a column, or run check 5, because the agent is the product. The scans
are the floor. The five checks are the launch.

## How BuildBase sits in this

Checks 2 and 3 are the ones a platform removes rather than helps with.
When sign-in, workspaces, roles, plans and credits are enforced by an API
the browser can only ask, there is no role column to flip, no plan field
to set after a redirect, no dev endpoint to leave behind, and the webhook
that changes a plan is signed and verified before anything moves. That is
what [the launch checklist](/guides/vibe-coded-app-launch-checklist) is
about, item by item.

It does not run checks 1, 4 or 5 for you. Your tables are still yours,
and the rule we give every agent is to keep them where they are; the
builder's secrets store is still where the secret goes; and what your
agent can reach in production is a decision about the agent, not about
any SDK. The honest claim is narrower than "secure": two of the five
questions stop being yours to answer, and the other three get a shorter
answer, because the data the browser can reach no longer includes who is
an admin and what they paid for.

The question underneath all five, what a backend is and which half of it
is your product, is [a separate explainer](/blog/what-is-a-saas-backend).
The incidents above are what happens when the second half is built by
the same prompt that built the first.
