HOW TO SECURE A TEMPO APP
Tempo Security Context
Tempo (tempo.new, by Tempo Labs) pairs a drag-and-drop visual editor with AI agents that generate React (Vite) code, typically wired to Supabase for auth and database. That combination is powerful and specific: designers and PMs can ship real code changes through the visual editor, and the entire backend trust boundary lives in Supabase Row Level Security — because the Supabase anon key ships in the client bundle by design. The recurring shape we see is a polished frontend with correct-looking route guards, sitting on tables where RLS was never enabled.
Security Checklist
1. Enable RLS on every Supabase table
The anon key in a Tempo app’s bundle can query any table directly via the Supabase REST API — your React components are not the access-control layer. In the Supabase dashboard or via SQL, confirm ALTER TABLE <table> ENABLE ROW LEVEL SECURITY has run on every table, then confirm each has policies. A table with RLS enabled but no policies denies everything; a table without RLS allows everything. Run the Supabase RLS Checker against your deployed URL to verify from the outside.
2. Write RLS policies that scope to the owner
USING (true) for authenticated users is not a scope — it means any logged-in user reads every row. User-scoped tables need policies like:
CREATE POLICY "own rows only" ON projects
FOR SELECT USING (auth.uid() = user_id);
Repeat for INSERT (WITH CHECK), UPDATE, and DELETE. AI-generated schemas frequently get SELECT right and leave UPDATE/DELETE open.
3. Treat React route guards as UX, not auth
Tempo’s generated apps commonly gate pages with a client-side check (if (!session) navigate("/login")). That hides the page; it does not protect the data. Anyone can call the Supabase API with the anon key from curl and skip your React app entirely. Every real restriction must exist as an RLS policy or inside a Supabase Edge Function that verifies the JWT.
4. Keep server-only secrets out of the Vite bundle
Anything prefixed VITE_ is compiled into the client bundle. The Supabase URL and anon key belong there; a Stripe secret key, OpenAI key, or Supabase service_role key does not. Grep the built output:
grep -rE 'sk_(live|test)_|sk-[A-Za-z0-9]{20,}|service_role' dist/
Anything found ships to every visitor. Move those calls into Edge Functions and rotate the key. The Token Leak Checker runs this check against the deployed app.
5. Remove storyboard and dev routes from production builds
Tempo’s visual workflow produces storyboards, component playgrounds, and preview routes during development. Before launch, confirm none of them are reachable on the production URL — try /storyboard, /tempobook, /dev, /playground, and any route you remember using in the editor. A component playground that renders with live Supabase data is an unauthenticated window into your app.
6. Review what non-engineers shipped
The visual editor means code changes land from people who never think about auth. Establish one rule: any change that touches data fetching, auth state, or a new table gets an engineering review before deploy. At minimum, diff the generated code after each visual-editing session — the change you didn’t know about is the one that ships the hole.
7. Validate input in Edge Functions
If your Tempo app uses Supabase Edge Functions, validate the body at the top of every function — Zod or manual checks. The generated function trusts await req.json() as-is; a crafted payload can write unexpected columns or oversized values.
8. Strip role and privilege fields from client writes
If your profiles table has a role, is_admin, or plan column, an open UPDATE policy lets a user promote themselves. Either exclude those columns via a column-level policy / trigger, or route privilege changes through an Edge Function that checks the caller’s role server-side.
9. Sanitize user content before rendering
AI-generated React components reach for dangerouslySetInnerHTML when asked to render rich text or Markdown. If any user- or LLM-generated content renders as HTML, sanitize it first (DOMPurify or equivalent). See LLM-rendered HTML/Markdown.
10. Lock down Supabase Storage buckets
Generated upload features often create a public bucket. Confirm every bucket’s public flag matches intent, add storage policies scoping objects to their owner, and cap upload size and MIME types in the upload code. See file upload zip-slip / XXE for what happens when uploads process server-side.
11. Harden Supabase auth settings
In the Supabase auth settings: enable email confirmation, set a minimum password length (8+), and disable any OAuth providers you wired up during prototyping but don’t use. Restrict OAuth redirect URLs to the production domain — leftover dev URLs are an account-takeover surface (SSRF / open redirect / OAuth).
12. Rate-limit expensive endpoints
Any Edge Function that calls an LLM or external paid API needs a per-user limit — track calls in a table and reject when over. The anon key means anyone can invoke your public functions in a loop.
13. Don’t ship source maps
Vite production builds omit source maps by default; confirm nothing re-enabled them (build.sourcemap in vite.config.ts). Shipped maps hand attackers your unminified source, including every route and query. See source maps and exposed .git.
14. Test with two accounts (BOLA)
Sign up as user A, create a record, note its ID. Sign in as user B and query the same record through the Supabase client. If it returns, your RLS policy scopes to “authenticated” instead of the owner. This is the single highest-yield test for a Tempo app. See BOLA in AI-generated CRUD.
15. Re-test after every AI edit, then scan
Regeneration can silently reintroduce a gap you already fixed — a re-generated component that queries a different table, a rewritten policy, a restored dev route. Re-run the two-account test after each significant AI edit, and run the Vibe Code Scanner against the deployed URL before each launch.
Common Vulnerabilities in Tempo Apps
RLS Missing or Too Broad
The anon key is in the bundle by design, so RLS is the only wall. Tables created quickly during a build session frequently launch with RLS disabled or with an “any authenticated user” policy on user-scoped data.
Client-Side-Only Auth
Route guards in React with no server-side enforcement. The app looks locked; the API is open.
Dev Surfaces in Production
Storyboards, component playgrounds, and preview routes that were useful in the editor and never removed from the deployed build.
Prototype Keys in the Bundle
A third-party API key pasted into a component during prototyping, compiled into the bundle by Vite, and shipped to every visitor.
Related Resources
Free Self-Audit Suite
Five free scanners.
Supabase RLS Checker
Verify every table’s RLS from outside, using only your app’s URL.
Vibe Coding Vulnerabilities
Full vulnerability taxonomy across AI app builders.
Automate Your Security Checks
VibeEval scans your Tempo application against every category above plus 305 more probes. Findings ship with fix prompts you can paste back into Tempo’s AI chat for one-shot remediation.
SCAN YOUR TEMPO APP
14-day trial. No card. Results in under 60 seconds.