Red Center Studio
Back to AI-Built App Audit

Example only. This is an example report on a fictional app, not a real customer. Acme Class Bookings and Acme Yoga do not exist. The findings are the kinds of problems the audit looks for, written to show what a real report contains and how it reads. No real data, people or incidents are described.

Example report / Fictional app

Sample AI-Built App Audit report, on a fictional app.

What you get from the AI-Built App Audit, shown on a made-up booking app: a plain-English verdict first, every finding ranked by risk with what we saw, why it matters and the fix, then a fix plan in order.

Verdict (example)

Not ready for real members yet, but it does not need a rebuild. The problems are in data rules, keys and server checks, not in the app's design. Fix the critical findings before anyone else signs up, and the high findings before taking payments. 10 findings: 2 critical, 2 high, 3 medium and 3 low.

Report details

The app and how it was tested.

App
Acme Class Bookings (fictional)
What it does
A class booking app for a fictional yoga studio, Acme Yoga. Members sign up, book and pay for classes, and upload a signed waiver form. Instructors see who is booked into their classes. An admin manages classes, members and refunds.
Built with
Built with an AI app builder: a React front end, a hosted Postgres database with row level security, serverless functions for payments and email, and the builder's own hosting on a custom domain.
Roles tested
Visitor (signed out), Member, Instructor and Admin, each tested with its own test account.
Access used
Read access to the code, a test account for each role and read access to the database settings. Nothing in the app was changed.
Status
Fictional example, written to show the report format
How findings are ranked

From critical to low.

LevelWhat it means
CriticalSomeone could see or change data that is not theirs, or take over the app, today. Fix before anything else.
HighA serious gap that needs a little more to exploit, or that breaks money or sign-in. Fix before launch.
MediumA real problem that will hurt users or costs as the app grows. Fix in the first weeks after launch.
LowGood practice and housekeeping. Fix when convenient.
What was checked

Every area, tested as each role.

AreaHow it was checkedResult
Data access rulesTested as each role, by reading and writing through the database API directly, not only through the app's screens.Finding 1
Exposed secretsSearched the browser code, the repository history and public settings for keys and tokens.Finding 2
Sign-in, roles and admin areasSign-up, sign-in, password reset, role changes and the admin pages.Findings 4 and 5
Payments, webhooks and server functionsThe payment webhook, the booking and refund functions, and the contact form.Findings 3 and 6
Code, errors and dependenciesCode structure, error handling and the packages the app depends on.Findings 8 and 9
Hosting, domain, backups and running costsCustom domain, file storage, database backups and what the app will cost to run.Findings 7 and 10
Findings

Every finding, riskiest first.

Each finding says what we saw, why it matters and the fix, written so the owner or their AI builder can follow it.

Finding 1 / Data access rulesCritical

Any signed-in member can read every booking

What we saw
The bookings table has row level security switched on, but its read policy allows any signed-in user. Signed in as a test member, we called the database API directly and received every member's bookings, with names, email addresses and phone numbers. The app's screens hide this; the database does not.
Why it matters
Anyone who creates a free account can download the whole member list. That is a privacy breach the first time it happens, and the app gives no sign that it has.
The fix
Replace the read policy with three: members read rows where the booking's user is them, instructors read bookings for their own classes, and admins read everything through a role checked in the database. Then repeat the test as each role and confirm a member sees only their own rows.
Size of the change
Small: a few policies and a test per role
Finding 2 / Exposed secretsCritical

The full-access database key is in the browser code

What we saw
The database's service key, which skips every row level security rule, is set as a public environment variable and appears in the JavaScript sent to every visitor. It is also in the repository history.
Why it matters
Anyone who opens the browser's developer tools can copy the key and read, change or delete any data, including other members' waiver forms. Fixing the data rules does not help while this key is public.
The fix
Rotate the service key now so the copied one stops working. Move the two calls that used it into server functions that keep the key as a server-side secret, and leave only the public key in the browser. Treat the old key as leaked even after it is removed from the code.
Size of the change
Small to medium: key rotation plus two server functions
Finding 3 / Payments, webhooks and server functionsHigh

The payment webhook does not check who sent it

What we saw
The server function that marks a booking as paid accepts any request with a booking ID. It does not verify the payment provider's signature, check the amount, or ignore an event it has already handled.
Why it matters
Someone could mark their own bookings as paid without paying, and a repeated event could book a class twice.
The fix
Verify the provider's signature with the webhook signing secret before doing anything, check the amount and currency against the booking, and record each event ID so a repeat is ignored.
Size of the change
Small: one function
Finding 4 / Sign-in, roles and admin areasHigh

Members can make themselves admin

