HOW TO SECURE AN ONLOOK APP
Onlook Security Context
Onlook is an open-source “Cursor for designers” — a visual editor for Next.js + TailwindCSS apps where you edit the rendered page and Onlook writes the React code. The hosted version builds and deploys Next.js apps with AI assistance, commonly wired to Supabase for auth and database. The security consequence: the person shipping code is often a designer working visually, and the code that ships is Next.js — a framework where the server/client component boundary, server actions, and middleware each have their own way of quietly going wrong. The recurring shape we see is “the page looks perfect, the API behind it is wide open.”
Security Checklist
1. Keep secrets out of client components
In Next.js, anything imported into a client component ("use client") or prefixed NEXT_PUBLIC_ ships to the browser. Audit your .env file: only values that are genuinely safe in public should carry the NEXT_PUBLIC_ prefix. Read server-side secrets (Stripe secret key, service-role key, OpenAI key) only inside server components, route handlers, or server actions. Verify with the Token Leak Checker against the deployed URL — if a secret appears in the JS bundle, rotate it.
2. Enable RLS on every Supabase table
If your Onlook app uses Supabase, the anon key in the browser is safe only when Row Level Security is on for every table with a policy that scopes rows to the requesting user. One table with RLS off means the anon key reads the whole table. Run the Supabase RLS Checker and confirm select, insert, update, delete policies exist per table — not just select.
3. Authenticate every route handler
AI-generated route handlers (app/api/*/route.ts) frequently ship without an auth check. At the top of every handler that reads or writes user data, resolve the session and return 401 when absent:
const { data: { user } } = await supabase.auth.getUser()
if (!user) return new Response("Unauthorized", { status: 401 })
Middleware that protects pages does not automatically protect API routes — check the matcher (item 7).
4. Add ownership checks to ID-keyed routes
Auth says “this is a logged-in user.” It does not say “this user owns record 42.” Every handler that takes an ID must verify the record belongs to the requester before returning or mutating it. Generated CRUD skips this constantly — see BOLA in AI-generated CRUD.
5. Treat server actions as public endpoints
Next.js server actions compile to HTTP endpoints anyone can invoke with a crafted POST — the form UI in front of them is not a gate. Every server action needs the same treatment as a route handler: validate the input with a schema, check auth, check ownership. An action that “only the settings page calls” is still callable by anyone.
6. Validate input server-side in actions and handlers
Onlook’s AI wires forms quickly; the generated submit path often trusts the body. Add a Zod (or equivalent) schema at the boundary of every server action and route handler:
const Body = z.object({ amount: z.number().positive().max(10000) })
const data = Body.parse(await req.json()) // throws on invalid
7. Fix the middleware matcher to cover API routes
Middleware-based route protection commonly ships with a matcher like ["/dashboard/:path*"] — pages are gated, /api/* is not. Either extend the matcher to include API routes or (better) keep per-handler auth checks so a matcher gap fails closed, not open.
8. Disable source maps in production builds
Production source maps expose your original TypeScript — including logic comments and any secret that leaked into server-ish looking code that actually bundles client-side. Confirm productionBrowserSourceMaps is false (the Next.js default) and that no config or build flag re-enabled it. See source maps and .git exposed.
9. Review AI and visual edits before deploy
Onlook’s core loop — designer edits visually, code changes underneath — means code can ship without engineering review. Put the Onlook project in a git repo, require a PR for the deployed branch, and have someone who reads React skim every diff that touches app/api/, server actions, middleware, or .env usage.
10. Sanitize user content before rendering
React escapes by default, but generated code reaches for dangerouslySetInnerHTML the moment rich text or Markdown appears. Sanitize before rendering anything user- or LLM-authored; see LLM-rendered HTML/Markdown.
11. Validate file uploads
If the app accepts uploads, enforce a size cap, a MIME allowlist, and server-generated filenames — in the route handler or Supabase Storage policy, not just in the dropzone component. The bug shape is an “image upload” that accepts anything; see file upload zip-slip / XXE.
12. Harden auth settings in Supabase
In the Supabase project settings: require email confirmation, set a minimum password length (8+), and restrict OAuth redirect URLs to your production domain. A wildcard redirect is an account-takeover surface — see SSRF / open redirect / OAuth.
13. Rate-limit expensive endpoints
Any route handler or server action that calls an LLM or other paid API needs a per-user and per-IP limit. Track attempts in a table and reject when over. Without this, one script can run up your bill in minutes.
14. Test with two accounts (BOLA)
Sign up as user A, create a record, copy its URL and API request. Sign in as user B, replay both. If B sees A’s data, either an ownership check or an RLS policy is missing. This five-minute test catches the single most common finding in Next.js + Supabase apps.
15. Run a security scan
The Vibe Code Scanner covers the deploy-side patterns; the full VibeEval scan adds BOLA, role-escalation, and secret-exposure probes against the deployed URL.
Common Vulnerabilities in Onlook Apps
Secrets in the Client Bundle
A key read in a client component, or renamed to NEXT_PUBLIC_ to “make the build work,” ships to every visitor. Anything that ever appeared in the bundle needs rotation, not just removal.
Route Handlers Without Auth
Generated app/api handlers return data to anyone. Pages look protected because middleware gates them; the JSON behind them is open.
RLS Off on One Table
Supabase apps fail on the one table where RLS was skipped — often a join or log table added late. The anon key then reads it directly, no app code involved.
Server Actions Trusted as Internal
Actions are treated as function calls, but they are public HTTP endpoints. Missing validation and auth in an action is the same bug as an open API route.
Related Resources
Free Self-Audit Suite
Five free scanners.
Onlook Security Checklist
Pre-launch checklist in severity order.
Is Onlook Safe?
In-depth analysis of Onlook’s defaults and what to audit.
Automate Your Security Checks
VibeEval scans your Onlook application against every category above plus 305 more probes. Findings ship with fix prompts you can paste into Onlook’s AI chat for one-shot remediation.
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.