Audit an inherited or unfamiliar codebase systematically rather than ad-hoc. Covers scope discipline, day-0 triage, SAST and SCA tool recipes (semgrep, CodeQL, gitleaks, trivy), OWASP Top 10 mapped to grep patterns, auth-surface walkthrough, and writing reports that drive remediation. Invoke when inheriting a codebase, accepting an audit engagement, or reviewing AI-generated code before shipping.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Audit an inherited or unfamiliar codebase systematically rather than ad-hoc. Covers scope discipline, day-0 triage, SAST and SCA tool recipes (semgrep, CodeQL, gitleaks, trivy), OWASP Top 10 mapped to grep patterns, auth-surface walkthrough, and writing reports that drive remediation. Invoke when inheriting a codebase, accepting an audit engagement, or reviewing AI-generated code before shipping.
Codebase Audit — the Inherited-Code Playbook
This is the skill that binds the others together. Most engagement value from a security review comes from the auditor's process, not from any single check. A junior auditor with a great tool produces a noisy report; a senior auditor with grep and a methodology finds the things that matter.
This skill is the methodology. It walks the audit from "I just got SSH access / a repo URL" through to "here's the prioritized report with owners and deadlines." It cross-links into every hardening skill in this repo as the deep-dive material per finding class.
When to invoke
Inheriting a codebase — new job, new client, open-source fork, acquisition target
"Audit my app" engagement — freelancer / consultancy work
Onboarding to a project — you'll touch the code soon and need to know what's safe to assume
AI-generated codebase review — before shipping a Claude/Copilot/Cursor-written app
Periodic re-audit — annual review of a system you already know
Step 0 — Scope before substance
The most common reason audits fail is the auditor trying to look at everything. Scope discipline is the difference between a useful report and a 300-page document nobody reads.
Before starting, write down:
Why this audit exists — diligence? incident? compliance? onboarding?
Who reads the report — engineers? CTO? legal? buyer?
Time budget — 4 hours? 4 days? 4 weeks? This determines depth.
What's in scope — which repos / services / domains / accounts. Get it confirmed in writing.
What's explicitly out of scope — third-party SaaS, customer data, anything the client did not authorize. Get this confirmed too.
Entry points — what URLs / endpoints does this expose? Background workers? CLIs?
Auth story — how do users log in, what session mechanism, are admins separate?
Data — what's stored, where (Postgres, Mongo, S3, files on disk, third parties)?
Secrets — where are they (env, vault, hardcoded — yes, look)?
Deploy — how does code get to production? Who can deploy? CI/CD or manual?
Blast radius — if this whole system were compromised tomorrow, what's the worst that happens? Customer data leak? Money lost? Operations halted? Reputation only?
If you cannot answer one of these from a 30-minute look, that's already a finding.
Step 2 — Inventory the surface
Now enumerate everything that matters.
Repositories and branches
git log --oneline | head -20
git log --all --format='%aN' | sort -u # who has committed
git tag # release cadence
git remote -v # where is it publishedls -la .github/ # workflows, codeowners
Run automated tools early, parse their output later. The tools give you a haystack of candidates; the manual review (Step 4–6) figures out which are real.
Enable in repo Settings → Code security → CodeQL analysis. It's free for public repos and worth its quotas for private. The results land in the Security tab; treat them as candidates, not verdicts.
Issues outside its rule set, recent CVEs not yet ruled
No tool finds broken access control. That is the single most common high-severity finding in real audits, and it requires manual review. Plan time accordingly.
Step 4 — Manual review — Auth surface
Auth surface is where the highest-impact findings cluster. Walk every entry point and answer:
Is auth checked? Server-side, in the handler itself — not just at middleware.
Is authorization checked? Auth says "you are X"; authz says "X may do this." They are not the same check.
Are object IDs resolved against the requester?GET /invoices/:id must verify the invoice belongs to the requesting user.
What happens on auth failure? Generic error (good) or detailed enumeration ("user not found" vs "wrong password" — bad)?
Where are session cookies issued?HttpOnly, Secure, SameSite=Lax?
Password reset and email change flows? Single-use token, expiring, invalidates sessions on success?
# Find every route + look for the auth check
grep -rEn "app\.(get|post|put|delete|patch)\(['\"]" src | head -40
# Then for each, check the handler for: session lookup, role check, owner check
The report exists to drive action. Optimize for "can a different engineer fix this in a sprint?"
Per finding
## [CRITICAL] Broken access control on /api/invoices/:id
### Summary
Any authenticated user can read any invoice by ID. No owner check.
### Evidence
- File: src/api/invoices/[id]/route.ts:12
- Reproduction: as user A, fetch /api/invoices/<an id belonging to user B>
$ curl -H "Cookie: $A_SESSION" https://app.example.com/api/invoices/$B_INVOICE_ID
→ 200 OK with B's data
### Impact
Cross-tenant data exposure. Any logged-in user reads every invoice.
### Remediation
Add owner check in the handler:
const invoice = await db.invoices.findFirst({ where: { id, userId: session.userId }});
if (!invoice) return NextResponse.json({ error: 'not found' }, { status: 404 });
### Severity reasoning
CRITICAL — affects all customer data, exploitable by any logged-in user, no
detection in current monitoring.
### Suggested owner: <name>
### Suggested deadline: <date — typically 7 days for critical>
Severity classes
Use a small scale, consistently applied:
CRITICAL — exploitable without auth, or with low-effort auth, affects all users or money / data integrity
HIGH — exploitable with some pre-condition (specific user role, specific data), affects sensitive data
MEDIUM — exploitable but limited blast radius, or requires significant attacker effort
LOW — defense-in-depth, hygiene improvement, not directly exploitable
INFO — observation, no action required
Aggressive severity inflation kills the report's usefulness. If everything is HIGH, nothing is HIGH.
Group by severity, then by area
A reader skims for CRITICALs and HIGHs first. Make that easy. Within severity, group by area (auth, data handling, infra) so an owner can take their slice.
Include a one-page summary
Decision-makers read the summary, not the report. Cover:
How many findings, by severity
The top 3 to fix this week
The systemic patterns (e.g. "auth checks are inconsistent across handlers — not a finding-per-handler problem")
Suggested timeline / sprint allocation
Step 10 — Anti-patterns junior auditors fall into
Boil the ocean — trying to audit everything in scope to the same depth. Time-box per area; go deep where impact is highest.
Tool output is the report — pasting npm audit output without synthesis or prioritization. Tool output is candidates, not findings.
No reproduction steps — "I think this might be vulnerable" is not a finding. Either prove it or downgrade to INFO.
Severity inflation — calling everything CRITICAL because the auditor wants attention. Kills credibility.
Wrong audience — writing for engineers when the buyer is the CTO, or vice versa. Match the language.
No remediation — listing problems without "do this to fix" leaves the recipient stuck.
Adversarial tone — "the developers clearly did not understand security" is unproductive. Frame as observations + fixes.
Missing the systemic — listing 30 instances of the same pattern, instead of "this pattern occurs throughout the codebase; fix here is to introduce a shared helper / linter rule."
Auditing without authorization — never audit a system you do not have written permission for, even if you think you're being helpful.
Step 11 — After delivery
Walk through the report with the team if possible. A 30-minute call beats a 50-page document for getting buy-in.
Offer to retest specific findings after fixes — closes the loop.
Document what's been fixed in the report itself, or in a follow-up appendix.
Note what's deferred with the reasoning. "Accepted risk: X, until Y date, owner Z" is fine; "ignored" is not.
When you find a critical mid-audit
Stop. Notify the client / stakeholder same-day if the finding affects customer data, money, or trust. Holding a CRITICAL for a final report on Friday when you found it on Monday is itself a finding.