DEV Community

Cover image for AI is writing your C#, and AI is now attacking it. Fix this one flaw first
Gamra-hub
Gamra-hub

Posted on

AI is writing your C#, and AI is now attacking it. Fix this one flaw first

Two things happened in 2026, close enough together that they should change how you think about a bug that's been around forever.

First: a lot of the C# shipping today wasn't fully written by a person. Sonar's developer survey put AI-generated or AI-assisted code at roughly 42% of everything being written. Second: in November 2025, Anthropic reported shutting down what it described as the first large-scale cyber-espionage campaign run mostly by an AI agent. A state-sponsored group used Claude Code as an autonomous operator that handled an estimated 80–90% of the actual work, including finding and exploiting vulnerabilities in live targets across around thirty organizations.

Read those two together. AI is writing a lot of the code, and AI can now go looking for the holes in it at machine speed. The slow, expensive part of an attack used to be a human sitting there reading your code, hunting for a way in. That part is getting automated.

So the question isn't really "is my code clean" anymore. It's "where's the most likely hole, and did I close it." For .NET the data points at one answer, and it's nothing exotic.

What the studies actually found

A handful of independent sources from late 2025 into 2026 line up almost suspiciously well:

  • AppSec Santa ran 534 code samples through six major models against the OWASP Top 10. About 1 in 4 came back with a confirmed vulnerability, mostly SSRF and injection (CWE-78/89/94).
  • Veracode tested wider, 100+ models across Java, Python, C#, and JavaScript, and saw 45% of generated code introduce an OWASP Top 10 issue, SQL injection included. The rate didn't drop over repeated cycles.
  • Endor Labs and a few academic reviews keep landing on the same root cause: missing input validation is the most common flaw in AI-generated code. Models leave it out by default unless you tell them not to.

The part that should bother C# developers in particular: a 2026 paper measuring AI-introduced vulnerabilities in the wild found AI's net contribution is unusually high in C#, with a net impact score around +7.6%, near the top of every language they looked at. Their explanation reads like a description of our day job: web and API code, full of repetitive, pattern-based call sites sitting right on security boundaries. Exactly the kind of thing a model will happily autocomplete without stopping to think about the boundary.

One more number worth holding onto: AppSec Santa found that 78% of the confirmed vulnerabilities were flagged by only one of the five scanners they ran. The generated code is clean. It compiles, it passes the linter, it reads fine. The bug lives in the logic, not the syntax, which is exactly what static scanners are worst at and what a reviewer's eye slides past because nothing looks wrong.

The flaw: SQL injection from input nobody checked

This is the one I see constantly. You ask for a quick search endpoint, and you get back something that works on the first run:

// AI-generated, "works", and wide open to SQL injection
public async Task<List<Product>> Search(string name)
{
    var sql = $"SELECT * FROM Products WHERE Name LIKE '%{name}%'";
    return await _db.Products.FromSqlRaw(sql).ToListAsync();
}
Enter fullscreen mode Exit fullscreen mode

It runs. The demo passes. And it's the same bug Veracode and the OWASP data keep flagging, because name goes straight into the query string. The model had no reason to validate or parameterize it; nothing in the prompt said the input was hostile, and its training data is full of this exact shortcut.

Type a normal product name, you get normal results. Type SQL, you get to run SQL. I don't need to hand you a payload to make the point: the string itself is the trust boundary here, and there isn't one.

The fix is boring. That's kind of the point.

Two layers.

First, stop building SQL by gluing strings together. Let the provider parameterize it for you. In EF Core, FromSqlInterpolated does this even though it looks like plain string interpolation. The trick is that every value in the interpolation gets sent as a SQL parameter, not concatenated into the command text:

public async Task<List<Product>> Search(string name)
{
    var pattern = $"%{name}%";
    return await _db.Products
        .FromSqlInterpolated($"SELECT * FROM Products WHERE Name LIKE {pattern}")
        .ToListAsync();
}
Enter fullscreen mode Exit fullscreen mode

This is the line worth actually understanding, because the difference between it and the broken version is invisible at a glance. FromSqlRaw takes a finished string and trusts it. FromSqlInterpolated takes a FormattableString and turns each hole into a parameter, so pattern reaches SQL Server as a value, never as part of the query text. Same-looking $"...", completely different safety story.

If you don't actually need raw SQL, skip it and let LINQ build the query. Then the question never comes up:

public async Task<List<Product>> Search(string name)
    => await _db.Products
        .Where(p => EF.Functions.Like(p.Name, $"%{name}%"))
        .ToListAsync();
Enter fullscreen mode Exit fullscreen mode

Second layer: validate the input at the edge, in the controller, before it ever reaches the data layer:

// in the controller / endpoint, not the repository
if (string.IsNullOrWhiteSpace(name) || name.Length > 100)
    return BadRequest("Invalid search term.");
Enter fullscreen mode Exit fullscreen mode

Not because parameterization isn't enough. For SQL, it is. But "check every input at the boundary" is the habit that closes the whole category, including the next bug the AI hands you that has nothing to do with SQL.

None of this is clever, and that's the point. The defenses against the most common AI-introduced bugs are the same boring fundamentals we already knew. What changed in 2026 is the math around them: more code per day, less human attention per line, and a real chance the thing probing for the gap isn't a person anymore.

So, practically

Treat AI-generated data-access code as unvalidated until you've read it yourself. Assume the input handling is missing, not present. Don't let "it compiles and the scanner's green" stand in for review; the data says a single scanner misses most of these, and the bugs sit in logic a human still has to check. And make the safe version the one you reach for on reflex (parameterized queries, validation at the edge) so the shortcut never makes it into the file to begin with.

If you've been reviewing AI-written C# lately, I'm curious whether you're seeing the same thing. For me it's almost always this, plus missing authorization checks. What keeps turning up in yours?

Top comments (0)