What a code review looks like.
This is the report we would send for a salon booking app built with Lovable on Supabase. The app is made up; the checks, the format and the order are the ones we use.
- BUILT WITH
- ACCESS
- FINDINGS
- VERDICT
Safe to demo, not yet safe to grow.
The booking flow works and the calendar code is sound. Two problems should be fixed before the next release: any signed-in customer can read and cancel every other customer’s bookings, and the key that bypasses the database’s access rules ships in the browser.
Three more should follow soon after: double bookings, an uncapped model call and a booking page with no tests. None of it needs a rewrite.
Five findings, ranked by severity.
C1criticalAUTH AND DATA ACCESS
Row-level security is off on the bookings table.
- FILE
supabase/migrations/20260611_bookings.sql- RISK
- The app’s public key can read and change every row, so any signed-in customer can see other customers’ names, phone numbers and appointments, and cancel them.
- FIX
- Turn row-level security on, with policies that let customers see, create and change only their own bookings and let staff see their own salon’s. A test signs in as a second customer and expects nothing back. The change is below.
C2criticalSECRETS
The service-role key ships in the browser bundle.
- FILE
src/integrations/supabase/admin.ts- RISK
- That key skips row-level security entirely. Anyone who opens the site’s JavaScript can copy it and act as the database administrator.
- FIX
- Rotate the key today. Move the two admin calls into an Edge Function that reads the key from its environment, and search the git history for older copies.
W1warnLOGIC BUGS
Two customers can book the same slot.
- FILE
supabase/functions/create-booking/index.ts- RISK
- The function checks that a slot is free, then inserts, in two separate queries. Two requests in the same moment both succeed.
- FIX
- A unique constraint on the salon, the chair and the start time, so the database refuses the second booking, and a clear message in the app offering the next free slot.
W2warnAI-API SPEND
The reminder writer retries with no cap.
- FILE
supabase/functions/send-reminder/index.ts- RISK
- Each reminder is written by a model API call inside a retry loop with no timeout and no limit. A slow API turns one reminder into an open-ended bill.
- FIX
- A timeout on the call, at most two retries with backoff, and a daily budget on the key. A fixed template covers the reminder when the model is unavailable.
W3warnTESTS AND STRUCTURE
Nothing tests booking, rescheduling or cancelling.
- FILE
src/pages/Book.tsx- RISK
- The calendar, the form and the confirmation live in one 900-line component, and every change is checked by clicking through it.
- FIX
- End-to-end tests for book, reschedule and cancel first, then split the page into three components. The tests are what make the fixes above safe to ship.
The fix for C1, as a change.
A new migration: row-level security on, and one policy for each thing a customer or a member of staff needs to do. Anything no policy allows stays closed.
- added
alter table bookings - added
enable row level security; - added
- added
create policy read_own - added
on bookings for select - added
using (customer_id = auth.uid()); - added
- added
create policy book_own - added
on bookings for insert - added
with check (customer_id = auth.uid()); - added
- added
create policy change_own - added
on bookings for update - added
using (customer_id = auth.uid()) - added
with check (customer_id = auth.uid()); - added
- added
create policy staff_read_salon - added
on bookings for select - added
using (salon_id in ( - added
select salon_id from staff - added
where user_id = auth.uid() - added
));
Two parts that are sound as they are.
ok
Sign-in.
- FILE
src/integrations/supabase/client.ts- WHY
- Supabase Auth with email links, set up correctly, sessions refreshed. Nothing to change.
ok
The calendar components.
- FILE
src/components/calendar/- WHY
- Small, typed and reused across three pages. The refactor in W3 keeps them as they are.
The order to fix them in, starting today.
TODAY
Rotate the service-role key and turn row-level security on (C2, C1).THIS WEEK
The unique slot constraint and the cap on reminder retries (W1, W2).THEN
Tests on the booking flow, and only then the split of the booking page (W3).
The same report, for your repository.
You grant read-only access to one repository, and the written review arrives within 48 hours of access.