2.3 Context Blindness and Unprotected Routes

One of the most surprising ways AI coding agents introduce security vulnerabilities is not by writing bad code — it is by writing perfectly functional code that is missing a critical connection to the rest of your application.

Modern AI coding agents like Claude Code, Cursor, and Copilot in agent mode can explore your full codebase — reading files, grepping for symbols, and tracing imports across modules. But having the ability to look is not the same as reliably looking in every case. These tools still default to the most statistically common pattern from their training data, may not proactively search for your authentication middleware unless prompted, and often prioritize functional correctness over security verification. Simpler completion modes (inline autocomplete, single-file chat) still operate with no view beyond the current file. This leads to a specific and dangerous failure mode: the AI writes new API endpoints without checking that your application requires authentication, producing routes that are syntactically correct, pass unit tests, and are completely open to anyone on the internet.

This section explains how this happens, why it is so hard to spot, and how to prevent it.

How Context Blindness Happens#

Imagine your application already has a solid authentication middleware — a function that runs before every route and rejects requests from unauthenticated callers. That function lives in auth.middleware.ts. Now you ask the AI to add a route that updates a user's profile. Even though an agentic tool could discover auth.middleware.ts by searching the project, it may not think to look — especially if your prompt doesn't mention authentication. The result: a working route that handles the update logic correctly but leaves out authentication entirely.

// Your existing middleware — in auth.middleware.ts
export const requireAuth = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token || !verifyToken(token)) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  req.user = decodeToken(token);
  next();
};

// Your existing protected routes — the AI may not have checked these
app.get('/api/orders', requireAuth, async (req, res) => { ... });
app.get('/api/profile', requireAuth, async (req, res) => { ... });
// ❌ AI-generated route — no authentication applied
app.put('/api/users/:id', async (req, res) => {
  const { name, email } = req.body;
  const user = await User.findByIdAndUpdate(req.params.id, { name, email });
  res.json(user);
});

The generated code works correctly under normal usage — any automated test that calls this endpoint with valid input will pass. The security flaw is invisible at the syntax level. It only becomes apparent when you ask: can an anonymous caller reach this endpoint? Without requireAuth, the answer is yes.

What the AI Focuses On vs. What the Application Actually Requires
Rendering diagram...

The AI did exactly what the prompt asked. The failure is not necessarily that the AI couldn't find your middleware — it's that nothing in the prompt signaled that security context was relevant, so the agent optimized for the functional requirement and moved on. This is why tools like Claude Code support a CLAUDE.md file at the root of your project — instructions written there are loaded into the agent's context at the start of every session, so you can declare rules like "every new endpoint must call requireAuth" once and have them apply automatically to every future prompt without repeating yourself.

What the Data Shows#

Security audits consistently find this pattern across AI-generated codebases. A 2025 Apiiro study across 62,000 repositories found a 10x surge in APIs with missing authorization and input validation over six months, directly correlated with the adoption of AI coding assistants. Privilege escalation vulnerabilities — flaws that let a lower-privileged user access resources they should not be able to reach — increased 322% in AI-assisted codebases over the same period.

In one real-world production audit, security researchers reviewing an entirely AI-generated codebase found — within just four hours — an admin endpoint with zero authentication, a JWT implementation that decoded tokens without ever checking the signature, and three database query modules built with string concatenation instead of parameterized queries. None of these produced errors. All passed the application's existing test suite.

The Veracode 2025 GenAI Code Security Report found that AI-generated code introduces 2.74 times more vulnerabilities than human-written code, and that 45% of AI-generated code contained at least one OWASP Top 10 vulnerability. More than half of these were in the access control category.

Unprotected Routes in Practice#

Unprotected Mutating API Endpoint

High
2.3 · Context BlindnessCWE-306

AI-generated route that modifies user data with no authentication check. The existing requireAuth middleware was defined in a separate file the AI didn't check for.

Authentication vs. Authorization — Two Different Checks#

Context blindness produces two distinct failures that are easy to confuse. Understanding the difference matters because fixing one without the other still leaves your application vulnerable.

