App Locksmith
Sample

This is a sample. Fernway isn't a real company and none of this describes a real client's app. I wrote it to show exactly what a report looks like, down to the wording.

Independent security review: Fernway

A meal-planning app for iPhone and Android, built with Claude Code, Expo and Supabase. Reviewed over five days in March 2026, then fixed.

Prepared for
Fernway Ltd, attention of the founder
Prepared by
Nate Parker, App Locksmith
Work done
2 to 6 March 2026
Report dated
9 March 2026
Version
Final, version 1.1
Reference
AL-2026-014

The short version

I found seven problems. Six are fixed and the fixes are live in your app. One needs a decision from you, and it's the last section of this report.

One of the seven was serious. The key that unlocks your whole database was packed inside the app people download from the store, where anyone could read it. Any single person who noticed could have taken every user account, saved meal plan and allergy note in Fernway. I found no evidence anyone did, and I say what I checked to reach that.

The rest were the ordinary kind: rules that let signed-in users read each other's data, a subscription check the app made about itself, and three smaller things. All of it is normal for an app that shipped fast. None of it means you built the wrong thing.

7problems found
6fixed by me
1needs your decision

How I rate them

  • CriticalSomeone could do this today, without skill or luck, and reach everybody's data.
  • HighReal damage, but it takes an account, effort or a particular sequence.
  • MediumWorth fixing soon. Limited damage, or it needs something else to go wrong first.
  • LowTidy-up. Little risk on its own, but it helps someone who is already looking.

What I looked at

  • Your app's code, as of commit 8f2a1c4 on the main branch.
  • The app as shipped: I downloaded your live build from both stores and took it apart the way a stranger would.
  • Your Supabase project: database rules, authentication settings and stored functions.
  • Your server functions on Vercel, and the environment variables they read.
  • How the app talks to all of the above, watched over a proxy while I used it.

What I did not look at

  • Apple's and Google's own systems, or Supabase's and Vercel's. I checked how you use them, not how they are built.
  • Your laptop, your email and your accounts. Most small companies are broken into through a person, not an app. That's a different job and worth doing.
  • Anything to do with your books, your contracts or your tax.
  • Physical security, staff training, and anything outside the app and its backend.

What I found

Each one says what it means for you, not what it's called in security. The fixes are already live unless the status says otherwise.

1

Your database master key was inside the app download

CriticalFixed

What it means
Fernway's Supabase service key was packed into the app that anyone can download from the App Store. That key ignores every permission rule you've set. It is meant to live on a server you control and nowhere else.
What could have happened
One person with the downloaded app and a free tool could have read, changed or deleted every row in your database: all 4,200 accounts, every saved meal plan, and every allergy note. It would have taken about ten minutes, and nothing in your logs would have looked unusual.
What I did about it
Took the key out of the app, then rotated it so the copy in every downloaded build is dead. The three things that genuinely needed it now live in a small server function the app calls, so the app itself holds no key that can bypass your rules.
How to check it yourself
Download your app from the store, unzip it, and search the files for service_role. There are no matches now. The exact command is in your handover notes.
Whether anyone used it
I checked your Supabase logs back to the app's first release. Every request using that key came from your own servers. That isn't proof nobody copied it, but there is no sign it was used.

What I sawThe key readable as plain text inside main.jsbundle in the downloaded app, starting eyJhbGciOiJIUzI1NiIs

2

Any signed-in user could read any other user's data

HighFixed

What it means
Your database had a rule saying you must be logged in to read the meals table. It didn't say and only your own rows. Any Fernway user, on a free account, could ask for someone else's saved plans, allergies and notes and get them.
What could have happened
A curious user or a competitor with a free account could have pulled every meal plan and every allergy note in the app. Allergy notes are health information attached to named people, which is the kind of data that turns a mistake into a legal problem.
What I did about it
Rewrote the rules on all five tables so a row is readable only by the account that owns it. Added a test that fails your build if anyone ever loosens one again, including an AI making a helpful change.
How to check it yourself
Sign in on two test accounts and try to open the other one's plan by its id. You get nothing back. I left both test accounts in your project.
Whether anyone used it
Your logs don't record which rows were returned, so I can't say whether anyone did this. Based on the query patterns, I saw nothing that looked like someone enumerating other people's data.

What I sawPolicy meals_select allowed auth.role() = 'authenticated' with no check of the owner column

3

Anyone could unlock Pro for free

HighFixed

What it means
The app decided for itself whether someone had paid, then told your server. A modified copy of the app could simply say yes. This is the single most common way AI-built apps leak money.
What could have happened
Lost revenue, quietly. You would have seen it as an odd gap between how many people use Pro features and how much Apple and Google pay you, and you'd probably have blamed the analytics.
What I did about it
Moved the decision to your server, which now asks Apple and Google directly whether a receipt is real and stores the answer. The app only shows what the server tells it.
How to check it yourself
Compare your Pro user count against the subscriber count in App Store Connect and Play Console each month. They should now track each other.
Whether anyone used it
I compared your current Pro accounts against real receipts. Nine accounts had Pro without a matching receipt. Seven are your own test accounts. The other two are listed in your handover notes for you to decide about.

