Sample report. The app and every finding are invented. The page was produced by the same code that writes real reports, so this is what you receive. Back to the App Audit

App Audit · Impossible Means Go

What we found in your app's code

sample-owner/member-portal · prepared for Sample Coaching Co. · read on 26 October 2026

We found 34 problems in your app.

23 are serious: 9 critical and 14 high. Fix these first.

Critical
Someone could get in, take data, or cost you money now. Fix before anything else.
High
A real hole, or a wrong result that will reach customers. Fix this week.
Medium
Breaks under normal use, or leaves you blind when it does.
Low
Worth fixing when you next work on that part.

Fix these first

#SeverityWhat's wrongHow to fix it
1Critical Your Stripe secret key is still in the project's history
src/config.ts (removed in commit 4e1a9c2)
Create a new secret key in Stripe, put it in your hosting settings instead of the code, then delete the old key in Stripe.
2Critical Your database master key is sent to every visitor's browser
.env (VITE_SUPABASE_SERVICE_KEY), used in src/lib/admin.ts line 6
Create a new service key in Supabase, keep it only on the server, and delete the old one.
3Critical Your OpenAI key is in the code every visitor downloads
src/lib/ai.ts line 3
Create a new key, call the AI only from a server function, and delete the old key.
4Critical Any signed-in member can read every other member's records
src/lib/supabase.ts line 14
Turn on row level security for the members table and add a rule that each member reads only rows where user_id matches their own.
5Critical Anyone signed in can open any order by changing the number in the address
api/orders/[id].ts line 8
Load the order only if it belongs to the signed-in member.
6Critical The chat assistant will hand any member another member's records
api/chat.ts line 18
Give the chat only the signed-in member's own rows, chosen on the server.
7Critical Anyone can sign up as an admin
api/signup.ts line 15
Save only the fields sign-up needs, and set the role on the server.
8Critical Members' uploaded files sit in a public folder
supabase/migrations/0003_storage.sql line 4
Make the folder private and hand out short-lived links only to the member who owns each file.
9Critical Card numbers are saved in your own database as plain text
src/pages/Checkout.tsx line 51
Stop saving card details anywhere. Let Stripe's own form collect them, and delete what is stored.
10High The admin page checks the role in the browser, not on the server
src/pages/Admin.tsx line 22
Check the admin role inside the server function that returns the data, and refuse the request when it is missing.

How someone could get in

Our attacker pass took the problems in this report and worked out how someone could combine them, reading the code that connects each step.

1. Any member downloads every other member's details and payments

They start with: A free account on your app

  1. They sign up and open the browser's developer tools.
  2. They ask the database for the whole members table, and get every member's name, email and files back.
  3. They change their own role to admin in the browser and open the admin data, which never checks.

What it costs you: Every customer's details and every order and refund leave your app, and you must tell all of them.

2. A stranger takes over your whole database without signing up

They start with: Your public website, and nothing else

  1. They view the page source and copy the database master key from it.
  2. With that key they read every member, every order and every uploaded file's address.
  3. They open the uploaded files, which need no sign-in, and change prices in the database while they are there.

What it costs you: Your members' documents are public, your records can be changed or wiped, and you would not know it happened.

3. A stranger makes themselves an admin and takes your programs for free

They start with: The sign-up form

  1. They sign up with one extra field, role set to admin, and the app saves it.
  2. As an admin they read every order and every stored card number.
  3. They send your payment webhook a fake "paid" message and unlock any program they like.

What it costs you: Your customers' cards are stolen, your programs are given away, and the card networks can stop you taking payments.

Reading code cannot prove an app is free of problems: it cannot see your live settings, your hosting dashboard or your database. Each of the 19 areas below says what we found, that we checked it and found nothing, or that we could not check it and why.

1. Secrets

3 problems
Critical

Your Stripe secret key is still in the project's history

What's wrong
A Stripe secret key was typed straight into a settings file. It was later deleted from the file, but every earlier version of the file is kept in the project's history, and the key is still readable there.
Why it matters
Anyone with that key can issue refunds and read every payment your business has taken. Deleting it from the file did nothing, because it still works.
How to fix it
Create a new secret key in Stripe, put it in your hosting settings instead of the code, then delete the old key in Stripe.
Where
src/config.ts (removed in commit 4e1a9c2)
Found by
Both AI readers, working separately
Paste this into your AI tool
My app reads its Stripe secret key from src/config.ts. Change it so the key is read only from an environment variable called STRIPE_SECRET_KEY, and make the app refuse to start if that variable is missing. Do not put any key in the code.
How to check it worked
Search the project for "sk_live". Nothing should be found, and the app should still take a test payment after you add the new key in your hosting settings.
Critical

