TEMPO SECURITY CHECKLIST

Tempo (tempo.new) combines a visual drag-and-drop editor with AI agents that generate React (Vite) code, usually on a Supabase backend. The security consequences follow directly from that architecture: the Supabase anon key ships in the client bundle, so Row Level Security is the entire server-side access-control story — and the visual editor means non-engineers can ship code changes nobody security-reviews. The checklist below is what we look for first when we audit a Tempo app.

Treat Critical as launch-blocking. High is week-one. Medium is the cleanup once you have users.

How to use this checklist

Walk it once with the Supabase dashboard open and two test accounts in hand, ticking items as you go. After each fix, sign in as user B and try to read user A’s data. After the whole list passes, run a black-box scan against the deployed URL.

Critical (fix before launch)

1. Enable and verify RLS on every table

Why it matters. The anon key in the bundle lets anyone query the Supabase REST API directly. A table without RLS enabled is fully readable and writable by any visitor, regardless of what your React app shows.

How to check. In the Supabase SQL editor: SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';. Every row must show rowsecurity = true, and every table needs at least one policy. Then verify from outside with the Supabase RLS Checker.

How to fix. ALTER TABLE <table> ENABLE ROW LEVEL SECURITY; plus owner-scoped policies (auth.uid() = user_id) for all four operations. “Authenticated” alone is not a scope for user data.

2. Confirm no server-side enforcement is missing behind React route guards

Why it matters. Tempo-generated apps gate pages client-side (if (!session) navigate("/login")). That is UX, not security — the data API is the Supabase endpoint, reachable with curl and the anon key.

How to check. For every “protected” page, identify the tables it reads, then query those tables with the anon key and no session (and with a second user’s session). Anything that returns is unprotected.

How to fix. Enforce every restriction as an RLS policy or inside an Edge Function that verifies the JWT. Keep the route guards for UX only.

3. Audit the bundle for API keys added during prototyping

Why it matters. Vite compiles every VITE_-prefixed variable and every inline string into the client bundle. Keys pasted into components during prototyping — OpenAI, Stripe secret, Resend, the Supabase service_role key — ship to every visitor.

How to check. Build and grep: grep -rE 'sk_(live|test)_|sk-[A-Za-z0-9]{20,}|re_[A-Za-z0-9]{20,}|service_role' dist/. Also run the Token Leak Checker against the deployed URL.

How to fix. Move every server-side call into a Supabase Edge Function; keep only the Supabase URL and anon key client-side. Rotate any key that ever appeared in the bundle or in git history.

4. Remove storyboard, playground, and dev routes from production

Why it matters. Tempo’s visual workflow generates storyboards and component playgrounds during development. Left in the production build, they render components — often with live data wiring — outside any auth flow, and they map your internal UI for attackers.

How to check. On the production URL, try /storyboard, /tempobook, /playground, /dev, and any preview route you used in the editor. Also grep the route config for storyboard imports.

How to fix. Exclude dev-only routes from the production build (conditional on import.meta.env.PROD) or delete them before deploy. Re-check after every AI edit — regeneration can restore them.

5. Test cross-user access with two accounts (BOLA)

Why it matters. The most common finding in Supabase-backed AI-generated apps is a policy that scopes to “any authenticated user” on user-scoped data. Auth passes, ownership was never checked. See BOLA in AI-generated CRUD.

How to check. As user A, create a record and note its ID. As user B, fetch it via the Supabase client. Expect an empty result or an error, never the row.

How to fix. Rewrite the policy to auth.uid() = user_id (and WITH CHECK on writes). Re-run the test for every user-scoped table.

6. Strip privilege fields from client-writable columns

Why it matters. If profiles has role, is_admin, or plan, and the UPDATE policy allows owners to update their own row, a user can promote themselves — RLS policies are row-level, not column-level, by default.

How to check. As a normal user, attempt update profiles set role = 'admin' where id = auth.uid() through the client. It must fail.

How to fix. Guard privileged columns with a trigger or a column-check in the policy, or route all privilege changes through an Edge Function that verifies the caller’s role.

High (fix in the first week)

7. Lock down Supabase Storage buckets

Generated upload features often create public buckets. Confirm each bucket’s public flag matches intent, add owner-scoped storage policies, and enforce size and MIME limits in the upload code.

8. Add rate limiting on Edge Functions that cost money

LLM calls, email sends, and external APIs invoked from public Edge Functions can be looped by anyone holding the anon key. Track per-user counts in a table and reject when over.

9. Restrict OAuth redirect URLs to production

Prototyping leaves localhost and preview URLs in the Supabase auth provider config. Remove everything but the production domain. See SSRF / open redirect / OAuth.

10. Establish review for visual-editor changes

Designers and PMs shipping code through Tempo’s visual editor is the point of the platform — and the reason changes land that no engineer sees. Require review on any change touching data fetching, auth, or schema.

11. Verify webhook handlers check signatures

Stripe and similar providers sign webhooks. Edge Functions that receive them usually skip verification in the generated version — fix before payments go live.

12. Disable email enumeration in the auth flow

Confirm signup, password reset, and magic link return identical responses for “user exists” and “user does not exist.”

Medium (fix when you can)

13. Confirm source maps are off in production

Check build.sourcemap in vite.config.ts and the deployed assets for .map files. See source maps and exposed .git.

14. Sanitize rendered user content

Any component using dangerouslySetInnerHTML on user or LLM output needs DOMPurify or equivalent. See LLM-rendered HTML/Markdown.

15. Remove seed data and test accounts

Delete demo users and sample rows created during prototyping — especially any test account with an elevated role.

16. Document each table’s access model

A written record of which roles read/write each table gives you something to diff against after the AI regenerates schema or policies.

17. Tighten Supabase auth settings

Email confirmation on, minimum password length 8+, unused OAuth providers disabled.

18. Set up monitoring for enumeration patterns

Watch Supabase logs for bulk reads from a single IP or a single user iterating IDs — that’s someone mapping your data.

After every Tempo AI edit or regeneration

  • Re-run the two-account cross-user test on user-scoped tables.
  • Confirm storyboard/dev/playground routes did not reappear in the build.
  • Grep the new bundle for key prefixes (sk_, sk-, service_role).
  • Diff RLS policies if the AI touched the schema.
  • Verify route guards weren’t substituted for a removed server-side check.

Common attack patterns we see in Tempo apps

The RLS-less table. A table added mid-session with the visual editor, never enabled for RLS; anyone with the anon key dumps it with one REST call.

The client-only paywall. Premium features gated by a React conditional; the underlying table is readable by every authenticated user.

The storyboard leak. A component playground left at a guessable route, rendering an admin table component wired to live data.

The prototype key. An OpenAI key pasted into a component in week one, still in the bundle at launch; a scraper finds it and drains the quota.

How to Secure Tempo

Step-by-step guide for hardening a Tempo app — RLS patterns, bundle hygiene, and dev-route cleanup in long form.

Is Tempo Safe?

In-depth analysis of Tempo’s defaults — what the platform handles, what the generated app leaves open.

Automate Your Checklist

A checklist tells you what to look for. A scanner tells you what’s actually broken in the deployed app right now. VibeEval drives a real browser through your Tempo deployment, attempts the cross-user BOLA and anon-key enumeration attacks above, and reports what got through — with the table or policy to fix.

SCAN YOUR TEMPO APP

14-day trial. No card. Results in under 60 seconds.

START FREE SCAN