Claude Code security review: an audit that only reports real vulnerabilities

· 6 min read

Ask Claude Code for a security review of your app and you'll get a list. Some of it is real. Much of it is generic advice: "consider rate limiting", "validate all input", "use HTTPS". A security audit with Claude Code is only useful when every finding comes with an attack path you can test, and that takes a routine, not a one-line prompt.

This post walks through the routine we use on our own code: scope the review, map where untrusted data enters, check authorization by hand, try to disprove each finding, and report only what survives. Everything here works as a plain prompt. At the end there's a shortcut if you'd rather not type it every time.

Why AI security reviews get noisy

  • Pattern matching without data flow. A string-built SQL query is only an injection if user input reaches it. Flagging every execute( call buries the one that matters.
  • Missing the defense upstream. The ORM parameterizes, the template engine auto-escapes, a validator rejects the payload two files earlier. Without following the data, the review can't see that.
  • Ignoring access control. The most common serious bug in web apps is "user A can read user B's data", and grep can't find it. It needs someone to ask, route by route, who is allowed to call this.
  • No proof. A finding nobody tried to break is a guess.

1. Pick the scope

Say exactly what to review. For a pull request, review the diff but follow data into the unchanged code it touches:

gh pr diff 123
git diff main...feature

For a whole repo, don't ask for "everything". Ask for the riskiest areas first: authentication, payments, file uploads, admin panels, and anything that takes a URL or a file name from the user. Then ask Claude to say what it covered and what it didn't. A review that admits it skipped the reporting module is more useful than one that implies it read it all.

Claude Code also has a built-in /security-review command for the pending changes on your current branch. It's a good quick pass before you push. The workflow below is for when you want a deeper audit of a specific area, with your own rules about what counts as a finding.

2. Map entry points, then trace sinks backwards

Start with where untrusted data comes in: HTTP routes, GraphQL resolvers, webhooks, queue consumers, CLI arguments, file and archive parsers. For each one, note who can call it (anonymous, logged-in user, admin), which auth middleware runs, and what it reads: body, query, headers, cookies, path params, uploads.

Then search for dangerous sinks and trace each one back to a source. A short list goes a long way:

  • Injection: queries built with string concatenation or f-strings, raw(, shell calls such as subprocess(..., shell=True) or child_process.exec.
  • Path traversal: path.join or os.path.join with user input, zip and tar extraction.
  • SSRF: any HTTP client fetching a URL the user supplied: webhook testers, image proxies, importers, PDF renderers.
  • XSS: innerHTML, dangerouslySetInnerHTML, v-html, |safe, mark_safe.
  • Unsafe deserialization: pickle, yaml.load without a safe loader, PHP unserialize.
  • Crypto misuse: MD5 or SHA-1 for passwords, Math.random for tokens, JWTs decoded without verifying the signature, == to compare secrets.

Only a sink with a path from a source is a candidate. Everything else is noise.

3. Check authorization route by route

This is the step most reviews skip and the one that finds the worst bugs. For every route that reads or changes data, ask:

  • Is authentication actually enforced? Middleware that is imported but not applied to the router doesn't count.
  • Is the object scoped to the caller? Invoice.findById(req.params.id) with no owner or tenant check is an IDOR: change the id, read someone else's invoice.
  • Can a normal user set a field they shouldn't, like role or isAdmin, by adding it to the request body (mass assignment)?
  • Do state-changing requests with cookie auth have CSRF protection?
  • Are login, password reset and OTP endpoints rate limited, and are reset tokens single-use with an expiry?

Tell Claude to go through these one route at a time and to name the file and line where each check happens. "Auth is handled by middleware" is not an answer. "requireUser is applied in src/routes/index.ts:12 to every route under /api" is.

4. Try to disprove every finding

Before anything goes into the report, ask Claude to argue against it:

  1. Write the exact request an attacker would send, and the code path it takes, file by file.
  2. Look for a defense on that path: validation, type coercion, a parameterizing ORM, framework escaping, an allowlist.
  3. Where it's cheap, prove it locally: a unit test, a curl against your dev server, or a one-line script calling the function with the payload.

Anything that doesn't survive is dropped. Anything that couldn't be fully confirmed is labeled Plausible, with a note on what would confirm it. This one step is what turns a list of warnings into a list of bugs. Keep the testing on your own code and your own dev environment; there's no need to point anything at production.

5. Report severity first, with the fix

Ask for a one-line summary, then findings from most to least severe, each in the same shape:

3 findings (1 Critical, 1 High, 1 Medium) in auth, uploads and the image proxy.

### 1. [Critical] Any user can read any invoice (IDOR)
src/routes/invoices.ts:27
GET /invoices/:id loads the invoice by id without checking it belongs to
req.user.orgId.
Attack: log in as any customer, request /invoices/1..N, get other
customers' invoices.
Fix: Invoice.findOne({ id, orgId: req.user.orgId }), return 404 when null.
Confirmed: integration test with two users, second user got 200.

A simple severity scale keeps everyone honest. Critical: unauthenticated or cross-tenant data access, or code execution. High: needs a normal account, or exposes sensitive data. Medium: needs unusual conditions or has limited impact. Low: defense in depth.

End with a short "checked and fine" section naming the risky areas that held up. It tells you what was actually covered, and it stops the next review from redoing the same work.

What a code audit won't catch

Reading application code covers one layer. Three others need their own pass:

  • Leaked secrets. A key deleted in a later commit is still in git history. Run a scanner such as gitleaks over the whole history, and rotate anything real before cleaning history.
  • Dependencies. Run your ecosystem's audit (npm audit, pip-audit, cargo audit, govulncheck), then check whether the vulnerable function is actually reachable before upgrading. Avoid npm audit fix --force; it can jump major versions without review.
  • Configuration. Security headers, CORS, cookie flags, Docker users, CI token permissions. A wildcard CORS policy with credentials can undo careful code.

And if you don't know where to start, a short threat model helps: list what an attacker would want, where data crosses trust boundaries, and the top five risks. Then point the code audit at those first.

Make it a skill

The routine above is long to type, and it only works if every step happens every time. That's what Claude Code skills are for: write it once as a SKILL.md and Claude loads it whenever you say "security review this" or "is this endpoint safe?". Our guide on how to write a Claude Code skill shows the format. The same verify-before-reporting idea drives our code review workflow, which pairs well with this one.

Or use the Security Audit Pack below: five ready skills for code audits, secret scanning, dependency triage, config hardening and threat models, built around exactly these steps.

Security Audit Pack for Claude Code

Five skills that audit your own code for exploitable bugs, leaked secrets and vulnerable dependencies, then build a threat model and harden your config. $4.99 one-time, or all 7 kits for $9.99.

Instant download. Free updates. 14-day refund, no questions asked.

Get the free Starter Kit

Subscribe and we'll send you the Starter Kit for Claude Code: five free skills that write your CLAUDE.md, commit messages and release notes and explain code, plus four agents that run each kind of work on the right model. Then a few practical emails on getting more out of Claude Code. Unsubscribe anytime.

More from the blog