WalkaroundLAUNCH CHECK · SAMPLE
CHECKED
tomo.walkaround.dev20 OCT 2026
21:12:12 GMT
DESKTOP WIDTH. This sample page, photographed from outside it, with nothing clicked and nothing typed.
PHONE WIDTH, cropped to the top of the page. The checks are the lines below, run against the requests they made: a photograph is not one of the twelve, and it cannot tell you what a stranger can reach.
These two photographs are of this sample page, taken from outside it at desktop and phone width. They are on our sample receipts: they are a look at the page, not part of the check.
×KEY IN PAGEEXPOSED
A key with full write access to your database is inside the JavaScript your homepage ships to every browser.
index-tomo-7f3c.js line 10:
"service_role":"eyJhbGciOiJIUzI1NiIsInR5…"
index-tomo-7f3c.js line 10:
"SUPABASE_SERVICE_ROLE_KEY":"eyJhbGciOiJIUzI1NiIsInR5…"
index-tomo-7f3c.js line 15:
"SUPABASE_SERVICE_ROLE_KEY":"SUPABASE_SERVICE_ROLE_KE…"
PASTE THIS INTO LOVABLEMy service role key is in the browser bundle. Move every use of it to a server function, replace the client copy with the public anon key, and then rotate the service key in Supabase so the old one stops working. Rotating is the step that matters: until it is done, anyone who already saved that key still has full write access, even after the code is fixed.
×TABLE: bookingsNO RULES
1,284 rows readable by anyone, using the key your site already ships to every browser. 3 tables are open like this (bookings, members, classes).
GET https://tomo.walkaround.dev/rest/v1/bookings?select=*
200 OK → 1,284 rows, incl. id, created_at, customer_email, name
GET https://tomo.walkaround.dev/rest/v1/members?select=*
200 OK → 312 rows, incl. id, email, plan, joined_at
GET https://tomo.walkaround.dev/rest/v1/classes?select=*
200 OK → 24 rows, incl. id, name, when_label, capacity
PASTE THIS INTO LOVABLETurn on row level security for bookings and members, classes and add a policy so a row is only visible to the user it belongs to. Then check every query on the site still works while signed in. The table stays open to anyone until that policy exists, not just until RLS is switched on.
×ROUTE AUTHNO LOGIN
2 of 8 app pages answered with data to a request that carried no login — including addresses and records.
GET https://tomo.walkaround.dev/bookings (no cookie, no login)
200 OK → /bookings returned amelia.hart@example.com, dev.patel@example.com
GET https://tomo.walkaround.dev/account (no cookie, no login)
200 OK → /account returned amelia.hart@example.com, dev.patel@example.com
PASTE THIS INTO LOVABLEPages that show a signed-in user their own records are answering strangers. Add a session check at the top of each of these pages and send anyone without a session to the login page, then check the data behind them is scoped to the signed-in user rather than to whoever asks.
×RECORDS BY IDREADABLE
Asking for one record by its number returned that record — with no login, from outside the app.
GET https://tomo.walkaround.dev/rest/v1/bookings?id=eq.1&select=*
200 OK → 1 row, incl. id, created_at, customer_email, name
GET https://tomo.walkaround.dev/rest/v1/bookings?id=eq.2&select=*
200 OK → 1 row, incl. id, created_at, customer_email, name
GET https://tomo.walkaround.dev/rest/v1/members?id=eq.1&select=*
200 OK → 1 row, incl. id, email, plan, joined_at
PASTE THIS INTO LOVABLEReading a row by its id is open to anyone. Add a policy on bookings, members, classes that only returns a row when the row belongs to the signed-in user, and make sure the id in the request cannot be swapped for someone else's. Then sign out and try the same request: it should come back empty.
×ENV IN CLIENTEXPOSED
Your build puts a server-side config file in the browser, and it holds keys that are not public.
GET https://tomo.walkaround.dev/env.js
200 OK → SUPABASE_SERVICE_ROLE_KEY
index-tomo-7f3c.js line 15 (client env object):
"SUPABASE_SERVICE_ROLE_KEY":"SUPABASE_SERVICE_ROLE_KE…"
PASTE THIS INTO LOVABLEMy build is shipping a server config file to the browser. Move the values in it to server-side environment variables, keep only the public ones in the client, and rotate anything that was in there. Anything the browser can read, a visitor can read.
×SUPABASE CONFIGMISCONFIG
The key your site hands to the browser is not the public anon key. It has the service role, which reads and writes every row.
project: https://tomostudio-demo.supabase.co
client key role: service_role (expected: anon)
key: "eyJhbGciOiJIUzI1NiIsInR5…" in index-tomo-7f3c.js
PASTE THIS INTO LOVABLEThe key my site sends to the browser is the service role key. Swap it for the anon key everywhere the browser sees it, keep the service role key on the server only, and create a server function for anything that needed the extra permissions.
×OPEN SIGNUPSOPEN
Anyone can create an account with an address they do not own, because signups are open and confirmation is off.
GET /auth/v1/settings
mailer_autoconfirm: true
disable_signup: false
PASTE THIS INTO LOVABLETurn email confirmation back on in Supabase authentication settings, so an account only starts working once the address is confirmed. Anyone can currently register with an address that is not theirs.
×SECURITY HEADERSWEAK
Ordinary hardening is missing: 2 of 3 checks.
scheme: https
strict-transport-security: absent
content-security-policy: absent
PASTE THIS INTO LOVABLEAsk your builder to add the missing headers — no Strict-Transport-Security header; no Content-Security-Policy. These are one-line changes at your host and they are what stops the cheap attacks.
✓PUBLIC FILESPASS
Bucket listing refused on 8 of 8 probed bucket names. Nothing downloadable by a stranger.
✓ADMIN ROUTESPASS
The admin addresses probed did not answer a stranger.
✓AUTH SETTINGSPASS
Redirect list is not a wildcard and anonymous sign-ins are off.
✓SOURCE MAPSPASS
No source map or source directory reachable (2 tried).
CHECKED: 12. SKIPPED: nothing — all twelve ran. NOT REACHABLE: anything behind a login; the server behind the app, and your Supabase console.
SAMPLE RECEIPT. This is one of our own demo apps, and every line on it is unsealed and free to read. Nothing on this receipt is for sale.
WHAT THIS IS NOT. Twelve checks against the outside of your app in 0.14 seconds, on 20 OCT 2026.
CHECKED: 12.
SKIPPED: nothing — all twelve ran.
NOT REACHABLE: anything behind a login; the server behind the app, and your Supabase console.
A clean receipt is not a guarantee of safety, and an open line is not a break-in: it is a door left unlocked, and anyone can try the handle.
ref 56 quartz 7 lilac fog · walkaround.app