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.

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.

START FREE SCAN