TL;DR
- Cursor almost always outputs
cors()orcors({ origin: '*' })with no origin restriction - Combined with
credentials: true, wildcard CORS lets any site make authenticated requests to your API - Fix: set
originto an explicit domain via env variable, never'*'
Last month I spun up a side project with Cursor. Scaffolded the whole Express backend in about twenty minutes. The AI got the routes right, added input validation, even used parameterized queries for the DB calls.
Then I ran a security scan before pushing to prod. First finding: CORS, flagged as high severity. I hadn't thought about it. Cursor had generated app.use(cors()) with no arguments -- which defaults to allowing every origin on the planet.
I went back through five other projects from the past year. Same pattern every time. Not always cors() bare -- sometimes cors({ origin: '*' }) explicitly. But always a wildcard.
The Vulnerable Pattern (CWE-942)
What Cursor typically generates:
const cors = require('cors');
// Pattern 1 -- no arguments, wildcard by default
app.use(cors());
// Pattern 2 -- explicit wildcard
app.use(cors({ origin: '*' }));
Either way: any domain can make cross-origin requests to your API. That is CWE-942 -- Permissive Cross-domain Policy with Untrusted Domains.
The real danger kicks in when you also set credentials: true:
app.use(cors({ origin: '*', credentials: true }));
Browsers block this combination per the spec. But developers discover the block, search Stack Overflow, and "fix" it by swapping to a specific origin -- except by then the AI has scaffolded dozens of routes with inconsistent CORS handling.
Even without credentials, wildcard CORS on an internal API exposes your endpoint structure, response shapes, and error messages to any site that wants to probe it.
Why AI Keeps Generating This
CORS blocks you immediately and visibly. You see the red error in the browser console, Google it, and the first twenty Stack Overflow answers say app.use(cors()). Tutorial repos -- the training data -- always use the wildcard because it makes the demo work.
The AI has seen thousands of these tutorials. When you say "set up an Express server", it reaches for the pattern that appeared most often in working code. Wildcard CORS is the one that never fails to compile, never throws an error, never blocks development.
Security intent is invisible to the model. The code is syntactically valid. The tests pass. The demo runs. The vulnerability ships.
The Fix
For a single allowed origin:
app.use(cors({
origin: process.env.ALLOWED_ORIGIN || 'https://yourapp.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
For multiple origins (staging plus prod):
const allowedOrigins = (process.env.ALLOWED_ORIGINS || '').split(',');
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
Set ALLOWED_ORIGINS=https://yourapp.com,https://staging.yourapp.com in your env file. No wildcards, no guessing.
For local development without polluting prod values:
const ALLOWED_ORIGINS = process.env.NODE_ENV === 'development'
? ['http://localhost:3000', 'http://localhost:5173']
: (process.env.ALLOWED_ORIGINS || '').split(',');
This way you never write origin: '*' even in dev.
I've been running SafeWeave for this. It hooks into Cursor and Claude Code as an MCP server and flags these patterns before I move on. That said, even a basic pre-commit hook with semgrep and gitleaks will catch most of what's in this post. The important thing is catching it early, whatever tool you use.
Top comments (0)