Your database master key is sent to every visitor's browser

What's wrong
The key that skips every database rule is stored in a setting whose name starts with VITE_. Anything with that prefix is copied into the code every visitor's browser downloads.
Why it matters
Anyone who views your page source can copy the key and read, change or delete every row in your database, without even signing up.
How to fix it
Create a new service key in Supabase, keep it only on the server, and delete the old one.
Where
.env (VITE_SUPABASE_SERVICE_KEY), used in src/lib/admin.ts line 6
createClient(url, import.meta.env.VITE_SUPABASE_SERVICE_KEY)
Found by
Both AI readers, working separately
Paste this into your AI tool
My app uses the Supabase service key in browser code through VITE_SUPABASE_SERVICE_KEY in src/lib/admin.ts. Move every call that needs that key into a server function, read the key there from an environment variable without the VITE_ prefix, and remove VITE_SUPABASE_SERVICE_KEY from the project.
How to check it worked
Open your live site, view the page source and search for "service_role". Nothing should be found.
Critical

Your OpenAI key is in the code every visitor downloads

What's wrong
The app calls the AI straight from the browser, so the AI key ships inside the page.
Why it matters
Anyone can copy the key and run their own AI work on your account. You get the bill.
How to fix it
Create a new key, call the AI only from a server function, and delete the old key.
Where
src/lib/ai.ts line 3
const openai = new OpenAI({ apiKey: import.meta.env.VITE_OPENAI_API_KEY, dangerouslyAllowBrowser: true })
Found by
Both AI readers, working separately
Paste this into your AI tool
Move every OpenAI call out of src/lib/ai.ts into a server function that reads OPENAI_API_KEY (no VITE_ prefix). Remove dangerouslyAllowBrowser and the VITE_OPENAI_API_KEY setting.
How to check it worked
View your live site's page source and search for "sk-". Nothing should be found.

2. Who can reach whose data

3 problems
Critical

Any signed-in member can read every other member's records

What's wrong
The app asks the database for the members list straight from the browser, and the members table has no rules saying who may read which rows. So the database answers with everyone.
Why it matters
One member who opens the browser's developer tools can download every member's name, email and uploaded files. That is your customers' data, and a breach notice to all of them.
How to fix it
Turn on row level security for the members table and add a rule that each member reads only rows where user_id matches their own.
Where
src/lib/supabase.ts line 14
const { data } = await supabase.from('members').select('*')
Found by
Both AI readers, working separately
Paste this into your AI tool
In Supabase, turn on row level security for the members table. Add a policy so a signed-in user can select only the rows where user_id equals auth.uid(). Then change src/lib/supabase.ts line 14 so it asks only for the signed-in member's own row.
How to check it worked
Sign in as one test member and try to load another member's row. The database should return nothing.
Critical

Anyone signed in can open any order by changing the number in the address

