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.

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.

START FREE SCAN