Sample output · free
This is what you get back.
A plain report of what in your app would leak data, lose data or break under real users, and what each problem would cost you. Free. Below: an invoicing app built in Lovable on Supabase, modelled on a real case, names taken out. Type “check my app” into Claude Code, Cursor or Codex and you get a page like this one, for your own app.
The free report · run on the owner's machine
Invoicing SaaS for small agencies
at risk
Anyone can read every customer's invoices. Do not put more customers on it yet. 1.6 out of 5, with 23 serious problems among 76.
Your real page is private, behind an access code, and kept 7 days · your source, your data and your secrets never reach us
Executive summary
This app is not ready for more customers. Any visitor can read every customer's invoices, a key that unlocks the whole database ships to every browser, and nothing records who read what.
It is weakest where it hurts most: security, how the data is stored, and what a client or regulator would ask about. Nothing scored 4 out of 5 or better.
Overall 1.6 out of 5. Of 76 problems found, 4 are critical and 19 high; the four critical ones are below.
One area, performance under load, could not be judged without load-test results; that is not the same as passed.
Critical findings
What is wrong, where it is, and what it costs you if it stays.
3 tables carrying an owner or tenant column have no row-level security in force: invoices, clients, line_items. The other 8 tables have policies.
For those tables isolation rests entirely on every query remembering to narrow by owner, so the single query that forgets returns every customer's rows to whoever asked. Here nobody has to forget: the public key every browser receives reads all three tables through the REST API, signed in or not.
Evidencesrc/integrations/supabase/types.ts:214
VITE_SUPABASE_SERVICE_ROLE_KEY, a credential that bypasses row-level security, is referenced at src/integrations/supabase/client.ts:12, inside code that ships to the browser.
Anyone who opens the site can read it out of the bundle and then read and write every row in every table directly, with no account, no policy applying and no trace in the application's own logs. Policies written later would not change that while this key is valid.
Evidencesrc/integrations/supabase/client.ts:12
At supabase/functions/send-invoice/index.ts:23 the org_id that scopes the query is read from the request body, not from the session, and the query runs with the service-role key.
Changing one value in a request returns another customer's records: no exploit, no stolen credential, only an identifier the caller already sees in their own URLs. This function then mails the invoice PDF to whatever address the request names.
Evidencesupabase/functions/send-invoice/index.ts:23
The schema declares no audit, activity or event table anywhere: 11 tables, no trigger that records a change, and the platform's API logs record that a request happened, not which rows it returned.
Nothing records what happened to a row, so an incident, a dispute or a regulator's question about a specific record has no answer after the fact. In a breach, the 72-hour notice has to say that every record since launch may have been read.
Evidencesupabase/migrations/20260520101500_init.sql:1
The full detail
Everything below is for your developer or your coding tool: every serious problem, where it sits in the code, and how each area of the app scored. You do not need to read it to know where you stand.
High findings (19)
The next tier down. Medium and low findings (53) are counted per area below; the real report lists every one.
- highSecurity Posture
The function that mails invoice PDFs has no rate limit on any path through the handler.
One script with a loop drains the email quota or gets the sending domain blocked, and the first notice is customer mail that stops arriving.
supabase/functions/send-invoice/index.ts:9 - highSecurity Posture
Sign-in has no attempt limit or lockout of any kind: not per account, not per network address.
Password guessing against one agency's account is never stopped, however long it runs, and nothing records that it happened.
src/pages/Login.tsx:41 - highSecurity Posture
The storage bucket that holds invoice PDFs is public.
Anyone holding a file URL, and a forwarded email is enough, downloads the invoice with no account and no record of the download.
supabase/migrations/20260611094210_storage.sql:7 - highSecurity Posture
The dependency audit reports 2 high-severity advisories in packages the build ships.
Known, published weaknesses sit in the running app with no record that anyone has weighed them.
package-lock.json - highAPI Design
JWT verification is off for the send-invoice function.
The function answers callers who are not signed in, so the org_id gap above is open to anyone without an account.
supabase/config.toml:14
Show the other 14 high findings
- highCompliance Signals
No route returns a user the personal data held about them, while 4 tables hold it.
A GDPR access request is answered by hand from the production database, or not within the month the law allows.
src/App.tsx:38 - highCompliance Signals
The product has no path by which an account and its data are removed.
An erasure request can only be honoured by deleting rows in production by hand, which is itself an unlogged change to live customer data.
src/App.tsx:38 - highData Architecture
Invoice amounts, VAT and totals are stored as floating-point numbers.
Totals drift by a cent against their line items, and the invoice that does not add up is the one an agency's client disputes.
src/integrations/supabase/types.ts:231 - highData Architecture
An invoice and its line items are written in separate requests with nothing tying them together.
A dropped connection between the two leaves an invoice with no lines, or lines with no invoice, and nobody is told.
src/hooks/useCreateInvoice.ts:57 - highTesting Maturity
The repository has no continuous integration of any kind.
Nothing runs between a change and production, so the tests that exist decide nothing about what ships.
package.json:6 - highTesting Maturity
No test asserts that one agency reading invoices with another agency's id gets nothing back; 9 tests were searched.
Isolation has never been checked, so the first proof that it fails is a customer looking at someone else's invoices.
src/lib/format.test.ts:1 - highTesting Maturity
The app has 14 route groups and no browser-level test exercising any of them.
Nothing proves the screen a customer uses submits, saves and navigates; two working pieces wired together wrongly ship green.
src/App.tsx:22 - highObservability & SRE
No error tracking is wired in, in the browser or in the edge functions.
Failures are seen by no one but the user who hit them, and incidents are discovered through support email, days late.
src/main.tsx:1 - highObservability & SRE
Nothing alerts when the payment webhook fails.
A subscription that stops renewing looks exactly like a customer who is still paying, until the month-end numbers do not add up.
supabase/functions/payment-webhook/index.ts:1 - highInfrastructure & DevOps
No database restore has ever been performed or written down.
The day a restore is needed is the day anyone learns how long it takes and whether yesterday's invoices are in it.
README.md:1 - highInfrastructure & DevOps
One Supabase project serves development, previews and production.
Every test invoice and every experiment runs against the same tables real customers read.
src/integrations/supabase/client.ts:5 - highRelease & Change Management
The invoices, clients and line_items tables exist in no migration in the repository; they were made by hand in the SQL editor.
The production schema cannot be rebuilt, reviewed or rolled back from the code, and a new environment starts without the three tables that matter.
supabase/migrations/20260520101500_init.sql:1 - highRelease & Change Management
There is no rollback path beyond publishing again.
A bad release stays live for as long as it takes to find and rebuild the last good version.
package.json:6 - highCode Quality
The payment webhook catches every error, logs it and answers 200.
The payment provider records the event as delivered, so a failed subscription update is never retried and the account's plan quietly goes wrong.
supabase/functions/payment-webhook/index.ts:48
Score by area
Each area is scored 1 to 5: 1 weak, 3 fair, 5 excellent. The areas that decide whether data is safe count more toward the overall score.
- Architecture Assessment2.04 findings — 3 medium · 1 low
- Code Quality2.05 findings — 1 high · 3 medium · 1 low
- Security Posture1.011 findings — 3 critical · 4 high · 3 medium · 1 low
- Data Architecture1.06 findings — 2 high · 3 medium · 1 low
- Infrastructure & DevOps2.06 findings — 2 high · 3 medium · 1 low
- API Design1.05 findings — 1 high · 3 medium · 1 low
- Frontend Architecture3.01 finding — 1 medium
- Dependencies & Supply Chain2.03 findings — 2 medium · 1 low
- Scalability & Performance2.04 findings — 3 medium · 1 low
- Technical Debt Inventory2.03 findings — 2 medium · 1 low
- Observability & SRE1.05 findings — 2 high · 3 medium
- Testing Maturity1.05 findings — 3 high · 2 medium
- Documentation & Developer Experience2.01 finding — 1 medium
- Accessibility & Internationalization2.03 findings — 2 medium · 1 low
- Release & Change Management1.05 findings — 2 high · 2 medium · 1 low
- Compliance Signals1.08 findings — 1 critical · 2 high · 4 medium · 1 low
- Cost Efficiency2.01 finding — 1 medium
Where the risk sits
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| 5 | · | · | · | · | 2 |
| 4 | · | · | · | · | 2 |
| 3 | · | · | 41 | 19 | · |
| 2 | · | 12 | · | · | · |
| 1 | · | · | · | · | · |
All 76 problems, by how likely they are to bite (rows, 5 at the top: already happening) and how much harm they would do (columns, 5 on the right: a breach, lost data, an outage or a regulator). The top right corner is where incidents come from.
Problems that make each other worse
Weak spots in different areas that, together, turn a small incident into a big one.
- Complex code where the tests are thinCode that is hard to follow sits where tests are thin. Every change there can break behaviour without anything catching it before users do, and this is where delivery slows down first. Here: Testing Maturity 1/5, 5 findings; Code Quality 2/5, 5 findings.
- Weak access control over personal dataWeak access control meets personal data that is not well tracked or audited. A breach is both more likely and harder to establish, contain and report within the legal deadlines. Here: Security Posture 1/5, 11 findings; Compliance Signals 1/5, 8 findings.
- Silent failure in operationsInfrastructure that is hard to operate meets signals nobody watches. Failures reach users first, are noticed late and are diagnosed from guesswork. Here: Observability & SRE 1/5, 5 findings; Infrastructure & DevOps 2/5, 6 findings. The weak sides meet in README.md.
- One file, several risksSerious problems in Code Quality and Observability & SRE all point at the same file,
supabase/functions/payment-webhook/index.ts, so one weakness there carries several kinds of risk at once. - One file, several risksSerious problems in Security Posture and Data Architecture all point at the same file,
src/integrations/supabase/types.ts, so one weakness there carries several kinds of risk at once. - One file, several risksSerious problems in Security Posture and Infrastructure & DevOps all point at the same file,
src/integrations/supabase/client.ts, so one weakness there carries several kinds of risk at once.
What could not be evaluated
Performance (load tested): no load-test results were supplied, and the review never runs the app, so this area is left unscored rather than scored low. Not evaluated is not the same as passed.
What this report leaves out, on purpose
The fix. Every finding names the gap, where it is and what leaving it costs. None of them says how to close it.
Also left out: file contents, environment values and secrets. The server never sees the source; only the result page is kept.
Free · runs on your machine
Get this report for your own app.
One line in the coding tool you already use. Then type “check my app” and you get this page for your own app. It runs in your coding tool, on your machine. What reaches us: counts, rule names and file paths with line numbers. Never your source, your data or a secret value. Your report page is kept 7 days behind an access code. Free, and it stays free.
Already at your laptop? Paste one of these
claude mcp add --transport http prodready https://mcp.prodready.si/mcp --scope user
codex mcp add prodready --url https://mcp.prodready.si/mcp
npx -y mcp-remote https://mcp.prodready.si/mcp
https://mcp.prodready.si/mcp
Settings → Connectors → Add custom connector, paste the URL.
Then type check my app. Every host's native config: all install lines →
Next: the SDLC
The report finds the problems. The prodready SDLC is how they get solved: the way serious software companies have shipped for the last twenty years. Plan every change, check it, have it reviewed, test it, and only then ship it. Your own coding tool follows that process change by change, working toward an app you can put real customers, real money and real data on.