Humantik

Sample. This is a Signed Review report for a made-up app, written to show what you receive. The app, the reviewer and every finding are invented; no real app was reviewed. The evidence files describe screenshots in words instead of showing them.

Review report, order SAMPLE-0001

This report is for the app's builder. It shows how to reproduce each failure, so keep it private; a buyer or investor gets the public registry page instead.

Review
Signed Review
Reviewer
Sam Example (REV-000)
Build reviewed
7c41e0b2d9a3
Checklist
version 1.0
Reviewed on
2026-10-20
Results
26 passed, 3 failed, 1 not applicable, 0 not checked
Report fingerprint
95587fe3a3b733aff096fcb78d096588b718b95c919c89765cd830e59ca3fccb

Scope: Tidyhaus is a made-up cleaning-booking app, built with Lovable on Supabase with Stripe. Web app on staging.tidyhaus.example; the admin area was included.

Tested: 2026-10-15 09:30 to 2026-10-19 15:00, America/New_York. Every logged-in request was made from the test accounts named in the steps, so you can find it in your own logs.

AI assistance: Claude, to read the repository and suggest where to look first; every finding was then tested by hand from the test accounts. No keys, passwords or user data were shared with it, as the builder agreed. The reviewer ran and confirmed every result and answers for each one.

What to fix

Reviewer's judgment

Customers see only their own bookings, messages and invoices, and cleaners see only the jobs assigned to them, which matches how the owner described the app. The one way around it is the role field under AR-03: any signed-in user can make themselves an admin, so fix that first. The payment flow holds up: prices come from the server, forged Stripe events are refused and repeated ones are ignored, and a paid plan cannot be switched on by hand.

Results

On the reviewed build, with the reviewer's test accounts
ItemWhat it checksSeverityResult
AR-01One user cannot read or change another user's records by changing an idcriticalPass
AR-04Team or workspace members cannot see or change another team's datacriticalNot applicable
PM-01Payment webhooks are rejected unless the Stripe signature checks outcriticalPass
PM-03A paid feature cannot be unlocked without a confirmed paymentcriticalPass
PM-04A repeated payment event does not grant credit or create an order twicehighPass
DX-01Data cannot be read or changed without logging incriticalPass
DX-02Database views follow the same access rules as their tablescriticalPass
DX-03Responses carry only the fields the screen needshighPass
DX-04Web addresses carry no keys, tokens or personal datahighPass
IN-01Text one user enters cannot run as code in another user's browsercriticalPass
AR-02Ordinary users cannot reach admin pages or admin functionscriticalPass
AR-03Users cannot write rows as someone else or change protected fieldscriticalFail
SK-01Browser code holds no service-role or other secret keyscriticalPass
SK-02The repository and its history hold no live secrets, and leaked keys are rotatedcriticalPass
SK-03Third-party keys that must sit in the browser are restricted to this app and the APIs it useshighFail
AU-01Password reset links are single-use, expire, and do not reveal which emails have accountshighPass
AU-02Logging out revokes the session on the server, and any copied access token stops at its expiryhighPass
AU-03Sessions and access tokens expire on a defined schedulemediumPass
AU-04Repeated login and code attempts are rate limitedhighPass
PM-02Prices and totals are set on the servercriticalPass
BR-01Database backups are switched on and a restore has actually been triedhighPass
EL-01Error messages shown to users do not reveal internalsmediumPass
EL-02Passwords, tokens, card data and personal data are not written to logshighFail
EL-03Failed logins and refused access attempts are recordedmediumPass
TP-01Dependencies have no known critical or high vulnerabilities left unaddressedhighPass
TP-02Personal data sent to analytics, trackers or chat widgets matches the privacy policyhighPass
DX-06Database functions called from the browser follow the same access rules as their tablescriticalPass
PD-01Deleting an account removes the user's records, files and sessionshighPass
PD-03Private uploads are not publicly reachablecriticalPass
SC-01The app publishes a working way to report security problemsmediumPass

AR-01 · One user cannot read or change another user's records by changing an id

Pass

