To find security vulnerabilities in your code with AI, run the fast deterministic scanners first (secrets, dependencies, static rules), then give a coding agent one vulnerability class at a time and ask it to trace how untrusted input moves through your code. Treat each finding as a claim to verify, fix it with a regression test, and keep the agent away from real secrets the whole time.
This guide is about auditing your own code, defensively. If you want to know how to keep the agent itself from doing damage (prompt injection, destructive commands, malicious MCP servers), read AI coding agent security first; this post assumes those safeguards are in place. Tool names and commands below follow the official documentation as of October 2026.
What an AI audit is good at, and what it isn’t
Static scanners and coding agents find different bugs, which is why you want both.
| Deterministic scanners | Coding agent | |
|---|---|---|
| Strength | Known patterns, known vulnerable package versions, secret formats | Logic: who may call this route, where this value comes from, what a check forgot |
| Weakness | Miss business logic; noisy on safe patterns | False positives, missed issues, different answers on each run |
| Cost | Seconds, repeatable | Tokens, slower, not repeatable |
| Best use | CI gate on every change | Targeted audit of risky areas and new code |
An agent shines on things like an endpoint that checks you are logged in but never checks that the order you are reading is yours. No regex sees that. On the other hand, an agent will not reliably notice that lodash in your lockfile is three years old; npm audit will.
What neither does: test your running system. An agent reads the source in your checkout. Misconfigured cloud storage, a firewall rule or a leaked token in a chat log are out of its view.
Before you start: a safe setup
A security audit is a read-heavy task, so set it up that way.
- Work on a branch or a git worktree. The audit and the fixes should be easy to review and easy to throw away.
- Start read-only. In Claude Code, use plan mode (Shift+Tab or
/plan) for the audit itself; in Codex,/permissions→ Read Only or--sandbox read-only. Switch to edits only when you fix. - Keep secrets out of reach. Don’t paste keys, tokens or
.envcontents into a prompt, ever. If the agent needs to know a variable exists, the name is enough. A.env.examplewith fake values is what it should read. - Use development credentials. If the agent runs your tests, those tests should talk to a local or staging database with throwaway keys, never production.
Step 1: run the deterministic scanners
These take seconds and give the agent concrete leads. All of them are free to run locally.
# Secrets in git history and in the working tree (gitleaks v8.19+)
gitleaks git -v .
gitleaks dir -v .
# Known-vulnerable dependencies
npm audit --audit-level=high
pip-audit -r requirements.txt
# Static analysis rules for your languages
semgrep scan --config auto
A few notes from the official docs:
- gitleaks split its old
detectcommand intogit(scan commits) anddir(scan files) in v8.19.0; the old commands still work but are hidden from help. - npm audit
--audit-levelonly changes the exit code, not the report.npm audit fixruns a real install;npm audit fix --forcemay install SemVer-major updates, so don’t run it blindly. - pip-audit with no arguments audits the current Python environment;
-raudits a requirements file. - Semgrep
--config autofetches rules tailored to your project from the Semgrep Registry, so it needs network access. Add--errorto exit with 1 when there are findings, which is what you want in CI.
Save the output to a file. In step 3 you’ll hand it to the agent instead of retyping it.
Step 2: audit one vulnerability class at a time
“Find security bugs in this repo” gets you a shallow list. A focused prompt per class gets you real analysis. The OWASP Top 10:2025 is a good map; these are the classes worth auditing first in a typical web app:
| Class | What to ask the agent to trace | Typical sign |
|---|---|---|
| Broken access control | Every route that reads or changes a record: does it check the record belongs to the caller? | findById(req.params.id) with no owner or tenant filter |
| Injection (SQL, shell, XSS) | Every place user input reaches a query, a shell command or HTML | String concatenation into SQL, exec with input, raw HTML rendering |
| Authentication failures | Login, session, password reset and token handling | Reset tokens that never expire, missing rate limit on login |
| SSRF | Every server-side request whose URL comes from the user | fetch(req.body.url) with no allow list |
| Secrets in the repo | Hardcoded keys, tokens in config, credentials in tests | Strings that look like keys; gitleaks will be more reliable |
| Supply chain | Dependencies, install scripts, CI workflow permissions | Unpinned actions, broad permissions: in workflows |
A prompt that works, in plan mode:
Audit this repository for broken access control. Do not edit files.
1. List every HTTP route that reads, updates or deletes a record by ID.
2. For each one, find where the caller's identity comes from and whether
the query is limited to records the caller owns (user or tenant).
3. Report only routes where you can show the missing check.
For each finding give: file:line, the route, where the ID comes from,
why the existing middleware does not cover it, and a severity
(high/medium/low). If you are unsure, say so instead of guessing.
Repeat the same shape for injection, auth and SSRF. One class per conversation keeps the context window focused, and you can run them in parallel in separate sessions.
For injection, it helps to tell the agent what “safe” looks like in your stack, so it doesn’t flag every query:
We use parameterized queries through db.query(sql, params) everywhere.
Find any place that builds SQL, shell commands or HTML from request
data without going through db.query params, execFile with an argument
array, or the template engine's escaping. Ignore code under tests/.
Step 3: use the built-in security reviews
Several agents now ship a security review you don’t have to write yourself.
Claude Code /security-review. The official command reference describes it as analyzing the changes on your current branch for security vulnerabilities: it reviews the diff between your branch and the default branch of origin and looks for risks like injection, auth issues and data exposure. It needs an origin remote. It covers your branch’s changes only, so run it before you open a pull request. Anthropic’s claude-code-security-review repository explains how to customize it: copy its security-review.md into .claude/commands/ and add your own rules, for example which patterns are safe in your codebase.
Claude Code security-guidance plugin. Installed with /plugin install security-guidance@claude-plugins-official, it reviews code while Claude writes it: a pattern check on each edit (things like eval(, child_process.exec, .innerHTML =), a background security review of each turn’s diff, and a deeper review when Claude commits. You can add project rules in .claude/claude-security-guidance.md. The docs are clear that it doesn’t block writes and can miss issues; it is one layer.
Codex /review. Codex’s review command (and codex review --uncommitted or --base main from the shell) is a general code review. Ask it explicitly to focus on security if that’s what you want.
Gemini CLI security extension. Installed with gemini extensions install https://github.com/gemini-cli-extensions/security, it adds /security:analyze for the changes on your branch and /security:scan-deps, which checks dependencies with OSV-Scanner.
For code you already have, rather than a branch’s changes, use the class-by-class prompts from step 2. You can also feed the scanner output in:
Here is the output of semgrep and npm audit in scan-results.txt.
For each finding, check whether it is reachable from user input in
this codebase. Mark each as confirmed, likely false positive or
needs a human, with a one-line reason. Do not edit files.
Step 4: separate real findings from false positives
AI security reviews are confident in tone whether they are right or not. Ask for evidence, not verdicts:
- Source: where does the untrusted value enter? A request body, a query string, a file upload, a webhook?
- Path: which functions does it pass through to reach the risky call? Ask for the chain with file:line for each hop.
- Guard: what validation, escaping or authorization sits on that path, and why doesn’t it cover this case?
- Reachability: is the route exposed? A finding in a dead admin script is not the same as one in a public API.
Then get a second opinion. A fresh session, or better a different model, should check high-severity findings before anyone writes a fix. Give it the finding and the code, not the first agent’s reasoning, so it doesn’t simply agree. AI code review describes how to weigh findings and keep that loop bounded.
Common false positives to expect: SQL built from constants only, HTML that passes through a framework’s automatic escaping, test fixtures with fake keys, and server-side requests to a fixed internal URL. Write the safe patterns into CLAUDE.md or AGENTS.md so later audits stop flagging them.
Step 5: fix with a regression test
A security fix without a test tends to come back. Ask for the test first:
Fix the missing ownership check in GET /api/orders/:id (finding #2).
1. First write a test where user A requests user B's order and expect
a 404. Run it and show me that it fails.
2. Then add the check in the smallest way that fits our existing
middleware. Don't change unrelated code.
3. Run the full test suite and show me the diff.
The failing-then-passing test proves the bug existed and that the fix covers it. Keep each fix in its own commit with a clear message, so the history explains what was wrong. For the general habit of test-first fixes, see debugging code with AI.
The same discipline applies to dependency findings: upgrade one package at a time, run the tests, read the changelog for breaking changes. A mass npm audit fix --force is how an audit turns into an outage.
If you find a real secret in the repo
This is the one case where the order of steps matters:
- Rotate the secret first. Revoke the key at the provider and issue a new one. Once a secret has been pushed, assume it is compromised; deleting the file doesn’t un-leak it.
- Move the new secret into your secret manager or environment, not back into the code.
- Then clean up: remove it from the code, and decide with your team whether to rewrite history. Rewriting history doesn’t help if the key wasn’t rotated.
- Prevent the next one with a pre-commit hook. gitleaks documents this
.pre-commit-config.yamlentry:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
Let the agent help with steps 2 to 4, but never by pasting the secret into the conversation. “The Stripe key in src/config.ts must move to an environment variable called STRIPE_SECRET_KEY” is all it needs.
Make it continuous
A one-time audit decays as soon as new code lands. Put the deterministic checks in CI and keep the AI review for pull requests:
# .github/workflows/security.yml (excerpt)
- run: npm audit --audit-level=high
- run: semgrep scan --config auto --error
For an AI pass on every pull request, Anthropic’s anthropics/claude-code-security-review GitHub Action comments findings on the PR. Its required input is claude-api-key; optional inputs include comment-pr, exclude-directories and false-positive-filtering-instructions. If you already run Claude in GitHub Actions, Claude Code GitHub Actions covers setup and secrets handling.
Doing it with AgentVera
AgentVera is a desktop app for macOS, Windows and Linux that runs Claude Code, Codex, Gemini CLI and other CLI agents side by side in real terminals. For security audits, that makes a few setups easy:
- Workspace: put a read-only auditor (permission set to Ask) next to a fixer working in a worktree, and a third pane with a different CLI as the second opinion. Each agent keeps its own permission level.
- Code review: turn on review at the end of each turn in the project settings, and a Claude model you choose reviews the fixer’s changes; findings come with severity and a file:line link and can go back to the agent, at most two rounds in a row.
- Review inbox: finished work waits as cards, and risky changes are flagged: auth and security code, dependency files, CI and infra files,
.envand changes with no tests. - Verify (Pro and Team): a separate read-only verifier checks the result against the criteria in your request, such as “a regression test exists and fails without the fix”.
- Agents in Docker: run Claude Code or Codex in a container that only sees the project folder, so files and SSH keys elsewhere on your machine stay out of reach during an audit.
- Checkpoints: each turn is snapshotted, so a fix that goes wrong is one click to undo.
The grid, worktrees, code review and Docker agents are on the free plan. Download AgentVera and run your first audit on a branch.
Common mistakes
- Asking for “all security issues” at once. You get a generic checklist. Audit one class at a time.
- Fixing before verifying. A false positive “fixed” adds code that nobody understands. Get evidence first.
- Pasting secrets to “check if they’re valid”. Never. Rotate instead.
- Running
npm audit fix --forcein an agent loop. Major upgrades break things in ways that look unrelated. - Treating a clean AI review as a guarantee. It means the agent didn’t find anything this time. Keep the scanners in CI.
- Letting the auditor edit. Keep the audit read-only and the fixes in a separate, reviewed step.
Wrapping up
Start with gitleaks, your dependency auditor and Semgrep. Then audit one OWASP class at a time with a read-only agent, asking for file:line evidence and a reachable path. Run /security-review (or your agent’s equivalent) before each pull request, have a second session check the serious findings, and fix each one with a regression test that fails first. If a secret leaks, rotate it before anything else. When you’re ready to put agents to work on the fixes, AI pair programming shows how to keep yourself in the navigator’s seat. If you are new to all this, learn to code with AI is a gentler place to start.