How to security-review the code your AI coding agent writes
By VCA Newsroom
AI coding agents are fast, and that speed cuts both ways. They'll happily generate a working feature in minutes — and just as happily wire up a SQL query with string concatenation, log a secret, or skip an authorization check. The code runs, the tests pass, and the vulnerability ships. Security review isn't optional when an agent is doing the typing; it's the part you still own.
Here's a practical, tool-agnostic workflow for reviewing AI-generated code before it reaches production.
Know what to look for
Most real-world vulnerabilities cluster into a handful of patterns. When you (or a tool) review agent output, scan for these first:
- Injection — SQL, shell, or template input built by concatenating strings instead of using parameterized queries.
- Cross-site scripting (XSS) — user input rendered into HTML without escaping.
- Broken auth/authorization — endpoints that check who you are but not whether you're allowed; missing ownership checks on records.
- Insecure data handling — secrets in code, credentials in logs, sensitive data in error messages.
- Vulnerable dependencies — a package the agent added that has known CVEs.
These map closely to the OWASP Top 10, and they're exactly the categories Anthropic says its automated security review looks for. Keeping this short list in your head makes manual review far more effective.
Use the agent to review itself — in a fresh context
The same model that wrote the code can review it, but not in the same breath. An agent that just spent 20 turns making a feature work is primed to defend it. Start a new session and give it one job: find the flaws.
A prompt that works well:
Review the diff on this branch for security vulnerabilities only.
For each finding, give me: the file and line, the vulnerability class
(e.g. SQL injection), a concrete exploit scenario, and the fix.
If you find nothing, say so — do not invent issues.
The "concrete exploit scenario" clause is the important part. It forces the model to prove the problem is real rather than listing vague "consider validating input" boilerplate.
Reach for the built-in tools
If you use Claude Code, there's a dedicated /security-review command that analyzes your changes for common vulnerability patterns and, when you approve, applies the fixes directly. It's available on the paid Pro and Max plans and on pay-as-you-go API accounts.
For continuous coverage, Anthropic also ships an open-source GitHub Action that runs on every pull request and posts inline comments on the exact lines it's worried about. Dropping it into a repo looks roughly like this:
# .github/workflows/security.yml
name: Security Review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-security-review@main
with:
claude-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
Now every PR — whether a human or an agent opened it — gets a security pass before anyone hits merge. Cursor, Copilot, and other assistants offer comparable review integrations; the principle is the same regardless of tool.
Don't skip the deterministic checks
AI review is a strong addition, not a replacement for the boring tooling that never gets tired:
- Dependency scanning —
npm audit,pip-audit, or Dependabot catches known-vulnerable packages an LLM won't recognize by name. - Secret scanning — tools like
gitleaksor GitHub's push protection stop a leaked key from ever landing in history. - Static analysis (SAST) — linters and tools like Semgrep enforce rules consistently across the whole codebase.
Use the AI to reason about logic and context — "can an authenticated user read another user's records here?" — and use deterministic scanners for the mechanical, pattern-matchable stuff. They cover each other's blind spots.
A concrete example
Suppose your agent generates this endpoint:
@app.get("/invoices/{invoice_id}")
def get_invoice(invoice_id: str, user=Depends(current_user)):
return db.query(f"SELECT * FROM invoices WHERE id = '{invoice_id}'")
A security pass should flag two issues: the query is a SQL-injection waiting to happen (interpolated invoice_id), and there's no check that the invoice actually belongs to user — any logged-in account can read any invoice by guessing IDs. The fix is a parameterized query plus an ownership filter:
@app.get("/invoices/{invoice_id}")
def get_invoice(invoice_id: str, user=Depends(current_user)):
return db.query(
"SELECT * FROM invoices WHERE id = %s AND owner_id = %s",
[invoice_id, user.id],
)
The injection is the kind of thing a scanner catches. The missing authorization check is the kind of thing that needs someone — or an AI reviewer with the right prompt — to understand what the code is for. That's the split to keep in mind.
The takeaway
Treat every diff your agent produces as untrusted until it's been reviewed — the same standard you'd apply to a new teammate's first pull request. Combine a fresh-context AI review, an automated PR check, and deterministic scanners, and you get most of the speed of AI-assisted development without inheriting its worst habits.
SOURCES
Auto-generated by Vibe Coding Academy on July 6, 2026, grounded in the real sources linked above. We review for accuracy, but please verify time-sensitive details against the primary sources.
Build Blueprint · Creator
Have an idea? Get the spec your AI agent can build from.
Describe any product and get a complete build blueprint — stack, data model, screens, APIs, and a ready-to-paste prompt for Claude Code or Cursor. Export to PDF.
Open the Blueprint ▸