Sample report: a full read of a Lovable and Supabase app
This is a full written read of a public, open-source app, shared to show how I read code and what I look for. It isn't one of my offers as it stands: if you're about to launch, start with the $149 Launch Blocker Check. The app is a small hobby project, but the checks are the ones I run on any app about to take real users: who can change what, what can cost you money, what breaks quietly, and what you'd lose if the database went.
I've removed the app's name, author and address, and I describe security problems that someone could exploit only by type, not by how to do it. In a paid check, each blocker comes with the exact file and line, what I saw, and the first step to fix it. I read the public code only; I didn't run the app or touch the live site. That's why some answers below are conditional. The $149 check reads code only too, so it marks the same kind of finding "Needs checking" and says what would settle it; a fixed-price job or Fix the list can settle it with your dashboard access and test logins.
| Prepared by | Bryce Watson |
| Delivered | September 22, 2026 |
| What I reviewed | The public code as it stood on one specific date: 2 database tables, 10 database migrations, 4 server functions, about 60 source files |
| Built with | Lovable; React, TypeScript, Vite; Supabase (database, sign-in, server functions, file storage); a paid web-scraping service |
| What the app does | A personal catalog: the public can browse it; only the owner is meant to change it |
| Access I had | The public code only |
| Access I didn't have | The Supabase dashboard, hosting, the scraping account, a test login, the live site. The $149 check doesn't use these either; a fix job can, with your read-only access. That's why several findings below are marked "Needs checking". |
3 How to read this
Severity: Critical fix before real users, High before launch or in week one, Medium next few weeks, Low when you're in that code, Note no action now.
Confidence: Verified (confirmed directly), Likely (the code clearly allows it, but I didn't trigger it), Needs checking (couldn't confirm with the access I had; the finding says what would settle it). Only findings I could confirm from the code alone are Verified, because I had no test login. With test accounts in a fix job, most "Likely" items become "Verified".
5 Findings
Summary: 12 findings. How serious the first two are depends on the setting in F-01, so in this public version they're ranked "depends on setting". In a paid check, each blocker also comes with the exact file and line and the first step to fix it (see F-01).
Security findings, summarized for this public sample
These four are why the verdict hinges on one setting. The good news is that each fix is small and the app's structure makes them easy to get right.
What F-01 would look like in a paid check (details blacked out here)
| Severity | Depends on [setting] |
| Confidence | Needs checking: the rules are certain from the code; the setting isn't visible without dashboard access |
| Where | [4 migration files, 9 lines]; [2 data files, 9 lines]; [sign-in code, 2 files] |
| What I found | The exact rules, quoted, and why each one is broader than "owner only" |
| Why it matters | What a stranger could do to your public pages, storage and bill, in plain words |
| How to check it yourself | The dashboard page and setting name, and what "safe" looks like there. No steps that touch the live app. |
| Recommended fix | The rule change, plus a prompt you can paste into Lovable |
| Effort | Small to medium. Quote item Q-01. |
↑ Back to findings
F-05 A bulk-update feature probably runs past the server's time limit and reports the wrong numbers
Medium Likely (the time limit depends on the plan: Needs checking) · Reliability · The bulk-update server function (loop and response) and the page button that calls it
What I found. One call works through up to 20 items one after another, each needing two slow page fetches plus a pause. That adds up to at least 110 seconds before any network time, close to or past the 150 seconds Supabase typically allows. When it's cut off, the page says "failed" even though some items were updated. When it finishes, the message reads a number the function never sends, so it shows "out of undefined", and it says "complete" after the first 20 even when more are left.
Why it matters. Confusing results and wasted money: a timed-out run has already paid for its fetches.
How to check it yourself. Run it once while watching the function's logs in Supabase.
Recommended fix. Do 5 items per call, have the page repeat until done and show progress, and fix the message.
Effort. Small. Quote item Q-04.
↑ Back to findings
F-06 Some scraped details can be silently wrong
Low Likely · Reliability, data quality · Three server functions' page-reading code
What I found. A duration is taken from the first "N min" anywhere on a fetched page, not from the labeled field. The bulk update takes the first search result whatever the edition. A price falls back to the first currency amount on the page, which may be a promotional banner.
Why it matters. Wrong values flow into the app's statistics with no sign they're guesses.
Recommended fix. Read from the labeled fields, match on year, and leave a blank rather than guess.
Effort. Small. Quote item Q-04 (same code as F-05).
↑ Back to findings
F-07 Outside calls have no time limit, and failures are mostly invisible
Low Likely · Reliability · Four calls to outside services across three functions; one background sync in the page
What I found. No outside call sets a timeout. Errors go only to function logs; one function sends raw internal error text back to the browser, and the background sync hides every failure.
Why it matters. When something breaks, the owner finds out only by noticing missing data.
Recommended fix. 15-second timeouts, generic error messages, and a small notice in the page when the sync fails.
Effort. Small. Quote item Q-03.
↑ Back to findings
F-08 The settings file is committed to the repository and not ignored
Low Verified (from the public repository) · Security, deployment · .env and .gitignore
What I found. The .env settings file is in the public repository. Today it holds only the project address and the key that every visitor's browser already receives, which Supabase designs to be public. I confirmed it's that public key, not the private key that bypasses all the rules, so nothing needs changing today. But the ignore file doesn't cover .env, so any real secret added later would be published with the next commit.
Why it matters. This is the finding that looks scariest and matters least. Automated scanners flag it loudly; the real question for this app was whether the database rules hold (F-01). Part of what you're paying for is knowing the difference.
Recommended fix. Ignore .env and keep the example file as a template, then confirm the builder still gets its settings.
Effort. Small. Quote item Q-05.
↑ Back to findings
F-09 No visible backup or export of the owner's data
Medium Needs checking · Data handling · Whole project; the delete and upload code
What I found. Everything lives in one database with nothing in the code to back it up or export it. Whether the plan includes restorable backups isn't visible to me. Deleting a parent item deletes everything nested under it. Deleted items leave their images behind in storage, which keeps growing.
Why it matters. Years of data entry could be lost to one bad change or mistaken delete.
How to check it yourself. In Supabase, open Database > Backups.
Recommended fix. Confirm backups, add a one-command export to a dated file, and clean up images on delete.
Effort. Small. Quote item Q-06.
↑ Back to findings
F-10 Some personal details are public by design
Note Verified (from the database rules) · Privacy
What I found. One public list shows prices, shops and purchase dates, plus the owner's internal account ID on every row. A third-party username is hard-coded in one function. No one else's personal data is stored.
Why it matters. Fine for a showcase, as long as it's a choice. Purchase dates are easy to overlook.
Recommended fix. None required; hide purchase dates from public reads if preferred.
↑ Back to findings
F-11 Code health: the next change is riskier than it needs to be
Low Verified (from the repository) · Code health, deployment
What I found.
- Owner-only buttons show for any signed-in account.
- The only test is a placeholder that checks
true equals true, and nothing runs checks on each change.
- The project records its outside code in three different, competing ways, so what the builder installs can differ from what a developer's laptop installs.
- The server functions pull in outside code without locking the version, so an update could change their behavior overnight.
- Three page files are 18 to 25 KB each, mixing data, filters and layout.
Why it matters. Nothing breaks today, but every edit is harder to check, and the access rules in F-01 have no test to catch a regression.
Recommended fix. One lockfile, a small automatic check on every push (lint, tests, build), a test that a second account can't write, and owner checks in the page.
Effort. Small to medium. Quote item Q-05.
↑ Back to findings
F-12 Outside libraries weren't scanned
Note Needs checking
What I found. I didn't install anything, so I didn't run a vulnerability scan. The main libraries are recent. On a fix job I'd run the scan or read your GitHub Dependabot alerts.
↑ Back to findings
What's already done well
- The private database key is used only on the server, and only on the caller's own records. It never reaches the browser.
- Every server function checks the caller's login before doing anything that costs money.
- Both page-fetching functions only fetch from an approved list of sites, which blocks the usual way this feature gets abused.
- Every write rule is tied to the signed-in account; the gap is only that "signed in" isn't the same as "owner".
- A later change removed public listing of the file bucket.
6 Fixed quote
These are the prices I put on this sample app's fixes when I wrote it; yours come from your own code. Every finding that needed work got a price. You choose which to fix. Once you approve the written scope, the price only changes if we both agree to a change in writing.
How it would go today: if a $149 check had named these as blockers, Fix the list would price the ones you chose as one list, with one fixed price and the time it takes. When I wrote this sample, the five fixes that matter before launch (Q-01 to Q-04 and Q-06) came to $1,050 in 5 business days. A check focuses on security, sign-in and payments, so its list would usually be shorter than this report.
Before each fix, we agree on a plain test of "done". If a fix fails that test on the exact version of the code I handed over, within 90 days of delivery, I fix it free.
The test of "done" for each fix
- Q-01: The dashboard setting is confirmed. A second test account gets a permission error on every change and upload, through the app and directly through the database. The owner still can. The bucket rejects oversize and non-image files.
- Q-02: The database rejects unsafe values in that field, and the page never renders one.
- Q-03: Only the owner can call the paid functions, up to a daily cap you choose. Oversized input is rejected. Outside calls give up after 15 seconds with a generic error.
- Q-04: A 60-item bulk update finishes from one click with a correct progress count and no timeout. Durations on 5 sample pages match the labeled field.
- Q-05: Settings file ignored and the builder still works; one lockfile; automatic checks run on every push; a test proves a second account can't write; owner buttons hidden from others.
- Q-06: Backups confirmed in writing; one command exports all data to a dated file; deleting an item removes its image.
The warranty doesn't cover later changes to the code, including your AI builder rewriting it, new features, or outages at Supabase, Stripe or your host. With Fix the list, you approve the list and its one price before you pay.