What I sawThe app set is_pro on the profiles row directly after a purchase, with no server-side receipt check

4

Password reset links never expired

MediumFixed

What it means
A reset link emailed a year ago still worked. Old emails sit in inboxes, and inboxes get broken into long after the fact.
What could have happened
Anyone who reached an old email, on a shared computer or a recycled account, could have taken over that user's Fernway account whenever they liked.
What I did about it
Set reset links to expire after one hour and to stop working once used.
How to check it yourself
Ask for a reset link, wait an hour, then click it. It should tell you the link has expired.

What I sawSupabase Auth setting: reset token lifetime left at the default of never

5

Nothing stopped one person running your AI bill up

MediumFixed

What it means
The button that writes a meal plan called your AI provider every time it was pressed, with no limit per account and no cap on the account overall.
What could have happened
One person with a short script could have pressed it thousands of times overnight. You'd have found out from the bill, and you'd have paid it.
What I did about it
Added a limit of twenty plans per account per day, and a hard monthly spending cap on the provider account. Both are set well above what your busiest real user does.
How to check it yourself
Your provider dashboard now shows a monthly cap. The limit is in your handover notes if you want to raise it.

What I sawNo rate limit on /api/plan, and no cap set on the provider account

6

Error screens showed your database's own messages

LowFixed

What it means
When something went wrong, the app showed the raw error, which named your tables and columns. Useful to someone working out how to attack you, meaningless to a user.
What could have happened
On its own, nothing. It makes the first two problems on this list much faster to find, which is why it's here at all.
What I did about it
Errors now show a plain sentence and a reference number. The detail goes to your logs, where you can still read it.
How to check it yourself
Turn off wi-fi and use the app. You should see a plain message, not a database error.

What I sawThe raw PostgREST error shown in the app's red banner, naming the profiles and meals tables

What needs your decision

I don't delete data or change what you collect without you saying so. This is the one thing I left alone.

7

An old table still holds 1,100 waitlist email addresses

MediumOpen, waiting on you

What it means
A table called waitlist_import, left over from before launch, holds 1,100 email addresses and names from your landing page. Nothing in the app reads it. I locked it so only you can reach it, but it's still there.
What could have happened
It's data you don't use, and data you don't use is only ever a liability. If your database were reached, it's 1,100 more people to tell.
What I did about it
Locked the table so no app account can read it, and left the data in place. Deleting people's data is your call, not mine.

What I sawTable waitlist_import, 1,100 rows, last written 11 months ago, no reads in the last 90 days

Your options

  • Delete it. I can do this in ten minutes and you'll have nothing to think about.
  • Export it and then delete it, if you still want to email those people one day.
  • Keep it. If you do, it should be in your privacy notice, because those people were told something when they signed up.

What I checked and found nothing wrong with

A report that only lists problems tells you nothing about what's solid. These were fine.

  • Payments. Stripe is used correctly on the web side and card numbers never touch your systems.
  • Passwords. Handled by Supabase Auth, stored correctly, and your own code never sees them.
  • The marketing site. No user data and no keys.
  • The packages you depend on. None of the versions you ship have known holes as of the report date.
  • Permissions. Your app asks for the camera and nothing else, and it uses it.

Keeping it this way

  • Rotate keys when anyone leavesContractors included. It takes ten minutes and it closes the most common door.
  • Keep the rules test in your buildIf it ever fails, something loosened your database rules. Don't skip past it.
  • Check the spending caps each quarterThey're set for today's usage. Raise them on purpose, not by removing them.
  • Let someone look again after big changesThis report describes your app on 6 March 2026. It doesn't describe what you ship in June.

What this report is, and what it isn't

  • It's a record of what I checked, what I found and what I changed, on the dates given. It isn't a certificate, and it isn't a guarantee.
  • Nobody honest can tell you an app has no security holes. I can tell you what I looked for, what I found, and what I fixed, and you can hand that to anyone who asks.
  • Every problem here is described after its fix went live, except the open one, which is described as it stands.
  • I reviewed the code and the live app. I did not review the security of Apple, Google, Supabase or Vercel themselves.
  • If you share this report, share all of it. Individual findings pulled out of it will read as worse or better than they are.

Nate Parker

App Locksmith, applocksmith.com

Signed 9 March 2026. Ask me anything about this report until 8 April 2026.

Want one of these for your app?

This is what you get, whichever review you book: the plain-English version, the technical detail, the fixes already made, and a dated report you can hand to a customer or a buyer.