Steps to reproduce

  1. Signed in as test user A and opened booking 8812, saving the request that loads it.
  2. Signed in as test user B and sent the same GET and PATCH for booking 8812 with B's token.
  3. Repeated for profiles, bookings, messages and invoices, and for the bookings list with its user filter removed.

Evidence

AR-04 · Team or workspace members cannot see or change another team's data

Not applicable

Steps to reproduce

  1. Asked the owner whether the app has teams or workspaces: it does not. Each account belongs to one person.
  2. Checked the schema: no table carries a team or organisation id.

Evidence

Reviewer's note: Tidyhaus has no teams or workspaces: each customer and each cleaner has their own account.

PM-01 · Payment webhooks are rejected unless the Stripe signature checks out

Pass

Steps to reproduce

  1. Read supabase/functions/stripe-webhook/index.ts.
  2. Sent an unsigned checkout.session.completed event to the staging webhook URL.
  3. Checked the bookings table for a new paid booking.

Evidence

PM-03 · A paid feature cannot be unlocked without a confirmed payment

Pass

Steps to reproduce

  1. As free test user C, opened /checkout/success?session_id=cs_test_made_up directly.
  2. Sent PATCH /rest/v1/profiles setting plan to 'pro' with C's token.
  3. Read where the plan is set in code.

Evidence

PM-04 · A repeated payment event does not grant credit or create an order twice

Pass

Steps to reproduce

  1. Completed one test purchase on staging.
  2. Resent the same event from the Stripe dashboard (Developers > Events > Resend).
  3. Counted bookings and confirmation emails for that session.

Evidence

DX-01 · Data cannot be read or changed without logging in

Pass

Steps to reproduce

  1. Signed out and recorded every data request the app makes.
  2. Repeated each one with no token, and queried every table in the public schema with the anon key.

Evidence

DX-02 · Database views follow the same access rules as their tables

Pass

Steps to reproduce

  1. Listed views in the public schema: one view, booking_summary.
  2. Queried it signed out and as user B.

Evidence

DX-03 · Responses carry only the fields the screen needs

Pass

Steps to reproduce

  1. Compared each main screen's response fields with what the screen shows, as user B.

Evidence

DX-04 · Web addresses carry no keys, tokens or personal data

Pass

Steps to reproduce

  1. Used sign-up, login, password reset, booking and invoice download, recording every address.

Evidence

IN-01 · Text one user enters cannot run as code in another user's browser

Pass

Steps to reproduce

  1. Entered the checklist's test values in the booking notes, profile bio and message fields as user A.
  2. Viewed each as user B and inspected the rendered elements.
  3. Deleted the test values.

Evidence

AR-02 · Ordinary users cannot reach admin pages or admin functions

Pass

Steps to reproduce

  1. As the admin test account, captured the requests behind /admin/users and /admin/refunds.
  2. Replayed each with user B's token, and opened the admin addresses as B.

Evidence

Reviewer's note: Passes on its own terms: an ordinary user's calls to admin functions are refused. But see AR-03, where a user can first make themselves an admin; fixing AR-03 is what makes this pass worth anything.

AR-03 · Users cannot write rows as someone else or change protected fields

Fail

Steps to reproduce

  1. Listed the policies on profiles: the UPDATE policy has USING (id = auth.uid()) and no column limits.
  2. As user B, sent PATCH /rest/v1/profiles?id=eq.[B] with {"role":"admin"}.
  3. Reloaded /admin/users as B.
Expected
The role change is refused or has no effect
Actual
User B became an admin and could open the admin user list

Evidence

SK-01 · Browser code holds no service-role or other secret keys

Pass

Steps to reproduce

  1. Searched every loaded JavaScript bundle and request on staging for key patterns.
  2. Decoded the one Supabase key found.

Evidence

SK-02 · The repository and its history hold no live secrets, and leaked keys are rotated

Pass

Steps to reproduce

  1. Ran gitleaks over all 412 commits of the repository.
  2. Checked .gitignore and the root config files.

Evidence

SK-03 · Third-party keys that must sit in the browser are restricted to this app and the APIs it uses

