Cursor Keeps Skipping Rate Limits on Login Routes (CWE-307) A developer discovered that AI coding assistants like Cursor and Claude Code consistently generate login routes without rate limiting, leaving them vulnerable to brute-force attacks (CWE-307). The developer found that models focus on authentication logic but omit cross-cutting concerns like rate limiting, which is rarely included in training data. The developer built SafeWeave, an MCP server that hooks into Cursor and Claude Code to flag routes missing rate limiting before deployment. I asked Cursor to build a login route for a side project last month. Email, password, JWT back on success. It worked first try, passed my manual tests, and I moved on to the next feature. Three weeks later I ran a load test against it out of curiosity and hit the endpoint two thousand times in under a minute. Not one request got throttled. That's when I started pulling apart every AI-generated auth route I could find, mine and other people's open source side projects, and the pattern held everywhere I looked. The models write correct authentication logic and completely skip rate limiting, because rate limiting isn't part of "does this login work," it's part of "does this login survive contact with an attacker." Here's roughly what Cursor and Claude Code hand you when you ask for a login route: // ❌ No rate limiting - CWE-307: Improper Restriction of Excessive Authentication Attempts app.post '/api/login', async req, res = { const { email, password } = req.body; const user = await User.findOne { email } ; if user || await bcrypt.compare password, user.passwordHash { return res.status 401 .json { error: 'Invalid credentials' } ; } const token = jwt.sign { id: user.id }, process.env.JWT SECRET, { expiresIn: '1h' } ; res.json { token } ; } ; Password hashing is fine. JWT signing is fine. But nothing stops a script from hitting this route as fast as the network allows. No lockout, no delay, no cap on attempts per IP or per account. A credential-stuffing list with ten thousand leaked passwords runs against this endpoint in seconds. Rate limiting lives outside the function the model was asked to write. The prompt is "build a login endpoint," and it returns exactly that: a function that authenticates a user. Rate limiting is middleware, a cross-cutting concern that sits above the route, and most training data tutorials, Stack Overflow answers, bootcamp repos shows login routes in isolation, without the infrastructure code wrapped around them. The model has seen thousands of examples of a bare login route and far fewer examples of that same route wired into a rate limiter, because in most tutorials that wiring happens in a separate file the reader never sees. npm install express-rate-limit js // ✅ Rate limited - 5 attempts per 15 minutes per IP const rateLimit = require 'express-rate-limit' ; const loginLimiter = rateLimit { windowMs: 15 60 1000, max: 5, message: { error: 'Too many login attempts, try again later' }, standardHeaders: true, legacyHeaders: false, } ; app.post '/api/login', loginLimiter, async req, res = { const { email, password } = req.body; const user = await User.findOne { email } ; if user || await bcrypt.compare password, user.passwordHash { return res.status 401 .json { error: 'Invalid credentials' } ; } const token = jwt.sign { id: user.id }, process.env.JWT SECRET, { expiresIn: '1h' } ; res.json { token } ; } ; Four lines of middleware config and one import. For production I'd pair this with an account-level lockout after repeated failures, not just an IP-based limit, since IPs are cheap to rotate. But the IP limiter alone stops the overwhelming majority of automated attempts. I've been running SafeWeave https://safeweave.dev to catch this before it ships. It hooks into Cursor and Claude Code as an MCP server and flags routes with no rate limiting as part of its posture checks. That said, even a quick manual pass over every route handling credentials, checking for a rate limiter, is enough to catch most of what's in this post. The important thing is catching it before the route goes live, whatever tool you use.