# Security audit

> A method for reviewing a codebase for the holes that actually matter — access control, tenant isolation, secret handling, and code that runs with privilege.

Source: https://gaitro.com/skills/security-audit

A good review isn't "read every line." It answers a few questions well, in order.

## 1. Map the surface

List every way in: HTTP routes and API endpoints, server actions and form handlers, background jobs, webhooks, the git host, anything that runs user-supplied commands. You can't reason about what you haven't enumerated.

## 2. For each entry point, check two things

- **Does it authenticate?** Who is the caller, and is an unauthenticated request rejected where it should be?
- **Does it authorise the specific object?** The caller may be signed in, but are they allowed to touch *this* record? Confirm the id in the request is scoped to the caller's account/project — not just "any logged-in user." Missing this is the most common real bug (IDOR).

Make a table: endpoint → who may call it → what it actually checks. Mismatches jump out.

## 3. Follow the data to where code runs with privilege

Trace user-supplied input to anything that executes: a shell command, a database query, a template, a git operation, an AI prompt. The sharp question is *whose code runs with which secrets*. If untrusted code (or a check command a low-trust user can add) runs with production secrets, that's a leak path, even if no single line looks wrong.

## 4. Treat shared/derived content as instructions

Anything authored by one tenant that another tenant's agent will read — shared knowledge, a shared skill, a summary shown to an agent — can carry injected instructions ("ignore your rules / exfiltrate secrets / run this"). Scan it before it crosses the boundary; render it as text, never as live HTML.

## 5. Confirm the human gate

For anything that changes production or money, verify a person decides, the decision is logged, and high-risk actions need the review they claim to.

## What to write down

For each finding: severity, *who* can exploit it (anonymous / any user / a low role / an insider), the file:line for every step of the path, a concrete attack, and whether you confirmed it by reading the whole path or only suspect it. "Confirmed" means you read every step — not that you ran the exploit.

## Trap to avoid

Don't conclude a control is missing from one function. Trace the whole path to where the dangerous thing happens — the check often lives at the point of execution, not at the input.
