Hidden Assumptions in cors
9 assumptions this code never checks · 2 critical · spanning Domain, Contract, Ordering, Scale, Environment, Temporal
Every codebase relies on things it never checks. Most of them are routine. CodeSea looked at expressjs/cors and picked out the few most likely to cause trouble — explained plainly, with what to do about each. The full list is just below.
Most of what this code assumes is routine. These 2 are the ones most likely to cause trouble here — in plain terms, with what to do about each. The rest are minor; they're under "Show everything".
If you turn on the 'include cookies / login credentials' option while also leaving the 'who can access this' setting at its default open-to-everyone value, browsers will refuse every request that tries to send a cookie or login token — silently. The server looks like it's working fine; the browser quietly throws away the response. This is one of the most common CORS traps and nothing in the library warns you.
What to do: If you need to allow cookies or auth headers in cross-origin requests, set the allowed-origins option to a specific domain or list of domains rather than leaving it at the default open-to-everyone setting.
If you configure this library to decide CORS rules on-the-fly by calling your own code (for example, looking up a database to see if an origin is approved), and that code either crashes or never finishes, the incoming browser request will either crash the server or hang forever — with no timeout and no error message to the caller.
What to do: Wrap your dynamic-origin lookup in error handling and always make sure it calls back within a reasonable time limit, even if the lookup fails.
Show everything (7 more)
If you accidentally pass the wrong type of value for the 'who is allowed' setting — for example a number or an object — the library won't complain; it will either quietly allow everyone in or quietly block everyone, depending on whether the value is 'truthy'. You won't get an error message to tell you something is wrong.
What to do: Double-check that your origin setting is a string, a list of strings, or a regular expression pattern — anything else will silently behave in unexpected ways.
lib/index.js:configureOrigin
The part of the library that builds the list of allowed HTTP methods uses a duck-typing shortcut that can be fooled by unusual inputs. If you pass a non-standard object as the methods setting, the header sent to browsers could contain garbage text, causing all preflight checks to fail silently.
What to do: Always pass the list of allowed methods as either a plain comma-separated string or a simple array of strings.
lib/index.js:configureMethods
The library assumes that setting response headers and then ending the response always works in the right order. In some unusual server environments — for example HTTP/2 adapters or certain testing setups — headers set this way might be silently ignored, so preflight responses arrive empty and browsers block the real request.
What to do: If you are running this library outside of a standard Express or Connect server, verify that your response object follows the same header-setting behavior as a normal Node.js HTTP response.
lib/index.js:corsWithOptions
Whatever origin label a browser (or anyone making a raw HTTP request) sends, the library copies it directly into the response without checking whether it looks like a real web address. On older server versions this could be exploited to inject extra content into the response; on modern servers the platform will reject the bad value with an error.
What to do: Make sure your server is running a recent version of Node.js (version 14 or newer) which automatically rejects malformed header values, and consider using an explicit allowlist rather than reflecting arbitrary origin values.
lib/index.js:configureOrigin
If you accidentally pass a deeply nested list-within-a-list as your allowed origins setting, the library will follow every level of nesting until it runs out of stack space and crashes. Flat lists of allowed origins work fine.
What to do: Keep your allowed origins as a simple flat list of strings or patterns, not a list of lists.
lib/index.js:isOriginAllowed
This library is built specifically for Express-style servers. If you plug it into a different kind of server framework — for example a serverless function host or an alternative Node.js framework — the response object may not have the right shape, and headers will either silently not be set or the whole thing will crash.
What to do: Use this library only with Express or Connect; for other frameworks, look for a CORS package built specifically for that framework.
lib/index.js:corsWithOptions
If you build a list of allowed origins, pass it to this library, and then later add or remove items from that same list in your own code, the changes will immediately affect which origins the running server allows — without restarting or reconfiguring anything. This can be surprising and hard to trace.
What to do: Treat the options you pass to this library as final; if you need to change allowed origins at runtime, use the dynamic callback form instead of mutating the original list.
lib/index.js:cors
See the full structural analysis of cors: the pipeline, data models, and system behavior that put these assumptions in context.
Full analysis of expressjs/cors →Frequently Asked Questions
What does cors assume that could break in production?
The one most likely to cause trouble: If you turn on the 'include cookies / login credentials' option while also leaving the 'who can access this' setting at its default open-to-everyone value, browsers will refuse every request that tries to send a cookie or login token — silently. The server looks like it's working fine; the browser quietly throws away the response. This is one of the most common CORS traps and nothing in the library warns you. What to do: If you need to allow cookies or auth headers in cross-origin requests, set the allowed-origins option to a specific domain or list of domains rather than leaving it at the default open-to-everyone setting.
How many hidden assumptions does cors have?
CodeSea found 9 assumptions cors relies on but never validates, 2 of them critical, spanning Domain, Contract, Ordering, Scale, Environment, Temporal. Most are routine — the analysis flags the two or three most likely to actually bite.
What is a hidden assumption?
Something the code depends on but never checks: a data shape, an ordering, an environment condition, a scale limit, or a contract with another service. It holds until the world it runs in changes, then fails silently.