Security audit

Your AI-built app is probably leaking something. Here is where we look.

AI generators are good at code and blind to consequence. They will write you a working application and leave the database readable by anyone holding the key that ships in your own JavaScript. This is the full scope of what we check, published rather than summarised, so you can run the important parts yourself if you would rather.

2 of the 10 checks below turn up a real finding in almost every AI-built repo I open. Not a sales line — the reason is structural, and it is explained under the list.

What the audit covers

Each item says how it is tested and what it looks like when it is wrong, because "review your authorisation model" tells you nothing about whether you have a problem today.

Almost always

Authorisation on the data layer

How we check: We take the public key out of your own front end and query your API and database directly, signed out and signed in as an ordinary user, table by table.

What it looks like: Row Level Security disabled or written as a policy that permits everything, so any visitor can read the whole table. In Supabase-backed builds this is the single most common serious finding.

Often

Object-level access between accounts

How we check: We create two accounts and try to read, edit, and delete each other’s records by changing the id in the request.

What it looks like: Endpoints that check you are logged in but never check the record belongs to you. Nothing is exposed to the public, and every customer can see every other customer.

Often

Secrets in the client bundle and the repo history

How we check: We read the shipped JavaScript for keys, and walk the git history for credentials that were committed and later removed.

What it looks like: A service key, an admin token, or an email provider password sitting in the browser bundle, or removed from the current files but still live in an old commit and in every fork.

Sometimes

Input handling and injection

How we check: We follow every input from the form to the query, looking for string-built SQL, unescaped rendering, and file uploads that trust their own filename.

What it looks like: Query strings assembled by concatenation, or user text rendered as HTML. AI-written code usually parameterises correctly, so this is rarer here than in hand-written code — but the exceptions cluster in the parts a human edited afterwards.

Almost always

Server-side validation

How we check: We submit requests that your forms would never send: missing fields, wrong types, negative quantities, prices supplied by the client.

What it looks like: Validation that exists only in the browser, so anyone with curl can write whatever they want — including the price of the thing they are buying.

Often

Session and authentication handling

How we check: We check token lifetime, where the session is stored, what happens on sign-out, and whether password reset can be used against another account.

What it looks like: Sessions that never expire, tokens kept where any script on the page can read them, or a reset link that does not invalidate after use.

Often

Payment and webhook integrity

How we check: We check that webhook signatures are verified, that events cannot be replayed, and that what your database believes matches what the payment provider believes.

What it looks like: An unsigned webhook endpoint that anyone can post to in order to mark an order paid, or subscription state that silently drifts from Stripe after the first failed charge.

Often

Dependencies and known advisories

How we check: We audit the lockfile against published advisories and separate the ones that are actually reachable from your code from the noise.

What it looks like: A generated project pinned to whatever resolved on the day it was built, carrying two or three packages with published vulnerabilities and no process for noticing the next one.

Often

Transport, headers, and exposure

How we check: We check TLS, redirects, security headers, CORS, and whether admin routes, source maps, or debug endpoints are reachable from the internet.

What it looks like: An admin panel on a guessable path with no server-side role check, or a CORS policy set to allow every origin because that made local development work.

Often

Personal data handling

How we check: We list what personal data you store, where it goes, which third parties receive it, and whether deletion is actually possible.

What it looks like: Analytics and error tracking capturing email addresses and form contents, no retention limit, and no way to fulfil a deletion request without hand-editing rows.

Why the same things are wrong every time

It is not that the models write insecure code. Mostly they do not — generated queries are parameterised and generated output is escaped more reliably than in code written at speed by a person.

The gap is that security is mostly things that must be added, and nothing in the loop asks for them. You prompted for a booking app; you got a booking app. Nobody prompted for "and make sure one customer cannot read another customer's bookings", because the app looked correct without it. Every check above is in that category: absent rather than wrong, and invisible until someone goes looking.

Which is also why this is fixable in days rather than months. There is rarely anything to rewrite. There is a list of things to add, in the right order.

How your code is handled

Nothing live is touched

We read the code and configuration you send. No probing, scanning, or testing against your running systems unless you ask for it in writing.

NDA before anything moves

Ask and it is signed before you send a line. Read access or a zip archive is enough — reviewing code needs no commit access.

You keep everything

Your repository, your IP, your accounts, your findings. If a pattern is ever written about publicly it is anonymised and you approve it first.

Findings go to you first

If something serious turns up, you hear it directly and immediately, with the fix, before anything else happens.

Find out in two days, for nothing

Send the repo. I read it against this list and write back the three findings that would hurt you first, with the fix for each. If the answer is that you are fine, you get that instead — which is worth knowing too.

Common questions

What is a vibe-coded app security audit?

A read of an AI-generated application specifically for the failures that generators produce: access control written in the interface instead of the database, secrets in the client bundle, unverified payment webhooks, validation that only exists in the browser. It is a code and configuration review, not a penetration test — nothing is attacked, and nothing runs against your live systems without you asking for it.

Are AI-generated apps less secure than hand-written ones?

Not uniformly. The generated code itself is often better than average at the things that used to break — it parameterises queries and escapes output more consistently than a rushed human does. The failures cluster somewhere else: in what was never generated at all. Nobody wrote the authorisation rules, separated the environments, or verified the webhook, because no prompt asked for it and the app worked without it.

How do I check my Supabase Row Level Security myself?

Take the anon key out of your own front end and query each table with curl while signed out, then again signed in as an ordinary user asking for another user’s rows. If data comes back either time, that table is open. Do it for every table — the ones people forget are the join tables and the ones added late.

What does the audit cost?

The first pass is free: send the repo and you get back the three most serious findings in writing, within two business days, with no call required. The complete audit — every item on this page, every route, with a written remediation plan and effort estimates — is part of the €750 review, fixed price, credited back if you continue.

Will you sign an NDA?

Yes, before you send anything. Just ask. Read access or a zip is enough — no commit access is needed to review code, and nothing is published about your build without your approval.

Do you test the live site?

Only with explicit written permission, and only against what you own. By default the audit reads your code and configuration and nothing else. Probing a live system you have not authorised is not something we do, whoever asks.

Something else? kristian.dienes@ladient.sk, or book 30 minutes.