Thought Leadership

Do We Still Need Secure Coding Training If We Use Snyk?

August 20, 2026

That’s a fair question, and it deserves a straight answer rather than a pitch.

Snyk, Checkmarx, and Semgrep are good tools. They find real vulnerabilities before code ships. They’ve saved dev teams from real incidents, and if you have one running in CI, you’re ahead of most. 

Nothing below is an argument against that. There’s a specific gap these tools can’t close, and it decides whether the next vulnerability gets written at all.

Why SAST tools miss vulnerabilities

A static analysis tool (SAST) reads code that already exists. It traces where untrusted data enters, follows it through your program, and tells you when it reaches somewhere dangerous without being made safe on the way. 

That works well, and for the patterns it knows, it works better than a human review does, but here’s the catch: The code has to exist first. Someone has to write the line before anything can catch it.

Which brings us to the case that SAST scanners reliably struggle with.

A vulnerability that’s genuinely hard to scan for

Here’s a saved-filters feature: A user picks a column to sort their invoices by, and we remember it for next time.

Writing the value. This code is correct, and the query is parameterized, exactly as it should be:

python

cursor.execute(

    “INSERT INTO saved_filters (user_id, sort_column) VALUES (%s, %s)”,

    (user_id, request.args[“sort”]),

)

Reading it back. Different file, written weeks later, possibly by someone else:

python

cursor.execute(

    “SELECT sort_column FROM saved_filters WHERE user_id = %s”, (user_id,)

)

sort_column = cursor.fetchone()[0]

query = f”SELECT * FROM invoices ORDER BY {sort_column}”

cursor.execute(query)

The second block is injectable, and the attacker’s payload arrives through the first one. Parameterizing the INSERT protected the insert — it never validated what was being stored. 

Whatever string was submitted went in intact, and now it’s being interpolated straight into a query.

This is second-order injection, and it’s exactly the pattern taint analysis loses. The source is request.args in one file; the sink is an f-string in another; and between them, the data takes a roundtrip through our own database. 

Most SAST scanners don’t follow data through persistence. Both halves look fine in isolation, because in isolation both halves are fine.

The failure isn’t the missing placeholder

Here’s the part worth sitting with: You cannot fix the second block with a placeholder. Parameter binding works for values, not identifiers. No database driver will let you bind a column name into an ORDER BY. The correct fix is to validate sort_column against an allowlist at the point it’s used, every time it’s used.

So, the developer who wrote that code wasn’t lazy. They weren’t ignorant of parameterization. (They’d applied it correctly ten lines earlier.) The belief that let the bug through was quieter than that: Data from our own database is trusted.

It isn’t. It’s user input with a delay on it.

No SAST scanner holds an opinion about that belief. It can flag the syntax on a good day, with the right configuration, if the trail survives the roundtrip. What the scanner cannot do is change what the next developer assumes when they read a value out of a table they own.

That’s the honest division of labor. The tool tells you a vulnerability shipped. Training decides whether the developer had the right mental model before they wrote the line. Different jobs. Both matter, and neither replaces the other.

It’s a lack of security training for developers

Once that query is in production, “who owns the fix” becomes a real conversation, but the decision was made much earlier — the moment someone read a value out of the database and chose whether to check it.

That happened on your team, at a keyboard, before anyone downstream saw it. Security can’t move that moment earlier. Only the person writing the code can.

That’s the case for developer training, not another annual module, but something aimed at the exact moment the assumption gets made, using the patterns your own codebase actually contains.

AI-generated code vulnerabilities are becoming more common

The argument is strengthening, because fewer people are typing this code by hand.

CloudBees surveyed around 200 enterprise technology leaders for its 2026 State of Code Abundance report: 81% said they’d seen an increase in production issues tied to AI-generated code.

That’s self-reported and worth reading as leaders’ experience rather than a measurement of anyone’s codebase, but the direction is not ambiguous.

Veracode tested this more directly. Given a coding task with both a secure and an insecure way to complete it, the models they assessed chose the insecure route in close to half of cases. (Veracode’s spring 2026 update found the pattern persisting despite newer models.)

The example above is precisely the kind of thing an AI assistant produces confidently in seconds, because each half is idiomatic and correct-looking on its own. An SAST scanner might catch it afterwards, but developer training decides whether the person accepting the suggestion notices the assumption underneath it.

It’s also worth noting that the context has shifted too: Injection sits at A05 in the OWASP Top 10:2025, down from third in 2021, and it’s catalogued as CWE-89. It has moved down the list, but it’s still here, twenty-plus years later.

That persistence is the point. While detection has improved enormously in that time, the mental model hasn’t.

See it; don’t take our word for it

NINJIO Secure Code has a module on injection that works through query patterns like the one above — including the second-order case, because that’s the one that survives an SAST scanner. It runs 10 to 20 minutes, built to sit inside a sprint rather than compete with it.

Ready to reduce your organization’s human risk?