[{"data":1,"prerenderedAt":269},["ShallowReactive",2],{"research-en":3},{"id":4,"title":5,"aside":6,"body":9,"coverAlt":242,"description":243,"extension":244,"faq":245,"lede":261,"meta":262,"navigation":263,"path":264,"published":265,"seo":266,"stem":267,"updated":265,"__hash__":268},"research_en\u002Fresearch\u002Fvibe-coded-app-security-2026.md","The State of Vibe-Coded App Security 2026",[7,8],"Outside view only. We read what a visitor's browser already reads: the code the app ships, the headers it sends back, the endpoints named in its own code. No logins, no password guessing, nothing downloaded.","No app is named here or in the data file. Each figure carries the method that produced it, and the file at the bottom holds every aggregate in one table if you want to check the arithmetic.",{"type":10,"value":11,"toc":224},"minimark",[12,17,21,24,27,31,34,37,40,44,47,50,53,56,60,63,66,70,73,76,79,82,86,89,92,95,98,102,105,108,111,125,129,132,135,138,142,214,218,221],[13,14,16],"h2",{"id":15},"six-numbers-that-tell-the-story","Six numbers that tell the story",[18,19,20],"p",{},"These six figures are the report. Everything after them is the base each one sits on, and how we got it.",[22,23],"research-key-findings",{},[18,25,26],{},"Platform defaults fail almost everywhere, and most of those failures are moderate. The settings only the person who built the app can change fail far less often, and they are the ones that put somebody's data on the open internet.",[13,28,30],{"id":29},"how-these-apps-grade-a-to-f","How these apps grade, A to F",[18,32,33],{},"Reeve grades the way a school report does. Findings knock points off 100 by severity. Then the worst single finding puts a ceiling on the result: one critical finding anywhere and the app can score no higher than D, one high finding and no higher than C.",[35,36],"research-grades",{},[18,38,39],{},"Most apps land in A or B. The D and F band is small. Those are the apps where a stranger can already read somebody's rows.",[13,41,43],{"id":42},"what-we-checked-and-how-many-apps-failed-each","What we checked, and how many apps failed each",[18,45,46],{},"Each check asks one question about the app from outside, and each one is counted over the apps where it got an answer. If a check could not reach an app, that app drops out of that check's base rather than counting as clean. The scan prints \"Couldn't check\" for those, and this page does the same.",[48,49],"research-checks-table",{},[18,51,52],{},"Two rows read worse than they are. Security headers are instructions the hosting platform sends to the browser, and a missing one leaves one class of attack with one fewer obstacle in front of it. A published source map is the app's original code sitting beside the compiled version: it shows an attacker where to look, and hands over nothing on its own.",[18,54,55],{},"A row can also read severe because a handful of apps in it shipped something serious while the rest shipped something ordinary. The data file breaks each row out by key type so you can see which is which.",[13,57,59],{"id":58},"more-than-half-of-checkable-supabase-apps-leak-table-data","More than half of checkable Supabase apps leak table data",[18,61,62],{},"This is the number we kept coming back to.",[18,64,65],{},"A Supabase project is open to the internet by design. The app running in the browser talks to the database directly, using a key that is meant to be public, and the thing standing between a stranger and the rows is Row Level Security: a rule on each table. Switch that rule off, or write a policy that lets everyone in, and any table in the project can be read with one request and no password.",[67,68],"research-stat",{"id":69},"supabase",[18,71,72],{},"Fewer than half the apps that name a Supabase project can be checked at all. Some send their database traffic through their own backend, where a check from outside cannot follow, so we say nothing at all about those. The share above is over the apps that answered.",[67,74],{"id":75},"rlsSplit",[18,77,78],{},"The split is the useful part. A readable table called settings or content is a mistake. A readable table called users, profiles or orders is other people's personal data on the open internet, and whoever built the app almost certainly believes it is private.",[18,80,81],{},"The check never reads a row. It asks the database how many rows would be visible and reads the count back out of a response header. That is enough to tell a table with an RLS rule from a table without one, and the check goes no further.",[13,83,85],{"id":84},"the-same-nine-checks-split-by-builder","The same nine checks, split by builder",[18,87,88],{},"Each app is grouped by the builder it was published with, and measured against the other apps on that builder.",[90,91],"research-builders-table",{},[18,93,94],{},"Most of what this table compares is defaults. Nobody picks Lovable or Bolt for its security headers. The platform sets those for every app it hosts, so one change there moves a whole column of this table.",[18,96,97],{},"Two limits before anyone quotes a row. We attribute apps by publish domain, so an app that moved to a custom domain sits outside this table entirely, and those are often the more finished ones. And a builder that routes database traffic through its own backend cannot be checked from outside at all. A blank there means we could not measure, and says nothing about how safe those apps are.",[13,99,101],{"id":100},"two-kinds-of-findings-two-kinds-of-fixes","Two kinds of findings, two kinds of fixes",[18,103,104],{},"Findings on this page fall into two groups, and the two need fixing by different people.",[106,107],"research-controls",{},[18,109,110],{},"The first group is a product decision at a company. One edit to a hosting default moves every app on that platform at once, which is why those rows come out so uniform and why they are the cheapest findings here to fix.",[18,112,113,114,119,120,124],{},"The second group is on the person who built the app. No hosting platform can know which of your tables are meant to be public, or which key belongs on a server. Every critical finding in this report sits in that group. The per-builder ",[115,116,118],"a",{"href":117},"\u002Fis","safety guides"," and the ",[115,121,123],{"href":122},"\u002Fchecklist","launch checklist"," both start there.",[13,126,128],{"id":127},"what-vibe-coders-get-right","What vibe-coders get right",[18,130,131],{},"We counted what passed, over the same apps.",[133,134],"research-clean-rates",{},[18,136,137],{},"Generic scanners usually get the last one wrong. A publishable key in the frontend (a Supabase anon key, a Stripe publishable key) belongs there: it is designed to be read, and the protection lives in the rules behind it. Flagging it sends somebody off to change a setting that was already correct.",[13,139,141],{"id":140},"how-we-measured-and-where-the-limits-are","How we measured, and where the limits are",[143,144,145,160,175,194],"research-method",{},[146,147,149,154,157],"template",{"v-slot:origin":148},"",[150,151,153],"h3",{"id":152},"where-the-apps-came-from","Where the apps came from",[18,155,156],{},"Three lists, merged and de-duplicated: apps people posted on X and Reddit, apps launched on Show HN, and apps we enumerated from the builders' own publish domains. The publish domains are the bulk of the sample and the only list that scales. The other two can only find an app somebody chose to post about.",[18,158,159],{},"Dead hosts came out first, and we judged that by reading the page itself. A deleted deployment often still answers a perfectly cheerful 200 OK.",[146,161,162,166,169,172],{"v-slot:denominator":148},[150,163,165],{"id":164},"the-denominator-rule","The denominator rule",[18,167,168],{},"Every percentage here is over the apps that check could answer, never over the sample as a whole. A check that could not reach an app leaves that app out of its own base, and it is never counted as clean. The two numbers beside each bar are that base, so anyone can see which one they are reading.",[67,170],{"id":171},"inconclusive",[18,173,174],{},"Grades are counted over a smaller number for the same reason. An app that returned too little to classify is dropped from every figure above rather than graded on a guess.",[146,176,177,181],{"v-slot:scope":148},[150,178,180],{"id":179},"what-we-did-and-did-not-do","What we did, and did not do",[182,183,184,188,191],"ul",{},[185,186,187],"li",{},"Passive reads only: the requests a visitor's browser already makes, plus the public endpoints the app itself points at.",[185,189,190],{},"The database check takes a row count from a response header. We never read a row, downloaded a file, logged in or guessed a password.",[185,192,193],{},"No app is named, here or in the data file. A list of apps paired with their weaknesses is a target list, and we do not keep one in publishable form.",[146,195,196,200],{"v-slot:limits":148},[150,197,199],{"id":198},"limits-worth-knowing","Limits worth knowing",[182,201,202,205,208,211],{},[185,203,204],{},"The sample leans towards newer apps. An app that has moved to a custom domain cannot be tied to a builder from outside, and those are often the more established ones.",[185,206,207],{},"Colour in a row marks the worst case we saw, which can be one app among hundreds. The data file breaks each row out by exact key type.",[185,209,210],{},"This is a three-day window in August. An app fixed the following week still counts here as it stood on the day we looked.",[185,212,213],{},"An automated external check is not an audit. Absence of findings is not a guarantee.",[13,215,217],{"id":216},"use-these-numbers","Use these numbers",[18,219,220],{},"Journalists, researchers and anyone writing about this are welcome to reuse the data with attribution. The file below holds every aggregate on this page in one flat table, and names no app.",[222,223],"research-cite",{},{"title":148,"searchDepth":225,"depth":225,"links":226},3,[227,229,230,231,232,233,234,235,241],{"id":15,"depth":228,"text":16},2,{"id":29,"depth":228,"text":30},{"id":42,"depth":228,"text":43},{"id":58,"depth":228,"text":59},{"id":84,"depth":228,"text":85},{"id":100,"depth":228,"text":101},{"id":127,"depth":228,"text":128},{"id":140,"depth":228,"text":141,"children":236},[237,238,239,240],{"id":152,"depth":225,"text":153},{"id":164,"depth":225,"text":165},{"id":179,"depth":225,"text":180},{"id":198,"depth":225,"text":199},{"id":216,"depth":228,"text":217},"A dark share card with the Reeve mark, the report title, and the number of live apps in the scan set as one large figure.","We ran the same nine external checks on tens of thousands of live apps built with Lovable, Bolt, v0, Replit and Base44. Here is what came back.","md",[246,249,252,255,258],{"q":247,"a":248},"Can you tell me which apps had which problems?","No. Counts and percentages only, never names, and the data file holds exactly what the page shows. A list of apps paired with their weaknesses is a target list, and we will not produce one.",{"q":250,"a":251},"My app might be in this data. What should I do?","Scan it yourself. It is the same nine checks, it takes about twenty seconds, and it shows your grade and what a stranger can already see. Nothing on this page identifies anyone, so that is the only way to find out where you stand.",{"q":253,"a":254},"Is a readable database table really that serious?","If anyone on the internet can pull those rows with one request and no password, yes. A readable settings table is sloppy. A readable users or orders table is a data breach that nobody has noticed yet.",{"q":256,"a":257},"Why do you count publishable keys as fine?","A Supabase anon key is supposed to be public. The protection is the database rules behind it, not the key itself. Tools that flag those keys train people to ignore the next warning, which may be the one that matters.",{"q":259,"a":260},"Will you run this again?","Yes. The scans keep running and the next edition follows them. The data file records the window these numbers came from, so an old copy cannot pass for a current one.","We looked for live apps built with Lovable, Bolt, v0, Replit or Base44, then ran each one through the same nine checks anybody can run free on the Reeve home page. This page is what came back.",{},true,"\u002Fresearch\u002Fvibe-coded-app-security-2026","2026-08-19",{"title":5,"description":243},"research\u002Fvibe-coded-app-security-2026","fSqBLvqn4ypbgFcMUSkxG-pdZ32UnHYm4HLOnFvmD2Y",1787826048204]