← Writing

4 — Authorisation is a vibe

Every endpoint in your app checks permissions. No two of them check permissions the same way. And on the table that actually matters, Row Level Security was never turned on.

I know this one from the inside, not from a write-up. I'd vibe-coded a concept app on Lovable to demo to a handful of potential clients — each with their own seeded dummy data, so every demo would look tailored to them. Then one client's login pulled up a dummy itinerary that belonged to somebody else's dummy airline. Nothing real was at risk, because the data on both sides was fake. But the query that surfaced it was completely real, and so was the missing predicate behind it.

Authorisation generated one handler at a time means forty endpoints with forty separate interpretations of who's allowed to do what, and no policy anyone could actually read end to end. "Who can see this?" becomes a question you can only answer by grepping the codebase. The multi-tenant version is worse: one missing WHERE org_id = ? isn't a bug, it's a data disclosure — usually invisible in the demo, because the founder is the only row in the table. I got lucky: mine had two.

Flowchart comparing vibe-coded authorisation, where GET /invoices/:id and DELETE /invoices/:id have missing or wrong checks while other handlers are fine, against a single policy layer that every request passes through with deny-by-default.

One of these can be shown to an auditor. The other can only be read, handler by handler.

Supabase and Postgres make this sharper than most stacks, because Row Level Security is opt-in: the anon key is public by design, and RLS is the only thing standing between that key and the whole database. Vibe-coded apps ship with it off, or with a placeholder USING (true) policy, because that's what makes the demo work on the first try. The flagship story here is CVE-2025-48757 — what researchers started calling "the Lovable curse." Matt Palmer and Kody Low found it in March 2025 via a Lovable-built app; a scan of 1,645 public Lovable apps turned up 303 vulnerable endpoints across 170 projects, a 10.3% hit rate, CVSS 9.3, unauthenticated read and write access to emails, addresses, payment details, and API keys. The telling detail is the remediation: Lovable's fix was a scanner that checked whether a policy existed on a table, not whether the policy was correct — a checklist bolted on where a policy layer should have been.

Flowchart: a query for invoices WHERE id = 42, with no org_id predicate and RLS off, returns row 42 from a table containing rows from multiple organisations — no matter which org actually asked.

One missing predicate, and the query can't tell your organisation from anyone else's.

This is worth taking seriously even without the AI angle: OWASP has ranked Broken Access Control the #1 web risk since 2021, found in 94% of tested applications, with more occurrences than any other category by a wide margin. Vibe coding doesn't invent this risk — it ships it faster and hides it better, because nobody had to consciously decide to skip the check; it was simply never generated. There's a compliance angle too, worth naming from the regtech side: a SOC 2 auditor asks for the access-control policy and evidence it's enforced. A policy engine or a coherent set of RLS rules is something you can hand over, diff, and test. Forty inline if statements scattered across handlers is an archaeology project, not an artifact.

The fix is one policy layer, deny by default, tested independently of any feature. Handlers ask the policy what's allowed — they never decide it themselves.

If you've shipped fast with AI tools and want a second pair of eyes before it goes further, that's exactly what a vibe code audit is for — there's a free one you can run yourself, or book time with me directly.

Run the free audit Book a call