Authentication answers: Who is this caller? This is the step that verifies a request comes from a real, logged-in user — typically by checking a session cookie or a JWT (JSON Web Token — a compact, signed token your server issues and the client sends with each request). If authentication is missing, anonymous users can reach the endpoint.

Authorization answers: Is this caller allowed to do this specific thing? Even after confirming who the caller is, you must still check whether they have permission to access or modify the particular resource they are requesting. Being logged in is not enough — you must verify they own or have rights to that specific record.

AI tools frequently implement authentication but skip authorization. A route that calls requireAuth confirms the caller is logged in — but if it does not also check that req.user.id === req.params.id, any logged-in user can modify any other user's data simply by changing the ID in the URL. This class of vulnerability is called Broken Object Level Authorization (BOLA). It is the number one vulnerability category in the OWASP API Security Top 10 (2023), and OWASP describes it as "extremely common in API-based applications" because the server does not typically track the full state of which objects belong to which user, making it easy to exploit by simply changing an ID value.

Broken Object Level Authorization (BOLA)

High
2.3 · Context BlindnessCWE-639

Authenticated route that is still missing authorization. Any logged-in user can access any other user's private orders by changing the userId in the URL.

Completing Partial Code Insecurely#

A second form of context blindness appears when you ask the AI to complete a partial implementation rather than write something from scratch. The AI takes the shortest path to a working result — and that shortest path is often the insecure one.

JWT signature skipping is the most common example. A JWT (JSON Web Token) has three parts: a header, a payload, and a signature. The signature is the critical part — it proves the token was issued by your server and has not been tampered with. If you skip signature verification, an attacker can craft any payload they want (for example, claiming to be an admin), and your application will accept it without question.

When an AI encounters a partial JWT verification stub, it often completes it by decoding the payload directly — which works for valid tokens and passes all tests, but skips the only step that provides any real security:

// ❌ AI-completed JWT verification — signature never checked
function verifyToken(token) {
  // Base64-decode the payload section of the JWT string
  const payload = JSON.parse(atob(token.split('.')[1]));
  return payload;
  // The signature in token.split('.')[2] is never checked.
  // An attacker can construct a token with any payload they want:
  //   header.{"userId": "admin", "role": "admin"}.fake_signature
  // This function will return that payload as if it were valid.
}

The fix is to always verify the signature against your server's secret key before trusting the payload. In Node.js, this means using jwt.verify() — not jwt.decode():

// ✅ Signature verification required before trusting the payload
import jwt from 'jsonwebtoken';

function verifyToken(token) {
  try {
    // verify() checks the signature AND returns the payload.
    // If the signature is invalid or the token is expired, it throws.
    const payload = jwt.verify(token, process.env.JWT_SECRET);
    return payload;
  } catch (err) {
    // Invalid signature, expired token, or malformed token — reject it.
    return null;
  }
}

JWT Signature Not Verified

Critical
2.3 · Context BlindnessCWE-347

AI completed a partial JWT stub by decoding the payload without verifying the signature. An attacker can craft a token with any claims they choose.

Why the CVE-2025-29927 Vulnerability Is a Perfect Example#

In March 2025, a critical vulnerability (CVE-2025-29927, CVSS 9.1) was disclosed in Next.js. The bug allowed attackers to forge a request header named x-middleware-subrequest to bypass middleware entirely — including authentication middleware. Any application that depended on middleware as its sole authorization layer was exposed.

This flaw mirrors exactly what AI coding tools produce when they place all authentication logic in a single middleware layer with no checks at the individual route level. The practical lesson is the same in both cases: middleware alone is not enough. Defense-in-depth means adding authentication checks at the middleware layer and authorization checks inside each route handler. If middleware is bypassed for any reason — a bug, a misconfiguration, or a forged header — the route handler should still verify the caller's identity before processing the request.

How to Prevent Unprotected Routes#

The most effective prevention is to make security requirements explicit before the AI generates code — either through your prompt or through persistent project instructions that the agent loads automatically, e.g., CLAUDE.md.

