Security-Checking Code From Claude Code, Cursor and Copilot
Coding agents write code that passes its tests and still fails security checks. Five steps to scan every change an agent makes and have it fix what it finds.
DevSecCode Team · · AppSec · 8 min read
Security-check code written by a coding agent
- Install a local scanner. Install the Deva CLI from devseccode.com/download, or the Deva Security extension in VS Code, Cursor or Windsurf. Standard scans run locally and need no account.
- Watch while the agent works. Keep the extension's watch mode on, or run deva scan . --watch in a second terminal on macOS or Linux, so every file the agent saves is re-checked.
- Make the agent read findings before it stops. In Claude Code, add a Stop hook that runs deva scan . --fail-on high and exits 2 when it finds issues, so Claude reads them and keeps working. For other agents, add the same command to the project instructions file.
- Review suppressions and fixes. Require a reason for every suppression, review .dsc.yml like code, and test behavior after a fix instead of trusting a finding's disappearance.
- Keep the same gate in CI. Run deva scan . --fail-on high --format json in CI and fail the build on any non-zero exit: 1 means findings at the threshold, 2 or higher means the scan itself failed.
Why agent code needs its own check
Coding agents are good at making tests pass. That is the problem. In the SusVibes benchmark, 57% of the solutions SWE-Agent with Claude 4 Sonnet wrote for real open-source feature requests passed their functional tests, but only 11.8% also passed the security tests. About 79% of the solutions that worked were insecure. Our research page has the method and the citation.
The weaknesses are not new. They are the same CWEs human developers write, produced faster and reviewed less:
- SQL built from strings instead of parameters (CWE-89).
- Shell commands built from input, often through
shell=True(CWE-78). - Endpoints that check who you are but not what you may do (CWE-862).
- API keys pasted into code to get a demo working (CWE-798).
- Server-side requests to URLs the user controls (CWE-918).
- Deserializing untrusted data with
pickleoryaml.load(CWE-502).
The security playbook for vibe-coded apps covers each class. This post is about the workflow: how to check every change an agent makes, on your own machine, before it reaches a pull request.
Step 1: Put a scanner where the agent writes
Agents write to your working tree, so the check belongs there too, not only in CI after a push. Install the Deva CLI from the download page, or the Deva Security extension in VS Code, or in Cursor and Windsurf from Open VSX. Standard scans run locally, need no account and are free.
deva scan . # summary and the 10 highest-priority findings
deva scan . --details # every finding
Step 2: Watch while the agent works
An agent can touch dozens of files in one turn. Rather than remembering to rescan, let the scanner follow the files.
In the editor, Deva's watch mode is on by default. It re-checks files when they change on disk, including changes made by a terminal or a coding agent, and findings appear in the Problems panel and the Deva sidebar.
From a terminal on macOS or Linux:
deva scan . --watch
Watch runs a full scan first, then reports what is new as files are saved. Run it in a second terminal beside Claude Code or Codex.
Step 3: Make the agent read the findings before it stops
Watching tells you. It does not tell the agent. The stronger pattern is to make the agent's own "done" depend on a clean scan.
Claude Code: a Stop hook
Claude Code runs hooks at points in its loop. A Stop hook runs when Claude finishes responding. If the hook exits with code 2, Claude receives the hook's stderr and keeps working. Deva exits 1 when findings reach the --fail-on threshold, so the hook translates that into 2.
Save this as .claude/hooks/deva-scan.sh and make it executable (chmod +x):
#!/bin/sh
input=$(cat)
# If Claude is already continuing because of this hook, let it stop.
# One repair attempt per turn, never an endless loop.
if printf '%s' "$input" | grep -Eq '"stop_hook_active"[[:space:]]*:[[:space:]]*true'; then
exit 0
fi
cd "${CLAUDE_PROJECT_DIR:-.}" || exit 1
deva scan . --fail-on high >&2
status=$?
case "$status" in
0) exit 0 ;; # nothing at or above high
1) exit 2 ;; # findings: Claude reads them and keeps working
*) echo "deva scan failed with status $status" >&2
exit 1 ;; # scanner error: reported, does not block Claude
esac
Then register it in .claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deva-scan.sh",
"timeout": 300
}
]
}
]
}
}
The hook scans the whole repository, so clear the high and critical findings that are already there before you turn it on; otherwise Claude is sent back to them at the end of every turn. After that, when Claude finishes a change that introduces a high or critical finding, it sees the finding, the file and the line, and fixes it before handing back. If findings remain after that attempt, the hook lets Claude stop and you see them. The timeout gives a full scan up to five minutes; the default for hooks is 60 seconds. Lower the threshold to medium once existing medium findings are dealt with too.
Other agents: an instruction they read
Cursor, Codex and GitHub Copilot's agent read a project instructions file (AGENTS.md, Cursor rules or .github/copilot-instructions.md). Add a line such as:
Before you finish, run `deva scan . --fail-on high`. If it exits 1, fix the
findings it lists. Do not suppress a finding or weaken the check to make it pass.
An instruction is weaker than a hook, because the agent can skip it. Keep watch running so you see what it missed.
Step 4: Don't let the agent fix a finding by hiding it
Agents under pressure to finish will reach for the shortest path, and the shortest path past a scanner is to silence it. Two rules help:
- Suppressions need a reason. In the extension, Suppress records the reason in the workspace's
.dsc.yml, so a suppression shows up in review as a diff with an explanation. Review that file like code. - A finding that disappears is not proof of a fix. Read the change. Then test the behavior: the query still returns the right rows, the command still runs with a validated argument list.
With Deva Pro, the extension can also validate a finding with AI, as likely real or a probable false positive, and suggest a fix as a diff you review before it touches your files, using a local model or your own API key.
Step 5: Keep the same gate in CI
The hook and watch catch problems on your machine. CI catches what reaches the repository from anywhere else. Use the same command and threshold, so a change that passed locally passes in CI:
scan_status=0
deva scan . --fail-on high --format json > scan.json 2> scan-progress.log || scan_status=$?
exit "$scan_status"
Standard scans need no login in CI. The get-started guide lists every exit code.
What this does not catch
Pattern and dataflow rules find code that looks like a known weakness. They cannot tell whether your authorization model is right, whether a business rule can be abused, or whether a secret already leaked before you started scanning. Keep code review, threat modelling for anything that handles money or personal data, and penetration testing. The point of scanning agent output is to make sure review time goes to those questions, not to string-built SQL.
Frequently asked questions
- Can Claude Code run a security scanner automatically?
- Yes. Add a Stop hook in .claude/settings.json that runs a scanner when Claude finishes responding. If the hook exits with code 2, Claude receives the hook's stderr and keeps working, so it can fix the findings before handing back. Check the stop_hook_active input field so the hook does not block twice in a row.
- How do I scan code that Cursor writes?
- Install the Deva Security extension in Cursor from Open VSX. Its watch mode re-checks files when they change on disk, including files the agent saves, and shows findings in the Problems panel. You can also add an instruction to your Cursor rules or AGENTS.md telling the agent to run deva scan . --fail-on high before it finishes.
- Is AI-generated code more vulnerable than code written by people?
- It contains the same kinds of weaknesses, such as SQL injection, command injection, missing authorization and hardcoded secrets, but it arrives faster and is often reviewed less. In the SusVibes benchmark, about 79% of the functionally correct solutions from SWE-Agent with Claude 4 Sonnet failed security tests.
- Does scanning agent code send my code to the cloud?
- Not with Deva's standard scans, which run on your machine and need no account. Deva's optional AI features use either a local model, where nothing leaves the machine, or your own provider key, where the prompt and code context for that request go to that provider.
- Is this free?
- Standard Deva scans, watch mode, dependency analysis and JSON output are free and need no account. Pro, at $9.99 per user per month with a 14-day trial, adds AI validation and fix suggestions, compliance mapping, SARIF export and signed evidence.
Sources
Related glossary terms
More articles
OWASP Top 10:2025 Is Live. SSRF Is Gone, Supply Chain Is #3.
OWASP published the 2025 revision of the Top 10. Three structural changes deserve real attention from anyone writing or auditing application code.
Shift-Left Pentesting: Why Offensive Security Belongs in Your IDE
Traditional penetration testing happens after deployment. A new generation of tools moves attack-surface analysis into the IDE, where the cost of a fix is measured in developer-minutes rather than incident reports.