Bereket Tilahun Hire on Upwork
01

ShopFast sample audit

A pre-launch security audit of an AI-built Next.js + Supabase + Stripe store, with every finding proven

A demo store with the eight flaws AI coding tools ship most often, audited the way I'd audit a client's app, each finding proven and fixed.

Running — Public demo and sample report, published Nov 2026. Demo app, not client work: the flaws were planted on purpose and the code is marked do-not-deploy.

Role Sole reviewer — threat model, review, proofs, report Licence No licence — demo, do not reuse GitHub
Next.js 14SupabasePostgreSQL RLSStripeOpenAI APINode.jsCVSS 3.1Bash

The problem

Apps built with Lovable, Bolt, Cursor or Claude Code reach real users fast, and they tend to repeat the same security mistakes: a privileged key in the browser, tables with no access rules, and payment flows that trust the client. Founders usually find out from a customer, not a test.

Why the obvious approach fails

  • Reading RLS policies is not testing them: a test that runs as the table owner bypasses RLS and passes for the wrong reason.
  • Scanners flag patterns but can't tell whether one customer can actually reach another customer's order.
  • Reports without a reproduction get argued about; reports with one get fixed.

Architecture

ShopFast is a small Next.js 14 store on Supabase (Postgres, auth) and Stripe, with an AI chat endpoint. Each planted flaw is marked in the code with a PLANTED SF-0n comment, and the poc/ folder holds the scripts that prove them.

  1. Read code + migrations
  2. Sign in as each role
  3. Try to cross the boundary
  4. Prove it with a script
  5. Severity, proof, fix
repository layout
app/api/
  orders/[id]/      SF-04 no ownership check
  checkout/         SF-06 price taken from the browser
  stripe/webhook/   SF-05 signature never checked
  chat/             SF-08 no auth, no limits
lib/supabase.ts     SF-01 service-role key shipped to the browser
lib/supabase/server.ts, middleware.ts
                    SF-07 CORS reflects any origin, cookies cross-site
supabase/migrations/ SF-02 RLS never enabled
poc/                run-all.mjs (one-command runner), sf01.sh,
                    sf08.sh, local Stripe + OpenAI stand-ins

Threat model

  • Cross-tenant reads and writes. Can one signed-in customer read or change another customer's profile or order? (SF-02, SF-04)
  • Privileged key exposure. Is the service-role key, which bypasses every RLS policy, reachable from the browser bundle? (SF-01)
  • Secrets in history. Was a secret ever committed, even if a later commit removed it? (SF-03)
  • Payment integrity. Can a customer set their own price or fake a payment confirmation? (SF-05, SF-06)
  • Cross-site data theft. Can another website read a logged-in customer's data through permissive CORS and cross-site cookies? (SF-07)
  • Cost abuse. Can anyone run up the AI bill through an unauthenticated chat endpoint? (SF-08)

Decisions

Test as each role, not as the owner
Every authorization check is run as anon and as two separate customers, both in a Postgres session with that user's JWT claims and through the real Supabase client, so RLS is tested the way the app actually reaches it.
Proofs stop at impact
Each proof shows the boundary being crossed and stops there. Stripe and OpenAI calls go to local stand-ins, so running the proofs touches no payment or AI account.
Severity a founder can act on
Every finding carries a CVSS 3.1 score and a plain fix-today, this-week or before-launch priority, because a score alone doesn't tell a non-specialist what to do first.
Label the demo as a demo
The report, the repository and the Upwork listing all say it is a demo with planted flaws, not client work, so nobody mistakes the sample for a track record.

Implementation

  • App: Next.js 14 App Router, @supabase/ssr and supabase-js, Stripe and OpenAI SDKs; one SQL migration defines the schema.
  • Proofs: npm run poc builds the app, starts it on localhost and runs the six HTTP-level proofs, writing redacted results to poc/out/results.txt; secrets and tokens are redacted in the output.
  • Report: plain-English summary, CVSS 3.1 score, proof and code fix per finding, and a fix-today / this-week / before-launch roadmap.
  • Repo: github.com/Berakhah/shopfast-demo.

Evidence

planted flaws proven in a live run 3 Critical, 2 High, 3 Medium, each with a CVSS 3.1 score, proof and code fix.
flaws re-proven by one command SF-01, 02, 04, 05, 06 and 07 run in npm run poc; SF-08 has its own load script and SF-03 lives in the first commit.
  • Anyone can re-run the proofs: the repository holds the code, the migration and the scripts, with run instructions in the README.
  • Flaws are marked where they live: every planted flaw has a PLANTED SF-0n comment at its location in the code.
  • Output is redacted by default: the proof runner masks JWTs, session cookies and Stripe keys before writing results.

Security controls

  • The fixes in the report: RLS enabled with owner-scoped policies, the service-role key kept server-side, ownership checks on every order route, prices read from the database, Stripe webhook signatures verified, an exact-origin CORS allowlist with SameSite cookies, and auth plus rate and token limits on the chat endpoint.

Trade-offs

  • Planted flaws, not found ones: a demo can show method and report quality, but not how a review performs on an unfamiliar codebase.
  • Local stand-ins for Stripe and OpenAI make the proofs safe to run anywhere, at the cost of not exercising the real providers.

Caveats & status

A demo, not client work: the flaws were planted deliberately to show the report format. It shows how I test and report, not what a real client app contained.

  • Demo app with deliberately planted flaws; not client work and not a measure of real-world findings.
  • The proofs need a throwaway Supabase project, which is exposed while it runs because the demo has RLS off by design.
  • No licence: the code is published to back the sample report, not for reuse.

Running — Public demo and sample report, published Nov 2026. Demo app, not client work: the flaws were planted on purpose and the code is marked do-not-deploy.

Published November 2026 as the sample report behind my pre-launch security audits. Repository public; demo Supabase project kept paused.

Repository

github.com/Berakhah/shopfast-demo No licence — demo, do not reuse

Public repository: github.com/Berakhah/shopfast-demo. Intentionally vulnerable — do not deploy.