Is Your AI Code Secure? A Security Audit Guide for Cursor & Copilot Projects
AI assistants write plausible code fast — including plausible vulnerabilities. Here's a systematic audit for the specific mistakes Cursor, Copilot, and Claude tend to ship.
Why AI code needs its own audit
AI assistants are trained on public code — including the millions of insecure examples on the internet. They optimize for plausible and working, not secure. The result: code that passes your tests, ships your feature, and quietly leaves a hole.
The failure mode is specific. The model rarely invents bizarre bugs; it confidently reproduces the common ones. So your audit should target the common ones directly.
The audit, by category
1. Hardcoded secrets
The single most frequent AI mistake. Asked to "connect to the database," the model inlines the connection string with credentials.
# Sweep for the usual suspects
grep -rniE "(api[_-]?key|secret|password|token|bearer)\s*[:=]" src/ \
--include=*.ts --include=*.tsx --include=*.js
Anything matching that isn't reading from process.env is a finding. Move it to an env var and rotate the key — assume a committed secret is already compromised.
2. SQL injection from string concatenation
AI loves template-literal SQL because it reads cleanly:
// VULNERABLE — never do this
const user = await db.query(`SELECT * FROM users WHERE email = '${email}'`);
Replace every interpolated query with parameterized queries:
// SAFE — the driver escapes the value
const user = await db.query("SELECT * FROM users WHERE email = $1", [email]);
3. Missing authorization checks
AI reliably implements authentication ("is this a logged-in user?") and reliably forgets authorization ("is this user allowed to touch this record?"). Audit every route that takes an ID:
// VULNERABLE — any logged-in user can read any order
const order = await getOrder(params.id);
// SAFE — scope to the owner
const order = await getOrder(params.id);
if (order.userId !== session.user.id) {
return new Response("Forbidden", { status: 403 });
}
This is IDOR (Insecure Direct Object Reference), OWASP's #1 category (Broken Access Control). It's the most common serious bug in AI-built apps.
4. Unvalidated input
AI assumes the happy path. Add a schema at every trust boundary:
import { z } from "zod";
const Body = z.object({
email: z.string().email(),
amount: z.number().int().positive().max(1_000_000),
});
const parsed = Body.safeParse(await req.json());
if (!parsed.success) return new Response("Invalid input", { status: 400 });
5. XSS from dangerous rendering
Watch for dangerouslySetInnerHTML, innerHTML, and eval on anything user-supplied.
// VULNERABLE
<div dangerouslySetInnerHTML={{ __html: userComment }} />
Render as text, or sanitize with a vetted library (DOMPurify) before injecting HTML.
6. Overly permissive CORS
Asked to "fix the CORS error," the model frequently reaches for the nuclear option:
// VULNERABLE — any origin can call your authenticated API
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Credentials", "true");
* with credentials is actually rejected by browsers — so the model often hardcodes a reflected origin, which is worse. Allowlist specific origins.
7. Leaked stack traces and verbose errors
AI-generated catch blocks love to return error.message to the client, leaking your stack and internals. Log server-side, return a generic message to the user.
A repeatable process
- Inventory trust boundaries — every route handler, every form, every webhook.
- Grep for the patterns above — secrets, string SQL,
dangerouslySetInnerHTML,Allow-Origin: *. - Read every ID-taking route for an ownership check.
- Confirm input validation at each boundary.
- Check dependencies:
npm auditand review what the assistant added topackage.json.
npm audit --omit=dev
Prevention: make the AI write secure code
- Put security rules in your project config (
.cursorrules, system prompts): "always parameterize SQL, always validate input with zod, always check resource ownership." - Ask the model to review its own diff for OWASP Top 10 before you accept it.
- Never accept a security-relevant change you don't understand.
Checklist
- No hardcoded secrets; leaked keys rotated
- All SQL parameterized
- Ownership checks on every ID-taking route
- Input validated at trust boundaries
- No unsanitized HTML injection
- CORS allowlisted, not
* - Generic client errors, detailed server logs
-
npm auditclean
Scan it with Troja
Troja was built for exactly this workflow: it runs 120+ checks against your deployed app, flags the IDOR, the string-concatenated SQL, the exposed key — and hands you a fix prompt you can paste straight back into Cursor. Scan before you ship.
Run the scan this post is about.
Free, no signup. See what's hiding inside your walls in ~30 seconds.
Keep reading
All postsTroja vs. checkvibe: the closest scanner comparison (2026)
checkvibe pioneered security + SEO + AEO scanning with AI fix prompts and a 7-engine matrix. Troja matches it and adds connected deep-stack scans. The honest comparison.
ReadTroja vs. Fixnx: which AI website scanner should you use?
Fixnx runs 100+ AI-powered security, SEO and speed checks with credit-pack pricing. Troja adds AEO, connected deep-stack scans and per-finding AI fixes. Compared.
ReadTroja vs. CyScan.io: recon tool vs. fix-it scanner
CyScan.io is a free attack-surface recon scanner — endpoints, subdomains, fuzzing, screenshots. Troja is a fix-and-ship scanner with AI fixes, AEO and deep-stack scans.
Read