IS TEMPO SAFE? SECURITY ANALYSIS | VIBEEVAL
Is Tempo safe? The short answer
Tempo the platform is safe. Apps built with Tempo are safe when the developer adds the layers the generator left out. Tempo (tempo.new, by Tempo Labs) generates a mainstream stack — React (Vite) frontend, Supabase auth and database — and the hosting, HTTPS, and deploys are handled. The generated apps consistently ship with the same set of gaps: Supabase tables without correct RLS, auth enforced only in React route guards, dev-time storyboard routes reachable in production, and prototype API keys compiled into the bundle. Each gap is a short fix; collectively they are the difference between a demo and a production app.
The Tempo generation pattern
Tempo’s combination of AI agents and a visual editor optimizes for “this works and looks right on first try.” That has a predictable cost:
- RLS lags the schema. The AI creates tables as the app grows; enabling and correctly scoping Row Level Security on each one is a separate step that frequently doesn’t happen.
- Auth lives in the client. Generated route guards check the session in React. The Supabase API behind them — reachable with the bundled anon key — is only as protected as the RLS policies.
- Dev surfaces persist. Storyboards and component playgrounds are central to the visual workflow; nothing forces their removal from the production build.
- Keys hardcode. A key pasted into a component during prototyping is compiled into the bundle by Vite and served to every visitor.
- Non-engineers ship. Visual edits from designers and PMs land as real code changes with no security review in the loop.
- Regeneration regresses. An AI edit can rewrite a component or policy and silently reintroduce a gap you already fixed.
These are not bugs in Tempo; they are characteristics of AI generation plus visual editing under “make this work” pressure. Knowing the pattern is the entire fix — every audit item below targets one of those failure modes.
The 6 areas to harden in every Tempo app
1. Row Level Security on every table
The anon key in the bundle means anyone can query the Supabase REST API directly. RLS is the wall — and it has to be enabled and correctly scoped on every table.
Fix. Enable RLS everywhere, then write owner-scoped policies:
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY "read own" ON projects
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "insert own" ON projects
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "update own" ON projects
FOR UPDATE USING (auth.uid() = user_id);
Cover all four operations — AI-generated schemas often get SELECT right and leave UPDATE and DELETE open. Verify from outside with the Supabase RLS Checker.
2. Server-side enforcement behind every client guard
A React route guard is a UX nicety. The security question is: if I skip the React app entirely and hit Supabase with the anon key, what can I read and write?
Fix. For every “protected” feature, enforce the restriction at the data layer — an RLS policy, or an Edge Function that verifies the JWT:
// Supabase Edge Function: verify the caller before doing anything
import { createClient } from "jsr:@supabase/supabase-js@2";
Deno.serve(async (req) => {
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
{ global: { headers: { Authorization: req.headers.get("Authorization")! } } },
);
const { data: { user } } = await supabase.auth.getUser();
if (!user) return new Response("Unauthorized", { status: 401 });
// ...proceed as this verified user
});
3. Ownership checks (BOLA / IDOR)
Auth confirms the request has a valid session. It does not confirm the session owns the row being requested. Policies that scope to authenticated instead of auth.uid() = user_id are the Supabase flavor of BOLA — see BOLA in AI-generated CRUD.
Fix. Manual test, every launch: sign up as user A, create a record, sign in as user B, fetch the same record through the Supabase client. Anything other than an empty result is a policy bug.
4. Bundle hygiene
Everything in a Vite build is public. The Supabase URL and anon key belong in the bundle; nothing else secret does.
Fix. Grep the built output before every deploy:
npm run build
grep -rE 'sk_(live|test)_|sk-[A-Za-z0-9]{20,}|re_[A-Za-z0-9]{20,}|service_role' dist/
Move flagged calls into Edge Functions (secrets in Deno.env), rotate the exposed keys — including anything that ever appeared in git history — and re-run the Token Leak Checker against the deployed URL.
5. Production build surface
Storyboards, component playgrounds, and preview routes are development tooling. In production they render components outside any auth flow.
Fix. Exclude dev-only routes from production builds:
{import.meta.env.DEV && <Route path="/storyboard/*" element={<Storyboards />} />}
Then verify on the deployed URL that /storyboard, /tempobook, /playground, and similar paths 404. Confirm source maps are off (build.sourcemap: false in vite.config.ts) — see source maps and exposed .git.
6. Review discipline for visual edits
The visual editor’s entire purpose is letting non-engineers change the app. The mitigation isn’t technical, it’s process.
Fix. One rule, enforced: any change touching data fetching, auth state, schema, or a new integration gets an engineering look before deploy. After every AI edit or regeneration, re-run the two-account test and the bundle grep — regeneration can quietly reintroduce a gap you already fixed.
Storage and uploads — the recurring extra
If the app handles file uploads, check the Supabase Storage side: buckets created by generated code are sometimes public, storage policies are often missing, and upload handlers rarely cap size or MIME type. Scope objects to their owner with storage policies, and validate uploads in code. See file upload zip-slip / XXE for the server-side processing variant.
Pre-launch Tempo checklist
A practical sequence to run before pushing a Tempo app to a real audience:
- RLS enabled on every table, with owner-scoped policies for all four operations.
- Two-account cross-user test passes on every user-scoped table.
- No secrets in the bundle beyond the Supabase URL and anon key.
- Storyboard / playground / dev routes 404 on the production URL.
- Privilege columns (
role,is_admin) not writable by their owner. - OAuth redirect URLs restricted to the production domain.
- Storage buckets private unless deliberately public, with owner-scoped policies.
- Rate limiting on Edge Functions that call paid APIs.
- Run VibeEval against the deployed URL for the dynamic pass.
Tempo vs Lovable vs v0 — security positioning
- Tempo. Visual editor + AI agents on React/Vite + Supabase. Failure modes concentrate in RLS, client-only auth, dev-route leakage, and unreviewed visual edits.
- Lovable. Opinionated Supabase + frontend generator. Same RLS and anon-key failure modes, without the visual-editor review gap.
- v0. UI-first generator. Failure modes follow whatever backend you wire up.
Pick the tool whose failure modes you have the bandwidth to audit. For Tempo, that means someone who is comfortable reading RLS policies and willing to re-test after every visual edit — the platform will not do it for you.
The verdict
Tempo produces working, good-looking React apps quickly, and its Supabase foundation is solid managed infrastructure. Security is not the generator’s focus. Treat every Tempo-built app as needing a security pass before production: RLS on every table, server-side enforcement behind every guard, a clean bundle, no dev routes, and a re-test after every AI edit. The list is mechanical and scannable. Run it before launch, every launch.
Related resources
- How to Secure Tempo — step-by-step hardening guide
- Tempo Security Checklist — pre-launch checklist
- Supabase RLS Checker — verify RLS from outside
- Token Leak Checker — find exposed API keys in your deployed app
- Vibe Coding Vulnerabilities — full vulnerability taxonomy across AI tools
- OWASP Top 10 for AI Code
Scan your Tempo app
Let VibeEval scan your deployed Tempo application for the RLS, client-only-auth, BOLA, and credential-leakage patterns that AI generators most often leave in.
COMMON QUESTIONS
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.