Lab audit · our own test app

Launch-Ready Report — demo-shop (lab audit)

Forthbeam Studio · Lab audit of our own test app · October 2026

Stack: React (Vite) · Supabase (auth, Postgres, Edge Functions) · Stripe Checkout

Reviewed: source repository, database migrations, Edge Function, and a local production build. The app was not deployed, so hosting-level checks are marked not assessed.

7issues found · 2 critical · 2 high · 3 medium
demo-shop is an app we built ourselves, with an AI coding assistant, and gave production flaws on purpose. It contains no client code or data. Every finding below is reproducible: run cd audit && npm install && npm run evidence.

Summary

demo-shop is not ready to take real payments. Anyone can read every customer's order, email and address with the public key, and the Stripe secret key ships inside the website's JavaScript. Both are fixable in under a day. After the critical and high fixes, it is in reasonable shape for a small launch.

SeverityCountWhat it means
Critical2Fix before taking any real user data or payments
High2Fix before launch
Medium3Fix in the first weeks after launch

Of the 13 checks we could assess from the code, 6 pass. 6 checks need a live deployment and 1 does not apply.

Findings

Critical · C1 Fixed in this audit

Orders readable without login

Where
supabase/migrations/0001_init.sql, table public.orders
Impact
products and profiles have row-level security; orders does not. With Supabase's default grants, anyone holding the public anon key (shipped in every page load) can list every order, including customer emails and delivery addresses. Signed-in customers can also read each other's orders.
Evidence
in a Postgres instance with Supabase's roles and grants, a logged-out (anon) query returned all 3 seeded orders with emails and addresses; a signed-in customer could read another customer's order. (audit/tests/c1-orders-rls.test.ts)
Fix
audit/fixes/0002_orders_rls.sql enables RLS; customers read and create only their own orders, new orders must start as pending, admins read all. After the fix: anon sees 0 orders, a customer sees only their 2, the admin sees all 3, and forged inserts are rejected.
Effort
1 hour
Critical · C2

Stripe secret key shipped to the browser

Where
src/lib/checkout.ts reads VITE_STRIPE_SECRET_KEY
Impact
Vite copies every VITE_ variable into the public JavaScript. Anyone can open the site, copy the key, and issue refunds, read customer data or create charges on the Stripe account.
Evidence
a production build with a test key placed it in plain text in dist/assets/index-*.js.
Fix
move Checkout Session creation into an Edge Function that holds the key server-side; rotate the key in Stripe.
Effort
2–3 hours, together with H2
High · H1

Payment webhook accepts forged events

Where
supabase/functions/stripe-webhook/handler.ts
Impact
the handler never checks the Stripe-Signature header. Anyone who finds the URL can send a fake "checkout completed" event and mark an unpaid order as paid.
Evidence
an unsigned forged event returned 200 and marked the order paid. (audit/tests/h1-webhook-forgery.test.ts)
Fix
verify the signature with the webhook signing secret (stripe.webhooks.constructEventAsync) before reading the payload; record processed event IDs.
Effort
1–2 hours
High · H2

Checkout trusts the price computed in the browser

Where
src/lib/cart.ts, src/lib/checkout.ts
Impact
prices live in the cart in localStorage and the browser sends the total to Stripe. A customer can edit the cart and pay any amount.
Evidence
a cart edited to 1 cent produced a Stripe unit_amount of 1 for a $32.00 product. (audit/tests/h2-client-price.test.ts)
Fix
send only product IDs and quantities to the server; look up prices in the database (or use Stripe Price IDs) when creating the session and the order.
Effort
1–2 hours, alongside C2
Medium · M1

Admin page protected only in the interface

Where
src/App.tsx, src/pages/Admin.tsx
Impact
the "Admin" link is hidden from non-admins, but /admin is a normal route and the page queries all orders without checking the role. Before C1 was fixed, any visitor could open it and see every order. After C1, the database protects the data, but the page still relies on that single rule.
Evidence
—
Fix
add a route guard that checks is_admin, and keep admin-only data behind database policies (now in place) or a server function.
Effort
1 hour
Medium · M2

No error tracking

Where
—
Impact
failed checkouts, webhook errors and front-end crashes are invisible until a customer complains.
Evidence
no error-tracking package or setup in the app.
Fix
add an error tracker (a free tier is enough) to the front end and the Edge Function, with alerts on new errors.
Effort
1 hour
Medium · M3

No automated tests on checkout or the webhook

Where
—
Impact
every change to payments is tested by hand, or by customers. C1, H1 and H2 would each have been caught by a short test.
Evidence
no test files and no test script in the app.
Fix
keep the audit's tests as a starting suite and run them in CI on every push.
Effort
1–2 hours

Scorecard

AreaCheckResult
Secrets & accessKeys in the client bundleFail · C2
Auth on every API routePass
Admin pages locked downFail · M1
Environment separationNot assessed
Data protectionRow-level security on every tableFail · C1 (fixed)
Input validationPass
File upload limitsNot applicable
CORS and rate limitsNot assessed
PaymentsWebhook signature checksFail · H1
Idempotent order updatesPass
Server-side price checksFail · H2
Failed-payment handlingPass
ReliabilityError trackingFail · M2
Useful logsPass
Vulnerable dependenciesPass (npm audit: 0)
Email deliverabilityNot assessed
Deploy & recoveryBackups you can restoreNot assessed
Tests on signup and checkoutFail · M3
A repeatable deployNot assessed
A way to roll backNot assessed

Fix plan

  1. Done in this audit: C1.
  2. Before launch: C2 and H2 together (one server function), then H1.
  3. First two weeks: M1, M2, M3.

Example Fix Sprint quote for C2, H1, H2 and M1–M3: $900 fixed, 4 working days, including tests on checkout and the webhook in CI, and a handover video. The audit fee is credited if booked within 14 days.

Raw output: audit/out/evidence.txt.

Reproduce it yourself · cd audit && npm install && npm run evidence

Get your audit · $149 Book a free 20-min check