What's wrong
The order page loads whatever order number is in the address and never checks it belongs to the person asking.
Why it matters
A member who changes /orders/1041 to /orders/1040 sees someone else's name, address and what they bought, and can keep going through every order.
How to fix it
Load the order only if it belongs to the signed-in member.
Where
api/orders/[id].ts line 8
const order = await db.from('orders').select('*').eq('id', req.query.id).single()
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/orders/[id].ts, add .eq('member_id', <the signed-in user's id from the session>) to the query, and return 404 when no row comes back.
How to check it worked
Sign in as one test member and open another member's order number. It should say not found.
High

The admin page checks the role in the browser, not on the server

What's wrong
The admin page hides itself unless the browser says you are an admin. The data behind the page never checks, so it answers anyone who is signed in.
Why it matters
A member who edits one value in the browser sees every order and refund.
How to fix it
Check the admin role inside the server function that returns the data, and refuse the request when it is missing.
Where
src/pages/Admin.tsx line 22
if (user.role !== 'admin') return <Navigate to='/' />
Found by
One of our two AI readers
Paste this into your AI tool
The admin data is protected only in src/pages/Admin.tsx. Add a check in the server function that returns the admin data: look up the signed-in user's role on the server, and return a 403 error unless it is admin. Keep the browser check as well.
How to check it worked
Sign in as an ordinary member and call the admin data directly. It should refuse with a 403.

3. AI features

3 problems
Critical

The chat assistant will hand any member another member's records

What's wrong
Before answering, the chat loads the whole members table into the AI's instructions, then trusts the AI to only talk about the person asking.
Why it matters
A member who types "list every member's email and what they paid" gets the answer. The AI is not a lock, and anyone can talk it past its instructions.
How to fix it
Give the chat only the signed-in member's own rows, chosen on the server.
Where
api/chat.ts line 18
const context = await db.from('members').select('*')
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/chat.ts, stop loading the whole members table into the prompt. Load only the rows where user_id equals the signed-in user's id, taken from their session on the server, and pass only those to the AI.
How to check it worked
Sign in as one test member and ask the chat for another member's email. It should not know it.
High

Anyone can run up your AI bill with no limit

What's wrong
The chat feature sends every message to the AI with no limit per person, per day or per month.
Why it matters
One script left running overnight could spend thousands of dollars on your AI account before you notice.
How to fix it
Add a daily limit per signed-in user, and set a hard monthly spend cap in your AI provider's dashboard.
Where
api/chat.ts line 31
const reply = await openai.chat.completions.create({ model, messages })
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/chat.ts, before calling the AI, count how many chat messages the signed-in user has sent today and refuse with a friendly message once they reach 50. Store the count in the database, not in the browser.
How to check it worked
Send 51 messages as one test user. The 51st should be refused.
High

The chat can promise discounts and refunds, and the app honours them

What's wrong
When the AI's answer contains a discount code, the app applies it. Nothing checks the code is real.
Why it matters
A member who talks the AI into it gets your program at any price they like.
How to fix it
Never act on the AI's words. Discounts come only from codes your server created.
Where
api/chat.ts line 44
if (reply.includes('DISCOUNT:')) await applyDiscount(user, code)
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/chat.ts, remove the branch that applies a discount from the AI's reply. Discounts should only be applied at checkout from codes stored in the database.
How to check it worked
Ask the chat for a 100% discount code and try it at checkout. It should be refused.

4. Sign-in and accounts

3 problems
Critical

Anyone can sign up as an admin

What's wrong
The sign-up function saves every field it is sent, including the role. Add role: admin to the form and you are an admin.
Why it matters
A stranger can create their own admin account in a minute and see and change everything.
How to fix it
Save only the fields sign-up needs, and set the role on the server.
Where
api/signup.ts line 15
await db.from('members').insert({ ...req.body })
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/signup.ts, stop spreading req.body into the insert. Save only name, email and password hash, and always set role to 'member' on the server.
How to check it worked
Sign up with an extra field role=admin. The new account should be a member.
High

A password reset link works forever, and more than once

What's wrong
The reset link has no expiry and is not cancelled after it is used.
Why it matters
Anyone who ever sees an old reset email, in a shared inbox or a forwarded message, can take over that account months later.
How to fix it
Make reset links expire after one hour and stop working once used.
Where
api/reset-password.ts line 27
const row = await db.from('resets').select().eq('token', token).single()
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/reset-password.ts, refuse a reset token that is older than one hour or has already been used, and mark the token used as soon as the password changes.
How to check it worked
Use a reset link once, then open it again. The second time should be refused.
High

Nothing stops someone guessing passwords all day

What's wrong
The sign-in form accepts unlimited attempts from anyone.
Why it matters
A script can try thousands of common passwords against your members' emails until one works.
How to fix it
Limit failed sign-ins per email and per connection, and slow down after five.
Where
api/login.ts line 10
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/login.ts, count failed sign-ins per email and per IP address in the database. After 5 failures in 15 minutes, refuse further attempts for 15 minutes.
How to check it worked
Get the password wrong 6 times as a test member. The sixth should be refused.

5. Privacy and regulated data

3 problems
Critical

Members' uploaded files sit in a public folder

What's wrong
The storage folder for members' files is set to public. Every file has a web address that works without signing in.
Why it matters
Contracts, ID scans and anything else your members upload can be opened by anyone who has or guesses the address, and search engines can find them.
How to fix it
Make the folder private and hand out short-lived links only to the member who owns each file.
Where
supabase/migrations/0003_storage.sql line 4
insert into storage.buckets (id, public) values ('files', true)
Found by
Both AI readers, working separately
Paste this into your AI tool
Change the files bucket to private. Add a storage policy so a signed-in user can read only files under a folder named with their own user id, and change the app to show files through signed links that expire after 10 minutes.
How to check it worked
Copy the address of an uploaded file, sign out, and open it. It should be refused.
Critical

Card numbers are saved in your own database as plain text

What's wrong
Before sending a payment to Stripe, the checkout form saves the full card number, expiry date and security code in your database.
Why it matters
Anyone who gets into the database (see F2 and F5) leaves with your customers' cards. Storing them at all breaks the card networks' rules.
How to fix it
Stop saving card details anywhere. Let Stripe's own form collect them, and delete what is stored.
Where
src/pages/Checkout.tsx line 51
await supabase.from('payments').insert({ card_number, expiry, cvc })
Found by
Both AI readers, working separately
Paste this into your AI tool
Remove the payments insert from src/pages/Checkout.tsx and collect cards only with Stripe Elements or Stripe Checkout. Then write a migration that deletes the card_number, expiry and cvc columns.
How to check it worked
Make a test payment and look in the payments table. No card number should be there.
Medium

A deleted member's details stay in the database forever

What's wrong
Deleting an account only hides it. The name, email and files stay.
Why it matters
A member who asks you to delete their data has not had it deleted, which privacy laws require.
How to fix it
Delete or anonymise a member's personal details when they delete their account.
Where
api/delete-account.ts line 6
Found by
One of our two AI readers

6. Untrusted input

2 problems
High

Uploads accept any file of any size

What's wrong
The upload button sends whatever file it is given straight to storage. Nothing checks the type or the size, in the browser or on the server.
Why it matters
Someone can upload a web page full of harmful code, or a 5 GB file, under your app's name. You pay for the storage and your domain gets flagged.
How to fix it
Limit uploads to the file types you need and a maximum size, and enforce both in the storage rules.
Where
src/components/Upload.tsx line 40
await supabase.storage.from('files').upload(path, file)
Found by
Both AI readers, working separately
Paste this into your AI tool
Add a storage policy for the files bucket that accepts only image/png, image/jpeg and application/pdf up to 10 MB. Add the same check in src/components/Upload.tsx so members see a clear message before uploading.
How to check it worked
Try to upload a 20 MB file and an .html file as a test member. Both should be refused.
High

The chat shows the AI's answer as live web code

What's wrong
Whatever the AI answers is put on the page as working web code, not as text.
Why it matters
Someone who gets the AI to repeat a line of script can run it in another member's browser and act as them.
How to fix it
Show the AI's answer as plain text, or clean it before showing it.
Where
src/components/ChatBubble.tsx line 12
<div dangerouslySetInnerHTML={{ __html: reply }} />
Found by
Both AI readers, working separately
Paste this into your AI tool
In src/components/ChatBubble.tsx, stop using dangerouslySetInnerHTML. Render the reply as text, or pass it through DOMPurify first.
How to check it worked
Ask the chat to repeat <img src=x onerror=alert(1)>. Nothing should pop up.

7. Supply chain and repo control

2 problems
High

Your app runs a version of a library with a published break-in

What's wrong
The lockfile pins an old version of the library that reads uploaded images. That version has a publicly known flaw that lets a crafted file run code on your server.
Why it matters
The instructions for using it are public. Automated scanners look for exactly this.
How to fix it
Update the library to the fixed version and turn on GitHub's automatic security updates.
Where
package-lock.json
Found by
One of our two AI readers
Paste this into your AI tool
Update the image library in package.json to its latest version, run the install so package-lock.json changes with it, and check the upload and resize features still work.
How to check it worked
Run npm audit. It should report no high or critical problems.
High

Two former contractors can still publish straight to your live app

What's wrong
Two people who worked on the app last year still have admin rights to the repository, and anything pushed goes live with no review.
Why it matters
One stolen contractor password puts any code its holder likes on your live site.
How to fix it
Remove anyone who no longer works on the app, and require a review before changes go live.
Where
GitHub repository settings, as listed in the history
Found by
One of our two AI readers
Paste this into your AI tool
List the people with access to this GitHub repository and remove anyone who no longer works on the app. Then add a rule that changes to main need one approval.
How to check it worked
Open Settings, Collaborators in GitHub. Only current people should be listed.

8. Wrong results

2 problems
High

The price is worked out in the browser, so a buyer can change it

What's wrong
The checkout page adds up the total itself and tells the server how much to charge. The server charges whatever it is told.
Why it matters
Anyone who edits one number in the browser buys your $497 program for $1.
How to fix it
Have the server look up the price of what is being bought and ignore any amount sent from the browser.
Where
src/pages/Checkout.tsx line 63
body: JSON.stringify({ amount: total })
Found by
Both AI readers, working separately
Paste this into your AI tool
In the server function that creates the Stripe payment, stop reading amount from the request. Look up the price of the product id on the server, apply any discount there, and charge that.
How to check it worked
Change the amount in the browser before paying as a test buyer. The charge should still be the full price.
High

Anyone can mark an order as paid

What's wrong
The payment webhook believes any message sent to it. It never checks that the message came from Stripe.
Why it matters
Someone who sends a fake "payment succeeded" message gets your program without paying.
How to fix it
Check Stripe's signature on every webhook message and refuse any that fail.
Where
api/stripe-webhook.ts line 9
const event = JSON.parse(req.body)
Found by
Both AI readers, working separately
Paste this into your AI tool
In api/stripe-webhook.ts, verify each event with stripe.webhooks.constructEvent using the raw body and STRIPE_WEBHOOK_SECRET, and return 400 if it fails.
How to check it worked
Send a made-up payment message to the webhook. It should be refused with 400.

9. Breaks in use

2 problems
High

The app stops working when about 60 people use it at once

What's wrong
Every request opens its own database connection and never closes it. The database allows 60.
Why it matters
The day your launch email goes out, the app goes down for everyone, at the moment the most people are looking.
How to fix it
Use one shared connection pool and close connections when each request ends.
Where
api/_db.ts line 9
const client = new Client(process.env.DATABASE_URL)
Found by
One of our two AI readers
Paste this into your AI tool
In api/_db.ts, replace the new Client per request with a single shared pool using the database's pooled connection address, and make sure every request releases its connection, including when it fails.
How to check it worked
Send 100 requests at once to the app. All of them should get an answer.
High

Uploads over 4.5 MB fail without telling anyone

What's wrong
Files go through a server function, and your host refuses anything over 4.5 MB. The member sees a spinner that never stops.
Why it matters
Members with real documents cannot use the feature, and you never hear why.
How to fix it
Upload large files straight to storage, and show an error when an upload fails.
Where
src/components/Upload.tsx line 40
Found by
One of our two AI readers
Paste this into your AI tool
Change src/components/Upload.tsx to upload files directly to Supabase Storage with a signed upload URL instead of through the server function, and show a clear message if the upload fails.
How to check it worked
Upload a 10 MB PDF as a test member. It should arrive.

10. Cost and scale

2 problems
High

Every page visit downloads every member's records

What's wrong
The dashboard loads the whole members table each time anyone opens it, then shows one row.
Why it matters
Your database bill and page load time grow with every new member, and every visit hands out everyone's data.
How to fix it
Load only the rows the page shows.
Where
src/pages/Dashboard.tsx line 19
const { data } = await supabase.from('members').select('*')
Found by
Both AI readers, working separately
Paste this into your AI tool
In src/pages/Dashboard.tsx, load only the signed-in member's own row, and page through lists 50 rows at a time.
How to check it worked
Open the dashboard with the browser's network tab open. One row should come back.
Medium

Your database plan runs out at about 2,000 members

What's wrong
At the current size per member, the free database plan fills at about 2,000 members. The app then stops saving.
Why it matters
Your best month of sign-ups is the month the app stops working.
How to fix it
Set an alert at 80% of the plan and move to a paid plan before then.
Where
supabase/config.toml
Found by
One of our two AI readers

11. Logging and monitoring

2 problems
Medium

Errors are never recorded anywhere you would see them

What's wrong
When something fails on the server, the app does not write it down or tell anyone.
Why it matters
You hear about failures from customers, if at all, and you cannot tell how often they happen.
How to fix it
Add an error tracker and have it email you when a new error appears.
Where
api/
Found by
One of our two AI readers
Medium

There is no record of who changed or deleted what

What's wrong
When an admin changes an order or deletes a member, nothing writes down who did it or when.
Why it matters
After a mistake or a break-in you cannot tell what happened.
How to fix it
Keep a simple log of every admin change: who, what and when.
Where
api/
Found by
One of our two AI readers

12. Layer separation

1 problem
Medium

The database is called from 38 different browser files

What's wrong
Every page talks to the database on its own, so the same rule is written over and over.
Why it matters
Fix a security rule in one place and the other 37 still have the hole.
How to fix it
Put database calls in one shared set of server functions.
Where
src/
Found by
One of our two AI readers

13. Tests and releases

2 problems
Medium

Nothing tests the payment code, and every change goes straight live

What's wrong
There are no tests for checkout or refunds, and changes are published to the live site the moment they are saved.
Why it matters
The next time your AI tool "fixes" something nearby, payments can break and nobody knows until a customer complains.
How to fix it
Add a test that takes a test payment, and have changes go to a preview site before the live one.
Where
.github/ (none)
Found by
One of our two AI readers
Medium

A database change file was edited after it had already run

What's wrong
The file that set up the members table was changed after it was applied, so your live database and your code no longer match.
Why it matters
The next fresh setup, or a restore, builds a different database from the one you run.
How to fix it
Never edit an applied change file. Add a new one with the difference.
Where
supabase/migrations/0004_members.sql
Found by
One of our two AI readers

14. Recovering from mistakes

2 problems
Medium

Deleting a member also deletes their orders, with no undo

What's wrong
Removing a member's account wipes every order they ever placed, and there is no way to bring them back from inside the app.
Why it matters
One wrong click loses payment records you need for refunds and tax.
How to fix it
Mark members as removed instead of deleting them, and keep their orders.
Where
supabase/migrations/0002_orders.sql line 7
member_id uuid references members(id) on delete cascade
Found by
One of our two AI readers
Low

Re-running a failed member import adds everyone twice

What's wrong
The import has no check for members already added.
Why it matters
One retry doubles your list and every email you send.
How to fix it
Skip members whose email is already in the table.
Where
scripts/import-members.ts
Found by
One of our two AI readers

15. What users see (apps with a screen)

2 problems
Low

The sign-up form shows 'Error 400' instead of what went wrong

What's wrong
When sign-up fails, the form shows a code number instead of the reason.
Why it matters
A customer who mistypes their email sees a code, doesn't know what to do, and gives up.
How to fix it
Show the message the server sends back, in plain words.
Where
src/pages/Signup.tsx line 58
setError(`Error ${res.status}`)
Found by
One of our two AI readers
Low

The sign-up form cannot be used with a keyboard alone

What's wrong
The submit button is a picture with a click handler, so it cannot be reached with the Tab key.
Why it matters
Some customers cannot sign up at all.
How to fix it
Use a real button.
Where
src/pages/Signup.tsx
Found by
One of our two AI readers

16. Requirements docs

Checked, nothing found

We checked this area and found nothing to fix. What we checked: the README says what each part of the app is for.

17. Licences and ownership

Checked, nothing found

We checked this area and found nothing to fix. What we checked: every dependency's licence allows selling the product.

18. Handover risk

Checked, nothing found

We checked this area and found nothing to fix. What we checked: a new developer can run the app from the README.

19. Operations

Not checked

Backups live in your database dashboard, which reading the code cannot see. Check it under Database, Backups.

How this was done

What to do next

  1. Start with the findings marked critical, then high. Each one says what to change in a single line. Paste the finding, the file and the line into your AI tool and ask it to make that change.
  2. Change any password or key this report names. Deleting it from the code is not enough: it is still in your project's history, and it still works until you replace it with a new one.
  3. Check the settings this report marks "depends on". We could not see them from the code. The report names each setting and where to find it.
  4. Save a copy. This link stops working on 25 November 2026, 30 days after it was sent.

Your code was deleted from our systems at 26 October 2026, 10:55 Manila time (UTC+8).

This report shows what we found by reading your code on 26 October 2026. It is not proof that your app is secure, and it does not cover changes made after that date.

Keep it checked as you build

The findings above are what was in your code on 26 October 2026. Every change you make from here adds new code. When you have made a round of changes, you can order another audit of the same repo here: https://www.impossiblemeansgo.com

Build the next one right from the first day

Most of what is in this report came from shortcuts the AI tool took while you were building fast. Fixing them fixes this app. Knowing how to set up the next one so the shortcuts never get in is a different skill, and it is what Impossible Means Go teaches.

Impossible Means Go is run by Atiba de Souza. Business owners come to build real AI in their own business, in the room with him, and leave with it working.

If you want to talk about what you would build next, reply to the email this report came with.