---
title: "AI Code Security: The Failure Modes That Actually Show Up"
excerpt: "The worry people voice is that an agent writes an exploitable bug. The failure we see more often is quieter: a refactor silently removes a check that was already there, and nothing in the summary mentions it."
metaDescription: "AI code security in practice: the real failure modes of agent-written code, why removed safeguards beat introduced bugs, and what actually catches them."
publishedAt: 2026-07-30
author: Shiplight AI Team
categories:
 - Engineering
 - Best Practices
tags:
 - ai-code-security
 - ai-code-quality
 - coding-agents
 - regression-risk
 - code-review
metaTitle: "AI Code Security: Real Failure Modes"
featuredImage: ./cover.png
featuredImageAlt: "Illustrated Shiplight blog cover: a code file with one protective guard rail quietly missing from a section, highlighted against intact rails elsewhere."
---
Most discussion of security in agent-written code focuses on the agent introducing a vulnerability: an unsanitized input, a bad crypto choice, a secret in a log line. Those happen, and static analysis catches a reasonable share of them.

The failure that costs teams more is the opposite shape. The agent does not add a hole. It removes a protection that was already there, and nothing in the pull request says so.

## The quiet deletion

Here is the pattern, and it is common enough to plan around.

An agent is asked to refactor a module. It rewrites the file cleanly. In the process it drops a null check, a rate limiter, an authorization guard, or a fallback for a degraded dependency. The code is tidier than what it replaced. It compiles, types check, and the tests pass, because nothing ever tested the guard.

Nothing in the summary mentions the removal, because the agent was not asked to preserve it and did not register it as significant. To the model it was one line among two hundred.

This is hard to catch for three reasons that compound:

**It looks like a cleanup.** A diff removing defensive code reads as simplification, which is what you asked for.

**Nobody remembers why the guard existed.** It was probably added after an incident years ago by someone who has left. The comment, if there was one, said what it did rather than what happened without it.

**Tests rarely cover safeguards.** Teams test the happy path and the obvious errors. The rate limiter that only matters under attack, the fallback that only matters when a dependency is down: usually untested, so their removal breaks nothing visible.

We covered the general version of this in [regression risk in AI-generated code](/blog/regression-risk-ai-generated-code). The security-specific version is worse, because the consequence is not a broken feature that someone reports. It is an absent protection that nobody notices until it is needed.

## Why review does not catch it

Human review is a weak filter against a large machine-written diff, for reasons that are about attention rather than diligence.

Reviewers read for what is *there*. Absence is genuinely harder to perceive than presence, and a review surface that shows added and removed lines equally still relies on the reviewer knowing that a removed line mattered.

Volume makes it worse. Agents produce larger diffs than humans, faster. Review quality per line drops as diff size rises, which is well established and not a new observation, and agent throughput pushes teams straight into the part of that curve where review stops working.

## The other failure modes worth naming

**Dependency choices.** Agents suggest packages that fit the task. Whether the package is maintained, popular for good reasons, or typosquatting something popular is not something the model reliably evaluates. Pin and audit as you would for any dependency.

**Secrets in the wrong place.** Agents write configuration and test fixtures. A real credential used to make something work locally can end up committed.

**Over-broad permissions.** An agent asked to make something work will grant the access that makes it work. The narrowest sufficient scope requires knowing why narrow matters, which is context the agent usually lacks.

**Tool permissions.** This one is newer. An agent with MCP servers connected can do whatever those servers expose, at the moment it decides to. A server with production database access is a production database credential held by something that decides on its own when to use it. Grant narrow scopes and prefer servers you can read. See [what is MCP?](/blog/what-is-mcp)

## What actually catches it

**Tests that assert the protection exists.** The durable fix for the quiet deletion is a test that fails when the guard is gone. If a rate limiter matters, something should assert that the eleventh request in a second is rejected. If an authorization check matters, something should assert that the wrong user gets a 403.

This is unglamorous and it is the only thing that reliably works, because it converts an absence into a visible failure.

**Behavioral coverage of the paths that matter.** Unit tests on the module the agent refactored will often pass, because the agent updated them. A test that drives the running application through the flow is harder to accidentally satisfy, since it checks what the system does rather than what the code says.

**Static analysis, for the class it covers.** Worth running, and worth being clear about its limits. Static analysis finds introduced patterns. It does not know that a check which used to be there is gone, because it has no memory of the previous version.

**Human review aimed at the right question.** Not "is this code good," which does not scale to agent volume, but "what did this remove, and why was it there?" That is a narrower question and a reviewer can actually answer it.

## Where this leaves the workflow

The practical arrangement that works: let the agent write the code, and make something outside the agent's context produce the evidence that it is safe.

That means a real run of the software, assertions derived from what the system is supposed to guarantee rather than from the diff, and a test artifact a reviewer can read. An agent's own report that a change is fine is not evidence, for the structural reason set out in [can coding agents test their own code?](/blog/can-coding-agents-test-their-own-code)

[Shiplight](/plugins) covers the behavioral half: the agent verifies the change in a real browser through MCP, and the successful check is written into your repository as a test that runs on every future change. The value for this specific problem is that once a guard's behavior is asserted in a test, a later refactor removing that guard turns into a red build rather than a silence. Web applications; this is not a static analysis or dependency scanning tool, and you want those too.

## Frequently Asked Questions

### Is AI-generated code less secure than human-written code?
Not uniformly, but it fails differently. The characteristic problem is not an introduced vulnerability so much as a silently removed safeguard during a refactor, which review and static analysis both handle poorly.

### Why does static analysis miss removed safeguards?
It analyzes the current code for known bad patterns. An absent check is not a pattern, and the tool has no memory that the check used to be there.

### How do I stop an agent removing protections?
Assert them in tests. A guard that has a test fails visibly when it is removed; a guard that has only a comment does not.

### Are MCP servers a security risk?
They can be. A server runs with the permissions you grant and the model decides when to call it, so grant narrow scopes and vet servers as you would any dependency with production access.

### Does code review catch this?
Poorly, at agent volume. Reviewers perceive what is present more easily than what is absent, and review quality per line falls as diffs get larger.

## Related Reading

- [Regression risk in AI-generated code](/blog/regression-risk-ai-generated-code): the general form of the quiet deletion
- [Can coding agents test their own code?](/blog/can-coding-agents-test-their-own-code): why the agent's sign-off is not evidence
- [AI code review vs verification](/blog/ai-code-review-vs-verification): reading code against running it
- [What is MCP?](/blog/what-is-mcp): the tool-permission surface
