Skip to content

Methodology

The whole method,
written down.

An audit is five to seven business days of reading. Not scanning, not tooling output, not a checklist walked top to bottom — reading, in a deliberate order, with a specific question in mind at each stage.

That order matters more than any individual finding, so it is written down here rather than kept as a trade secret. If you want to run this yourself instead of hiring me, the method below is the whole thing.

  1. 01

    Day one — find the trust boundaries

    Before reading any business logic I map where untrusted input crosses into trusted territory. Every route handler, every server action, every webhook, every file upload, every third-party callback.

    Each one gets classified as public, authenticated, or privileged. Then I check whether the code actually enforces that classification, rather than whether a UI hides the button.

    This is where most AI-generated codebases fail first, and the failure has a signature: the page is behind a login, and the API route the page calls is not.

  2. 02

    Day one — sweep for secrets

    A full pass over the built client bundle and the git history, looking for anything key-shaped.

    Git history matters more than the working tree. AI coding tools auto-commit aggressively, and a key committed in the first hour and removed in the second is still in the history and still live.

  3. 03

    Day two — read the data layer

    Schema, migrations, and the queries that touch them. I am looking for three things: whether the schema is versioned or was pushed in place, whether queries are parameterised, and whether the access pattern survives a thousand rows.

    The N+1 that is invisible at ten users and fatal at a thousand almost always lives on the busiest page, because that page was written first and iterated on most.

  4. 04

    Day three — model the cost

    For anything calling an LLM, an external API, or unbounded compute: what does one user cost, and what does one hostile user cost?

    The second number is the one that matters. An endpoint with no throttle and a per-request model call is not a performance problem, it is a billing incident waiting for someone to write a for-loop.

  5. 05

    Day four — assume it is already broken

    If something failed in production right now, how would you know? Which route, which user, which deploy?

    Then the recovery question: can you roll back, do you have backups, have they ever been restored. An untested backup is a belief, not a control.

  6. 06

    Day five — rank by consequence

    Every finding gets two scores: how likely it is to happen given your actual traffic, and how much of the system it takes down when it does.

    The ranking is by the product of those two, not by how easy each is to fix. That ordering is frequently uncomfortable — the top item is often the expensive one — but presenting cheap fixes first is how a fix list becomes a to-do list nobody finishes.

  7. 07

    Days six and seven — write it, then defend it

    The report is written to be handed to an engineer who was not on the call. Every finding names the file, explains the failure in terms of consequence rather than category, and proposes a specific fix scoped to a day, a week, or a quarter.

    Then a live sixty-minute call, recorded. You push back, I defend or concede. Findings you consciously accept get marked accepted rather than open — that is a legitimate engineering decision, and a report that does not distinguish the two is not useful six months later.

One finding, start to finish

Abstract method descriptions are easy to agree with and hard to evaluate. Here is one real finding carried the whole way through.

  1. Discovery

    Route inventory turns up app/api/documents/[id]/route.ts. It reads a document by id and returns it. There is a session check at the top of the handler, so it is not on the obvious gap list.

  2. The second look

    The session check confirms that someone is logged in. It never confirms that this someone owns this document. Authentication is present; authorisation is absent.

  3. Consequence

    Any registered user can read any document by incrementing an id. Sign-up is open, so "any registered user" means anyone. This is the whole document store, and enumeration takes about a minute to write.

  4. Rank

    Likelihood: high — it requires no skill and the ids are sequential. Blast radius: total — every document, every customer. Ranked first, above six findings that are individually easier to fix.

  5. Fix

    Scope the query by owner rather than checking ownership after fetching. Then the same audit across every [id] route, because this pattern is never in only one place. Scoped at half a day.

  6. Why it was missed

    Not carelessness. The generated code did exactly what it was asked: put this behind a login. Nobody asked the second question, because knowing to ask it is the job.

On tooling

Tools assist. Static analysis, dependency scanning, and an LLM reading alongside me all shorten the search.

What they do not do is rank. Every tool I know reports a missing null check and a missing authorisation boundary with roughly equal urgency, because neither has any idea which one is your customer table. Deciding what matters at your stage is the part that is not automated, and it is the part you are paying for.

Any AI tooling used on client code runs under terms that exclude training on submitted content. A tool that cannot guarantee that is not used.