DEV Community

Cover image for Why Cursor Keeps Writing Wildcard CORS Into Your Express API
Charles Kern
Charles Kern

Posted on

Why Cursor Keeps Writing Wildcard CORS Into Your Express API

TL;DR

  • Cursor consistently generates cors() with no config -- equivalent to origin: '*'
  • When devs "fix" the credentials error, they often land on origin: true which is worse
  • Replace with an explicit origin allowlist -- 10 minutes, prevents any site from making credentialed requests to your API

I've been auditing side projects lately. Mostly Node/Express backends, mostly built with Cursor or Claude Code. One pattern shows up without fail: the CORS setup.

Here's what nearly every AI-generated Express app looks like at the top of the entry file:

const cors = require('cors');
app.use(cors());
Enter fullscreen mode Exit fullscreen mode

The AI is not wrong to add CORS. Without it, browsers block cross-origin requests and the frontend breaks immediately. So the AI adds it. The version that works instantly. The version that also lets any website on the internet make requests to your API.

The vulnerable pattern (CWE-942)

// What Cursor generates by default
app.use(cors()); // equivalent to { origin: '*' }

// Or the explicit version
app.use(cors({ origin: '*', credentials: true }));
Enter fullscreen mode Exit fullscreen mode

That second variant is rejected by browsers -- the Fetch spec does not allow * with credentials. So the developer sees the console error, googles it, and lands here:

// "Fixed" after seeing browser console error
app.use(cors({ origin: true, credentials: true }));
Enter fullscreen mode Exit fullscreen mode

origin: true tells the CORS middleware to echo back whatever Origin header the request sends. Combined with credentials: true, any website can make authenticated requests to your API -- with cookies, Authorization headers, session tokens -- and read the response. That's the actual exploit.

Why the AI keeps doing this

Training data for AI editors is saturated with quick-start tutorials, Stack Overflow answers, and "get it working" repos. These all prioritize solving the immediate problem -- the browser blocking cross-origin requests -- not the security consequences.

cors() with no arguments is the fastest path to a working frontend. The AI has learned that this is what developers want. It's not wrong about that. It's just missing the follow-up step that no tutorial bothers to cover.

The fix

Replace the catch-all with an explicit allowlist:

const allowedOrigins = [
  'https://yourapp.com',
  'https://www.yourapp.com',
  process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null
].filter(Boolean);

app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
}));
Enter fullscreen mode Exit fullscreen mode

The !origin check handles server-to-server requests and curl (which do not send an Origin header). Everything else gets validated against the list.

For multi-tenant apps or dynamic subdomains, use a pattern:

const originPattern = /^https:\/\/([\w-]+\.)?yourapp\.com$/;
origin: (origin, callback) => {
  if (!origin || originPattern.test(origin)) callback(null, true);
  else callback(new Error('Not allowed by CORS'));
}
Enter fullscreen mode Exit fullscreen mode

Checking if you're already exposed

curl -H "Origin: https://evil.com" \
  -H "Access-Control-Request-Method: GET" \
  -X OPTIONS https://yourapi.com/api/users -i | grep -i "access-control"
Enter fullscreen mode Exit fullscreen mode

If you see Access-Control-Allow-Origin: https://evil.com in the response -- and especially if you also see Access-Control-Allow-Credentials: true -- fix this before the next deploy.

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 will catch most of what's in this post. The important thing is catching it early, whatever tool you use.

Top comments (0)