Skip to content
NEXDGE
Start free

Security risks in Cursor and Copilot code: the patterns that repeat

AI coding tools produce vulnerabilities in predictable shapes: SQL injection, IDOR, hardcoded credentials and wildcard CORS. Each pattern and its fix.

4 min readUpdated 29 July 2026
In short

AI coding assistants produce a consistent set of vulnerabilities (SQL injection through template literals, insecure direct object references, hardcoded credentials, and permissive CORS) because their training data over-represents tutorial code where security hardening is omitted for clarity.

Key takeaways
  • AI security defects are patterned, not random, which makes them findable before an attacker finds them.
  • SQL injection now arrives as a template literal, which reads like idiomatic modern JavaScript and survives review.
  • IDOR is the highest-frequency critical finding: the endpoint checks that you are logged in, never that the record is yours.
  • Wildcard CORS usually enters as a fix for a local development error and is never revisited before launch.
  • The surrounding code being excellent is the mechanism that hides all of these from human reviewers.

The tools are excellent and you should keep using them. But it is worth understanding what they optimise for, because working code and secure code overlap heavily without being the same set, and the gap between them has a specific and repeatable shape.

Why are the vulnerabilities so consistent?

A language model reproduces the distribution it was trained on. That distribution is dominated by tutorial code, library examples written for readability, and Stack Overflow answers written for brevity. In all three genres, the security-relevant lines are the first thing removed. A tutorial that parameterises every query, validates every session, and handles every timeout is a bad tutorial, because the teaching point drowns.

So the model learned an idiom of code where hardening is absent, and it reproduces that idiom faithfully. The result is predictable, and predictable is good: you can check for four specific things instead of auditing everything.

1. SQL injection via template literals

The most common finding in AI-generated database code, and the one most likely to pass a human review, because the modern syntax reads as current and clean rather than as the classic string-concatenation smell people were trained to spot.

Generated, and exploitable
const query = `SELECT * FROM users WHERE username = '${username}'`
const result = await db.query(query)

// Supplied as username:  ' OR '1'='1' --
// Resulting query returns every row in the table.

The models know parameterisation. They produce it when asked explicitly. They do not reliably default to it, and defaults are what ship.

Fixed
const result = await db.query(
  'SELECT * FROM users WHERE username = $1',
  [username],
)

2. Insecure direct object references (IDOR)

The highest-frequency critical finding. The route authenticates correctly and then fetches by an identifier taken straight from the request, so any logged-in account can read any record by changing a number.

Authenticated but not authorised
router.get('/api/invoices/:id', authenticate, async (req, res) => {
  const invoice = await Invoice.findById(req.params.id)
  return res.json(invoice)     // any user, any invoice
})

The fix is to scope the query by the session's user rather than guarding it afterwards, as covered in the pre-deployment checklist. What makes IDOR particularly costly is that exploiting it requires no tooling and leaves ordinary-looking traffic in your logs. It is indistinguishable from normal use until someone notices the volume.

3. Hardcoded credentials

When a context window contains configuration examples, models reproduce the shape, including plausible-looking literal secrets. These get committed, pushed, and picked up by automated scanners within minutes of touching a public repository. Credential-stuffing infrastructure monitors new public commits continuously; the window between push and exploitation is measured in minutes, not days.

Appears more often than it should
const db = new Database({
  host: 'prod-db.internal',
  password: 'sup3rS3cur3P@ss',   // literal secret in source control
})

Every credential belongs in an environment variable, without exception. If one has already been committed, rotate it first and clean the history second. The ordering matters, because the commit is already public and rotation is what actually closes the exposure.

4. Permissive CORS

This one almost always enters the codebase as a local development fix. A CORS error blocks progress, the model resolves it with a wildcard, the error disappears, and nobody returns to it because nothing is visibly broken.

Development fix that ships
app.use(cors({ origin: '*' }))

// Intended
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
  credentials: true,
}))

Why manual review misses all four

Because the file around the defect is genuinely well written. Clean route organisation, consistent error shapes, sensible names, complete comments. Human review is a pattern-matching process, and every pattern says this file is fine, so attention drops precisely where it needs to rise.

That is the structural argument for automated specialist review on AI-generated code specifically: it applies the same scrutiny to every line regardless of how good the neighbourhood looks. If you are choosing a tool for this, the comparison of AI code review tools covers what each category can and cannot detect.

Frequently asked questions

Is code from Cursor and Copilot insecure?

Not uniformly, but it contains a predictable set of vulnerabilities: SQL injection via template literals, insecure direct object references, hardcoded credentials, and permissive CORS. These come from the training distribution rather than from any specific tool.

What is the most common security flaw in AI-generated code?

Insecure direct object reference: the endpoint verifies the user is authenticated but never verifies the requested record belongs to them, so changing an ID in the request returns another user's data.

Why does SQL injection still appear in modern AI-generated code?

Because it arrives as a template literal rather than string concatenation. The syntax reads as idiomatic modern JavaScript, so it does not trigger the visual pattern most reviewers were trained to catch.

What should I do if a credential was committed to Git?

Rotate the credential first, then clean the history. The commit may already have been scanned and cloned, so rotation is what actually closes the exposure; rewriting history afterwards is cleanup rather than remediation.

Can a linter catch these vulnerabilities?

Partially. Linters and static analysers detect some injection patterns and hardcoded secrets, but they cannot determine whether an authorisation check is missing, because that requires understanding which resources should belong to which user.

Sources

  1. Do Users Write More Insecure Code with AI Assistants? (Stanford, 2022)Controlled study finding participants with an AI assistant wrote less secure code while believing it was more secure.
  2. OWASP Top 10The consensus list of the ten most critical web application security risks.
  3. CWE-89: SQL InjectionMITRE's formal definition of the weakness class.

See what a dedicated security specialist finds in code you already shipped.

Run a security review