Vibe-coded & AI-built apps
Security testing for vibe-coded applications
Applications built largely by AI coding tools fail in a predictable and narrow set of ways. They are rarely missing input validation. They are frequently missing authorization entirely, and the database is doing nothing to stop it.
The context
Why this is different
There is nothing wrong with building this way. Teams ship products in weeks that would previously have taken a year, and the code an assistant writes is frequently cleaner than what a rushed human writes at 2am. The problem is not code quality in the ordinary sense.
The problem is that a coding assistant optimises for the request in front of it. Asked to build a page that lets a user see their invoices, it will build a page that lets a user see invoices. Whether it checks that the invoices belong to that user depends on whether the prompt said so, and prompts usually do not, because the person writing them is thinking about the feature rather than the threat model.
So the bugs cluster. Across AI-assisted codebases the same handful of gaps appear again and again, and almost all of them sit in the authorization layer or in the gap between what the frontend enforces and what the backend actually checks. That predictability is useful: it means testing can be targeted rather than exploratory, which is why this engagement is cheaper than a general application pentest.
What we look for
The failure classes that actually show up.
Supabase and Firebase rules that permit everything
The single most common serious finding in this category. Row Level Security is disabled, or enabled with a policy that evaluates to true for any authenticated user. Because the client holds an anon key and talks to the database directly, an attacker does not need your application at all.
create policy "enable read" on invoices for select using (true); -- any authenticated user reads every row in the table
Server routes with authentication but no authorization
The route checks that a session exists and then trusts the identifier in the request. Being logged in becomes sufficient to read or modify anyone's data. This passes every manual test the developer performs, because they only ever test as themselves.
// authenticated, but never checks the row belongs to the caller const { data } = await db.from('orders').select().eq('id', params.id)
Unprotected server actions and API routes
Next.js server actions and route handlers are network endpoints. Generated code frequently guards the UI that calls them rather than the action itself, so the button is hidden from users who should not see it and the endpoint remains callable by anyone who knows the name.
Keys in the client bundle
Service role keys, admin API tokens and third-party secrets end up prefixed with NEXT_PUBLIC_ or referenced in a client component, which ships them to every visitor. We search the built bundle rather than the source, because the two disagree more often than teams expect.
grep -rEo '(sk-[A-Za-z0-9]{20,}|service_role|eyJ[A-Za-z0-9_-]{20,})' .next/static/
Sequential and guessable identifiers
Auto-incrementing integer primary keys exposed directly in URLs and API responses. Combined with a missing authorization check this turns a single bug into a full data extraction, because enumeration is trivial.
No rate limiting anywhere
Login, password reset, signup, and any endpoint that calls a paid API. On AI-built products the last one matters commercially as well as security-wise: an unauthenticated endpoint that proxies to a model provider is someone else's free compute and your invoice.
Permissive CORS and missing origin checks
Wildcard origins with credentials enabled, or a reflected origin header, generated because it made a local development error go away. It usually did, and it also made every authenticated endpoint callable from any site the user visits.
Dependency and template inheritance
Starter templates and scaffolds carry their own history. We check what the generated project pulled in, whether the versions have known advisories, and whether example credentials or seed users from the template survived into production.
How we test it
The engagement.
- 01
Map what the client can reach directly
For products on Supabase, Firebase or similar, the browser talks to the database. We enumerate every table, view, storage bucket and function reachable with the public key, from an unauthenticated position and from a normal user account.
- 02
Policy review, row by row
Every RLS policy read against what it actually permits rather than what it was intended to permit. Policies using true, policies missing a with check clause on writes, and tables with RLS enabled but no policy at all.
- 03
Two-tenant authorization sweep
We provision two independent accounts and replay every request from one as the other, comparing responses rather than status codes. This is the single highest-yield test on AI-built products and it cannot be automated meaningfully without knowing your permission model.
- 04
Bundle and endpoint extraction
The built client bundle is searched for secrets, internal endpoints, feature flags and server logic that leaked into shipped code. Route handlers and server actions are enumerated from the build output, including ones the UI never calls.
- 05
Business logic and abuse
Manual testing of the flows that carry money or trust: subscription state, credit consumption, invitations, role changes, and anything that calls a paid third-party API on a user's behalf.
- 06
Fix, verify, retest
Findings come with the specific policy or middleware change that closes them, written for your stack. When you have deployed the fixes we retest every finding and reissue the report at no extra cost.
What you get
Deliverables
- From
- $4,500
- Typical duration
- 1 to 2 weeks
- Findings report with reproduction steps for every issue
- Row Level Security policy review with corrected policies written out
- Authorization matrix showing what each role can actually reach
- Secrets found in the built client bundle, with rotation guidance
- Prioritised fix list your engineers can work through in order
- Free retest and reissued report once you have remediated
- Customer-shareable attestation letter for security questionnaires
Questions
The ones engineers ask.
Not inherently, and we are not going to lecture you about it. What matters is that the resulting code has a specific and well-understood failure profile, heavily concentrated in authorization. That is testable in a focused way, which makes the engagement shorter and cheaper than a general application pentest.
A Row Level Security policy that permits more than intended, or a table where RLS was never enabled. Because the anon key is public by design and the client talks to Postgres directly, RLS is your actual access control layer. We review every policy rather than sampling.
Not the important ones. A scanner cannot know that invoice 4192 belongs to a different tenant, because both responses are valid JSON with a 200 status. Detecting that requires two accounts, an understanding of your permission model, and a diff. That work is manual by nature.
One to two weeks of testing for a typical product, from $4,500, fixed scope. Retesting after you have fixed things is included rather than billed separately. If your product is unusually large we will say so during scoping instead of discovering it halfway through.
A staging environment with representative data, two test accounts on separate tenants, and read access to the repository if you want the code review component. If you cannot give repository access we work black box, which finds most of the authorization issues but less of the secret exposure.
Related
LLM application security
If your product calls a model, the prompt and output paths need their own testing.
SaaS penetration testing
The full engagement, covering multi-tenancy, SSO and the API surface.
How our AI analysis works
What the machine does, what the consultant does, and why nothing unverified ships.
Know exactly what an auditor, and an attacker, would find.
Tell us what you need certified or tested. We will scope it properly, quote a fixed price, and tell you honestly if the timeline you have in mind is realistic.
No sales sequence. A scoping call and a written proposal cost nothing.