What we saw
The profile update allows every column of the member's own profile to be changed, including the role column. A test member set their role to admin through the database API and could open the admin pages.
Why it matters
One request turns any member into an admin, with access to refunds and every member's details.
The fix
Keep roles out of the member-editable profile: move them to a table only admins can write, or block changes to the role column in the update policy. Check the role on the server for every admin action, not only when the admin page loads.
Size of the change
Small: one policy change and server checks on admin actions
Finding 5 / Sign-in, roles and admin areasMedium

Password reset links point at the builder preview

What we saw
The sign-in settings list the builder's preview address as the site address, and the custom domain is missing from the allowed redirect addresses. Reset and confirmation emails send members to the preview, where the link fails.
Why it matters
Members who forget their password cannot get back in, and new members cannot confirm their email, so they ask for help or leave.
The fix
Set the site address to the custom domain, add it to the allowed redirect addresses, and test sign-up, email confirmation and password reset end to end on the live domain.
Size of the change
Small: settings and a test
Finding 6 / Payments, webhooks and server functionsMedium

No rate limit on the contact form or sign-up

What we saw
The contact form writes a row and sends an email on every request, and sign-up has no limit on attempts. Neither has a bot check.
Why it matters
A script can fill the database with junk, run up email sending costs and get the domain marked as a spam sender.
The fix
Add a per-address rate limit to the contact and sign-up functions, a hidden trap field, and a bot check that real people do not have to solve.
Size of the change
Small to medium
Finding 7 / Hosting, domain, backups and running costsMedium

Waiver forms are stored in a public bucket

What we saw
Uploaded waiver forms are saved in a public storage bucket with predictable file names, so a file opens for anyone who has or guesses its address.
Why it matters
Waiver forms hold personal and health details. They should never be readable without signing in.
The fix
Make the bucket private, serve each file through a short-lived signed link after checking the viewer is that member or an admin, and rename existing files so old addresses stop working.
Size of the change
Medium: storage rules, a server function and a one-off file move
Finding 8 / Code, errors and dependenciesLow

Raw database errors are shown to members

What we saw
When a booking fails, the app shows the database error message as it is, including table and column names.
Why it matters
It confuses members and tells an attacker how the database is laid out.
The fix
Show a plain message such as "That class is full. Pick another time.", and log the full error on the server instead.
Size of the change
Small
Finding 9 / Code, errors and dependenciesLow

Outdated packages with published security advisories

What we saw
Several packages are a major version behind, and the package manager's audit lists advisories for two of them.
Why it matters
Known problems in old packages are the easiest ones for attackers to find, and big version jumps get harder the longer they wait.
The fix
Update the packages with advisories now, commit the lock file, and check updates once a month.
Size of the change
Small
Finding 10 / Hosting, domain, backups and running costsLow

Backups have never been restored

What we saw
The database plan includes daily backups, but no restore has been tried and nobody owns the job. The current plan covers the expected number of members.
Why it matters
A backup that has never been restored may not work when it is needed.
The fix
Restore last night's backup into a test project once, write down the steps, and repeat it every few months.
Size of the change
Small
What is working

Worth keeping.

  • Passwords are handled by the database's own sign-in service, not stored by the app.
  • The custom domain serves every page over HTTPS.
  • The booking screens are clear on a phone, and the code is split into sensible components that are easy to fix in place.
Fix plan

What to change, in what order.

  1. Today. Findings 1 and 2. Rotate the service key and close the bookings read policy. Nothing else matters until these two are done.
  2. Before taking payments. Findings 3 and 4. Verify the payment webhook and take roles out of the member profile.
  3. Before launch. Findings 5, 6 and 7. Fix the sign-in addresses, add rate limits and make the waiver bucket private.
  4. In the first month. Findings 8, 9 and 10. Friendly error messages, package updates and a test restore.

Fix, rework or rebuild?

Fix in place. Every finding can be fixed in the current app with the current builder, and none of them needs the app rebuilt. If Acme Yoga later wants the app in its own Cloudflare account, that is a separate decision, not a fix.

A real report also comes with a short walkthrough recording of the most important findings, showing each one in the app.

After the report

Getting the fixes done.

Each finding comes with its fix, written so you or your AI builder can apply it. If you would rather have the work done for you, the next paid steps are part of App Rescue and Move to Cloudflare: Rescue in place, fixed $999 USD (sign-in, SEO and indexing, security and stability, with your app staying where it is), or Move to Cloudflare as owned code, from $2,999 USD with a fixed quote first.

If we find nothing worth fixing, you get a full refund. Book a fix within 30 days and the fee is credited against it.

Reminder: this whole page is an example on a fictional app. Acme Class Bookings, Acme Yoga and every finding above are made up to show the report format. A real report covers your own app and is written for you.