Fail

Steps to reproduce

  1. Listed third-party keys in the browser: one Google Maps key.
  2. Asked the owner for the key's settings screenshot.
Expected
Website restrictions set to the app's own addresses
Actual
The key has no website or API restrictions, so anyone can copy it and use it on the owner's bill

Evidence

AU-01 · Password reset links are single-use, expire, and do not reveal which emails have accounts

Pass

Steps to reproduce

  1. Requested resets for test user A and for an unregistered email.
  2. Used A's reset link once, then again.

Evidence

AU-02 · Logging out revokes the session on the server, and any copied access token stops at its expiry

Pass

Steps to reproduce

  1. Copied user A's tokens, logged out with the app's button, and tried to refresh.
  2. Called /rest/v1/bookings with the old access token until it stopped working.

Evidence

AU-03 · Sessions and access tokens expire on a defined schedule

Pass

Steps to reproduce

  1. Decoded an access token and asked for the session settings.

Evidence

AU-04 · Repeated login and code attempts are rate limited

Pass

Steps to reproduce

  1. Asked for the auth rate-limit settings.
  2. Made 10 wrong-password attempts on test user A on staging.

Evidence

PM-02 · Prices and totals are set on the server

Pass

Steps to reproduce

  1. Edited the price and amount fields in the request that starts checkout.
  2. Read the function that creates the checkout session and the money columns.

Evidence

BR-01 · Database backups are switched on and a restore has actually been tried

Pass

Steps to reproduce

  1. Asked for the backup settings and for a recent test restore.

Evidence

EL-01 · Error messages shown to users do not reveal internals

Pass

Steps to reproduce

  1. Sent malformed booking data, asked for a missing booking, called a function with no token.

Evidence

EL-02 · Passwords, tokens, card data and personal data are not written to logs

Fail

Steps to reproduce

  1. Searched the code for logging calls.
  2. Made a test payment on staging and asked for the matching function log lines.
Expected
Webhook logs hold the event id and type only
Actual
The full Stripe event, with the customer's email and phone number, is written to the function logs

Evidence

EL-03 · Failed logins and refused access attempts are recorded

Pass

Steps to reproduce

  1. Made 5 failed logins on test user A and one refused insert as user B, noting the times.

Evidence

TP-01 · Dependencies have no known critical or high vulnerabilities left unaddressed

Pass

Steps to reproduce

  1. Ran npm audit --omit=dev against package-lock.json.

Evidence

TP-02 · Personal data sent to analytics, trackers or chat widgets matches the privacy policy

Pass

Steps to reproduce

  1. Walked the main screens with the network tab open, listing third-party domains.
  2. Compared what reached each tracker with the privacy policy.

Evidence

DX-06 · Database functions called from the browser follow the same access rules as their tables

Pass

Steps to reproduce

  1. Listed the database functions in the public schema with their security setting.
  2. Called each signed out and as user B with user A's ids.

Evidence

PD-01 · Deleting an account removes the user's records, files and sessions

Pass

Steps to reproduce

  1. Created test user D with a booking and an uploaded photo, kept D's tokens, and deleted the account from Settings.

Evidence

PD-03 · Private uploads are not publicly reachable

Pass

Steps to reproduce

  1. Listed storage buckets.
  2. Uploaded a private photo as user A and opened its address signed out and as user B.

Evidence

SC-01 · The app publishes a working way to report security problems

Pass

Steps to reproduce

  1. Requested /.well-known/security.txt on the app's domain.
  2. Asked the owner to confirm a test message arrived.

Evidence

Outside this review

I ran every test in this report myself, on the build named above, from the test accounts named in the steps. Any AI help is listed above; I checked its output, and every result is my own.

— Sam Example, 2026-10-20

Checking a real report

A real report comes as a folder: report.json, the evidence folder, and a delivery folder holding this page, the exact checklist followed and fingerprint.txt, a list of every file's SHA-256. The report fingerprint above is the SHA-256 of that list, so any changed or swapped file gives a different fingerprint, and the registry page quotes it. Those files are not published for this sample.