Give the AI your authentication pattern#

When prompting for a new endpoint, paste a working example of how your existing protected routes are structured, or specify the file path of a working example so the agent can read it itself. Don't just describe the pattern — show it:

Add a route to update a user's profile.

Here is how existing protected routes are structured in this codebase:

app.get('/api/orders', requireAuth, async (req, res) => {
  if (req.user.id !== req.params.userId) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  const orders = await Order.find({ userId: req.user.id });
  res.json(orders);
});

The new route must follow the same authentication and authorization pattern.

When given an explicit pattern, the AI will follow it. Without it — even when the agent could discover your auth middleware by exploring the project — it tends to default to generating a route that works functionally but has no access control, because functional correctness is what the prompt optimized for.

Add security requirements to your AI configuration#

Tools like Cursor, Copilot, and Claude Code support persistent instruction files (.cursorrules, .github/copilot-instructions.md, CLAUDE.md). These are the "persistent project instructions that the agent loads automatically" mentioned earlier — CLAUDE.md for Claude Code, .cursorrules for Cursor, and .github/copilot-instructions.md for Copilot. The OpenSSF published guidance in September 2025 recommending that teams embed security rules in these files. For example:

Every new API endpoint must:
- Apply the requireAuth middleware
- Verify that req.user.id matches the resource owner before returning or modifying data
- Return 401 for unauthenticated requests and 403 for unauthorized ones
Never generate auth-related code without including both authentication and authorization checks.

These instructions are stored with the project and apply to every prompt automatically, so you do not have to repeat them each time.

Audit every AI session before committing#

After any session where the AI generated or modified routes, run a targeted search for unprotected mutating endpoints:

# Find all routes that modify state
grep -rn "app\.\(post\|put\|patch\|delete\)" src/

# In Next.js App Router — find all route handlers
grep -rn "export async function\s\+\(POST\|PUT\|PATCH\|DELETE\)" app/

# Look for route definitions without the auth keyword on the same line
grep -rn "app\.put\|app\.post\|app\.delete\|app\.patch" src/ | grep -v "requireAuth\|authenticate\|authMiddleware"

Review every result manually. Ask: can this endpoint be reached without a valid session token? If yes, it is unprotected.

Prevention checklist for context blindness vulnerabilities

WhenActionWhat to Check
Before promptingPaste auth middleware or a working protected-route example into the promptDoes the prompt explicitly state: 'apply the same auth pattern as these existing routes'?
Before promptingAdd security requirements to .cursorrules, CLAUDE.md, or Copilot instructionsDo the standing instructions require authentication AND authorization on every new endpoint?
After AI generates routesGrep for POST/PUT/PATCH/DELETE route definitionsDoes each one call your auth middleware? Does each one verify resource ownership before modifying data?
After AI generates auth codeSearch for jwt.decode, atob, base64 in auth contextIs there a corresponding signature verification? Is the algorithm explicitly specified?
Before committingWrite or run an unauthenticated request testDoes every mutating endpoint return 401 for requests with no Authorization header?
OngoingUse a framework with opt-out authIs your framework configured so routes are protected by default, and public routes are explicitly annotated?

Source: OpenSSF Security-Focused Guide for AI Code Assistant Instructions (September 2025); Apiiro AI Security Research (2025)

A Pattern That Gets Worse as the Codebase Grows#

Context blindness is not a one-time problem — it compounds. Every time the AI generates a new route without verifying your security requirements, the risk of a missing security connection increases. A project that starts with ten well-protected routes can quietly accumulate unprotected endpoints over months of AI-assisted development if no systematic review process is in place.

The Apiiro research found that the 10x increase in missing authorization vulnerabilities correlated directly with how heavily teams adopted AI tools — not from any single session, but from the cumulative effect of hundreds of AI-generated additions across a codebase. The practical takeaway: treat the audit habit (grep for unprotected routes, test unauthenticated requests) as a standard step after every AI coding session, not as a one-time fix.

Sources: