DEV Community

Cover image for How to Fix Wildcard CORS in Cursor-Generated Code (CWE-942)
Charles Kern
Charles Kern

Posted on

How to Fix Wildcard CORS in Cursor-Generated Code (CWE-942)

TL;DR

  • Cursor and Claude Code default to cors() with no arguments in every Express scaffold
  • Wildcard CORS lets any origin read your API responses -- combine it with credentials and you have CWE-942
  • Fix: swap the wildcard for an explicit origin allowlist

I've reviewed maybe a hundred AI-generated Express backends in the last few months. CORS is misconfigured in almost every single one. Not subtly -- the default Cursor scaffold drops app.use(cors()) right below the imports and moves on. No allowlist. No origin validation. Just a wildcard.

For localhost demos this is fine. In production with any authentication layer, it's a problem.

The thing about cors() with no arguments is that it sets Access-Control-Allow-Origin: *. That alone isn't catastrophic -- browsers block credentialed cross-origin requests when the response includes a wildcard origin. So developers hit a CORS error in the browser, paste the error into Cursor, and Cursor "fixes" it by adding credentials: true. Now you have a genuine CWE-942: any origin can make a credentialed request and read your API response.

The Vulnerable Pattern

Cursor generates this at least half the time I scaffold a new route:

const cors = require('cors');
app.use(cors()); // CWE-942: Permissive Cross-Origin Resource Sharing
Enter fullscreen mode Exit fullscreen mode

Or after the inevitable "why is CORS broken" follow-up:

app.use(cors({ origin: '*', credentials: true })); // invalid -- browser rejects wildcard + credentials
Enter fullscreen mode Exit fullscreen mode

Which still breaks. So the next "fix" Cursor often suggests is reflecting the request origin:

app.use(cors({ origin: true })); // reflects any origin as-is -- completely open
Enter fullscreen mode Exit fullscreen mode

That one works in the browser. It also means any site can read your API response after auth. Both the vulnerable pattern and the "fixed" version come from training data -- StackOverflow answers and tutorial code written to make the demo work, not to be secure.

Why This Keeps Happening

The GitHub corpus is full of Express tutorials where cors() with no args is the first line after app.listen. Optimized for "works in 30 seconds on localhost". That's the code AI editors absorbed.

CORS is also confusing enough that developers often don't push back on the AI's suggestion. The browser error message is opaque, the fix looks small, and when it "works" nobody asks what changed. The result is that CWE-942 ends up in codebases with real authentication, real user data, and nobody who remembers why the CORS config looks the way it does.

The Fix

Replace the wildcard with an explicit allowlist:

const allowedOrigins = [
  'https://app.yourdomain.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 passes server-to-server requests and curl where there's no Origin header. Everything else has to be on the list. credentials: true is safe here because origin is validated before that setting applies.

For FastAPI:

# before (CWE-942)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True)

# after
origins = [os.getenv("FRONTEND_URL"), "https://app.yourdomain.com"]
app.add_middleware(
    CORSMiddleware,
    allow_origins=[o for o in origins if o],
    allow_credentials=True
)
Enter fullscreen mode Exit fullscreen mode

Catch it before code review with a semgrep rule:

rules:
  - id: wildcard-cors
    patterns:
      - pattern: cors()
      - pattern: cors({ ..., origin: '*', ... })
    message: "Wildcard CORS (CWE-942). Use an explicit origin allowlist."
    severity: WARNING
    languages: [javascript, typescript]
Enter fullscreen mode Exit fullscreen mode

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)