Lovable Leaked Source Code and Secrets From Every Old Project for 76 Days
A backend regression at Lovable re-exposed source code, database credentials, and AI chat history on every project built before November 2025. The reports came in on day 19 and got closed as intended behavior. Here's the mechanism, why the bug bounty missed it, and the check that was never there.
On April 20, 2026, a security researcher made a free Lovable account, sent five API calls, and read another user’s source code, hardcoded database credentials, and full AI chat history. Not his project. Anyone’s, as long as it was built before November 2025. He had filed the same finding through Lovable’s bug bounty 48 days earlier, where it was closed as a duplicate of intended behavior. He posted the working version to X, and Lovable shipped a fix in about two hours. The interesting number in this story is not the two hours. It’s the 76 days before them, and the reason the clock ran that long: the bug had a paper trail inside Lovable that said it was a feature.
We build deliberately broken reference apps to learn what these tools ship, and we wrote up a Lovable app that leaked every user’s data through disabled RLS. That was a flaw in the code Lovable generates for you. This one is a flaw in Lovable itself, the platform that holds the code, and it is the more instructive failure because nothing was misconfigured by a founder. The provider had built the right protection, then walked back over it, and then could not see the reports telling it so.
The protection existed, then a refactor undid it
Lovable did not start out exposed. Cyber Kendra’s account of the post-mortem lays out the order: it removed public access to project chat histories in March 2025, and in November 2025 extended private-by-default to every subscription tier. Two deliberate hardening steps, a year apart. The reasoning was sound and the order was the problem: private-by-default landed in November, so every project created before November carried a public flag the new default never retroactively cleared.
On February 3, 2026, a backend change to unify permissions reintroduced public access to chat history and source code on those public projects. In Lovable’s own words: “A backend regression reintroduced access to chat histories on public projects,” undoing protections it had “deliberately put in place between March and November 2025.” This is the most ordinary kind of regression. A team consolidates two permission systems into one, and the new path has a gap the old path covered. A negative test asserting that a stranger gets a 403 would have caught it, but those are the tests nobody writes for a property they assume is structural, and the ones that do exist tend to build their fixtures with the current defaults, so they exercise new private-by-default projects and never the legacy public cohort. The protection was a year old and treated as settled. The refactor reopened it and the build stayed green.
The API checked who you were, never what you owned
The flaw the researcher demonstrated is Broken Object Level Authorization, the number one entry on the OWASP API Security Top 10. The project-messages endpoint authenticated the caller, confirmed a valid logged-in user, and then served any project still flagged public, its chat history and source code included, without re-checking that the caller owned what came back. Authentication intact, object-level authorization gone. The protection the refactor dropped was the March 2025 one that had kept a public project’s chat and source private, and with it gone, “public” silently went back to meaning “readable by any logged-in stranger.”
It rhymes with the cosmetic page guard we wrote up, where the dashboard redirects to a login screen while the API behind it answers anyone. The shared shape is that the protection a user can see is not the one the server enforces. There the gap was the app’s. Here it is the platform’s, on its own control plane, which is a worse place to find it. A pre-November project returned 200 OK with the full payload; a newer one returned 403. The split fell exactly on the November private-by-default line, the tell that this was legacy database state and not a toggle a founder forgot to flip. The exposure rode on a public flag set at creation time, for one cohort, that the new default never went back to clear.
What came back is the part that makes a vibe-coding leak different from an ordinary source leak. The payload included the project’s source code, the database credentials hardcoded into that code, the connected customer data those credentials unlock, and the complete AI chat history. For an app built by prompting, the chat history is not a side artifact. It is the build log, and founders paste secrets into it constantly: connection strings, third-party API keys, the contents of a .env they wanted the model to wire up, error dumps with live tokens in the stack trace. The leaked Supabase keys are the same keys whose blast radius we walked through in the RLS post. The chat history is where the next set was sitting in plain text. Leaking the conversation can out-leak the repository.
The reports arrived on day 19 and died in triage
Here is where the story stops being a normal regression. Researchers started reporting this through Lovable’s HackerOne program on February 22, nineteen days into the window. On March 3 a researcher filed it precisely, under the title “Broken Object Level Authorization on Lovable API leads to unauthorized access to user data and project source code.” Not a vague hunch. The CWE, the endpoint, the impact, handed over in the title.
It was closed as a duplicate, without escalation. Lovable’s explanation: “The decision to close the reports was based on internal static documentation and context we had provided to our HackerOne partners, which still described public project chat visibility as intended behavior.” That documentation had been written back when public chat visibility briefly was the intended behavior, and never updated after the hardening that made it a vulnerability. So every accurate report pattern-matched against a stale doc and got filed as noise.
This is the failure worth naming, because it is the one most teams reading this have a live copy of. Call it closed as intended: the authorization truth lived in a document, the document was wrong, and the people guarding the front door were reading it instead of the code. Bug bounty triage is usually outsourced, a vendor’s first-line analysts filtering noise against the runbook the customer handed them. Lovable shipped a major hardening in November and never updated that runbook, so when accurate reports came in, the triagers matched them to a document that still called the behavior intended and closed them as duplicates. The reports were not missed. They were received, read, checked against the recorded definition of correct, and rejected for disagreeing with it. Two copies of what “secure” meant had gone stale at once: the test suite that should have failed, and the triage doc that should have escalated. Neither was wrong about the rules as written. The rules as written were a year out of date.
It is the sibling of the failure in our Meta support-AI teardown. There the authorization decision sat inside a model you could talk out of it. Here it sat inside a document nobody talked back into date. Both are the same mistake at one remove: the gate was real, but it lived somewhere that drifted away from the truth, and no deterministic check sat behind it to catch the drift.
The fix took two hours, which is the indictment
When the researcher published a working proof on April 20, Lovable shipped a fix within about two hours. That speed is not the redemption it looks like. It is the evidence. A defect that takes two hours to close once someone with authority believes it was never blocked by engineering difficulty. It was blocked by nobody with authority believing it for 76 days. The state lived in the right place. The code change was small. The feedback path from “a stranger can read this” to “an engineer who can fix it” was the part that was broken, and no amount of patch speed at the end repairs the 76 days the signal spent getting routed to a closed ticket.
The fix itself: revert the regression so the project-messages endpoint enforces ownership again, and, the broader remediation, convert all historically public projects to private except Lovable’s own templates. That second step is the one that actually retires the cohort risk, because it stops relying on a per-request authorization check being perfect and removes the public flag that made pre-November projects reachable at all. Lovable also committed to opt-in remixing, retraining its HackerOne triagers, and reviewing access logs to notify owners whose projects were read by a non-owner during the window.
The response made the hole worse before it made it better
The same-day messaging is worth recording, because it is a case study in how not to hold an incident. The Register captured the arc: Lovable first said it “did not suffer a data breach” and framed the exposure as intended behavior, then attributed the confusion to unclear documentation about what “public” meant, then pointed at HackerOne for closing the reports. Three stances in a day, each technically defensible and collectively corrosive, because every one of them put the cause somewhere other than the regression. The post-mortem two days later was straighter, conceding that “our first public response was dismissive and failed to acknowledge the real concern.” The honest version was available the whole time. It just arrived fourth.
The “intended behavior” defense is the tell that the company’s own mental model had drifted to match the stale triage doc. They were not lying when they first said it was intended. They were reading the same outdated definition of correct that had closed every report. The exposure and the denial had a common root: the people guarding the door and the people speaking for the company were both reading a year-old answer to “what is a stranger allowed to read here.”
What we can’t confirm, and won’t assert
A few things are not pinned down, and a teardown that papers over them is doing the same thing Lovable’s triage did. The proof-of-concept reportedly pulled real names, LinkedIn profiles, and credentials from a Danish nonprofit’s live app; that specific detail rests on a single outlet’s reporting, so treat it as reported, not established. Lovable’s framing says reaching a project required its link, while the five-API-calls demonstration implies project identifiers were discoverable or could be walked. No source we trust resolves whether a stranger could enumerate projects or had to obtain links first, and the difference is the difference between a targeted leak and a bulk one. Lovable has published no count: not how many projects were read, not how many owners were notified, not how the log review concluded. Until those land, the honest scope is “every pre-November public project was reachable,” not “every one was taken.”
If you shipped on a platform, you inherited its regressions
The lesson for a founder is not “leave Lovable.” It is that putting your code on a managed platform moves a class of risk you can’t see and can’t patch. Your source, your keys, and your prompt history sit inside someone else’s authorization model, and when that model regresses you find out on the provider’s schedule, through the provider’s triage, calibrated to the provider’s possibly-stale idea of what’s intended. You can’t audit that boundary. You can only narrow what’s behind it. Two moves are in your hands today: rotate every credential that ever touched a Lovable project built before November 2025, because a leaked key stays leaked until it’s revoked and deletion is not revocation, and stop pasting live secrets into the prompt at all, because the chat history is a durable store you don’t control the access rules for. If you built on Lovable before November and you have not rotated those keys, the assumption to hold is that they’re public until you do.
The provider had the right protection, retired it by accident, and was told 19 days in by people who had reproduced it. The thing that failed last was not the code. It was the document that defined what the code was supposed to do, and the people downstream who trusted the document over the bug report. Check what yours says, and check who’s allowed to disagree with it.