All posts

Field guide

CORS misconfiguration in AI-generated code: the wildcard that opens your API

A blocked cross-origin request is one of the most common walls an assistant hits, and the quickest way through is to allow every origin. Do that with credentials on, and any website your user visits can call your API as them. Here is what CORS is really doing and how to open it just enough.

Last updated

Cross-origin errors are a rite of passage: the frontend on one origin calls the API on another, the browser blocks it, and something needs to change on the server. When an AI tool hits this, the fastest fix that makes the error disappear is to allow every origin, and if the request carries cookies, to also turn on credentials. Both changes clear the wall and the app starts working, which is why they ship. The problem is what that combination actually permits.

What CORS is protecting

By default, a browser stops a page on one origin from reading responses from another. That default protects your users: it is why a random site cannot quietly read your logged-in email in another tab. CORS is how your server makes deliberate exceptions to that rule, telling the browser which specific origins are allowed. It is a permission list, and the danger is writing one that says everyone.

Want to see exactly what Prbl flags? Watch it scan a demo app, no repo or account needed.

See a live scan →

Why the wildcard plus credentials is the trap

An origin of "*" says any website may call your API. credentials: true says the browser should include the user's cookies on those calls. Put together, a malicious page the user visits can make authenticated requests to your API as that user and read what comes back. That is the account, accessed from a stranger's site.

// the risky pattern: every origin, with credentials
app.use(cors({
  origin: "*",
  credentials: true,
}));

Browsers forbid the literal wildcard-plus-credentials pair, which nudges people toward reflecting the request's origin back instead. That looks more specific but is just as open, because it approves whatever origin shows up. The real fix is an allowlist.

The fix: an allowlist of your own domains

Name the origins you actually serve and reject the rest. Compare the incoming origin against your list and only allow it if it matches, so your own frontend works and no one else's does.

const allowed = new Set([
  "https://app.example.com",
  "https://example.com",
]);

app.use(cors({
  origin: (origin, cb) =>
    cb(null, !origin || allowed.has(origin)),
  credentials: true,
}));

Now credentials are safe, because they are only ever sent to origins you control. The full walkthrough is in the fix for a CORS wildcard origin. This is one of a family of insecure defaults assistants reach for to clear an error, all with the same tell: a protection widened to make something work.

Frequently asked questions

What does CORS actually do?

CORS, cross-origin resource sharing, is how your server tells the browser which other origins are allowed to read responses from your API. By default a browser blocks a page on one origin from reading responses from another, which protects your users. CORS is the mechanism you use to make deliberate exceptions, for example letting your own frontend on one domain call your API on another. It is a permission list, and the danger is setting it too wide.

Why is origin '*' with credentials dangerous?

A wildcard origin says any website may call your API, and credentials true says the browser should include the user's cookies on those calls. Together they mean a malicious site the user visits can make authenticated requests to your API as that user and read the responses. That is the whole account, from someone else's page. Browsers actually forbid the literal combination of a wildcard and credentials, which pushes people toward reflecting the origin instead, which is just as dangerous if done blindly.

Is reflecting the request's origin a safe workaround?

Not on its own. Reflecting whatever origin the request carries, and allowing credentials, is effectively the same as a wildcard, because it approves every origin dynamically. It looks more specific but grants the same access. The safe version is to compare the incoming origin against an allowlist of your own domains and only echo it back if it matches, rejecting everything else.

What if my API is public and has no cookies or auth?

Then a permissive read is less severe, because there is no session to abuse and nothing private to read. But be careful: as soon as you add authentication, cookies, or any per-user data, the old wide setting becomes a real exposure, and it is easy to forget it is there. Set the allowlist correctly from the start so it is not a landmine waiting for the day you add login.

Check your API's CORS policy

A wildcard origin is easy to set during development and easy to forget once you add login, at which point it quietly becomes a real exposure. A scan flags a permissive CORS setup with credentials, so you can swap in an allowlist before it matters. Run a free scan and check how open your API is.

Prbl scans the AI-generated parts of your codebase for exactly the kinds of issues above.

CORS Misconfiguration in AI-Generated Code: The Wildcard That Opens Your API: Prbl