no-permissive-cors
Disallows CORS configured to accept any origin
Disallows CORS configured to accept any origin
Flags CORS configured to accept any origin — a bare enableCors(), origin: '*', or the reflecting origin: true.
Rule Details
Three shapes all accept every origin, and only one of them looks obviously wrong:
app.enableCors(); // no options → origin defaults to '*'
app.enableCors({ origin: '*' }); // explicit wildcard
app.enableCors({ origin: true }); // reflects whatever Origin was sentorigin: true is the one worth understanding. It does not mean "CORS on" — it
echoes the request's own Origin header straight back in
Access-Control-Allow-Origin, so every site passes the check. Unlike '*', it
also stays valid alongside credentials: true, and browsers will send
cookies on those requests. That combination lets any page your user visits read
authenticated responses from your API.
Measured on five real NestJS applications, both CORS call sites present were
permissive: one bare enableCors() and one enableCors({ origin: true }).
❌ Incorrect
app.enableCors();
app.enableCors({ credentials: true }); // no origin key → default '*' applies
app.enableCors({ origin: '*' });
app.enableCors({ origin: true, credentials: true });✅ Correct
app.enableCors({ origin: ['https://app.example.com'] });
app.enableCors({ origin: 'https://app.example.com' });
app.enableCors({ origin: false }); // CORS explicitly off
// Resolved at runtime — the rule does not guess at these
app.enableCors({ origin: configService.get('cors.origin') });
app.enableCors({ origin: (o, cb) => cb(null, allowed.includes(o)) });What this rule deliberately does not report
A security rule that guesses earns a reputation for noise, so anything not statically decidable is left alone:
- an
originthat is a variable, member expression, template literal, array, regex, or callback - an options object imported from another module —
enableCors(corsOptions)only reports whencorsOptionsis declared in the same file - an options object with a spread (
{ ...base }) and no visibleorigin, since the spread may supply it - a variable that is reassigned after its declaration. The value at the call site may not be the value at the declaration, so only a binding written exactly once — its initialiser — is read.
Options
'nestjs-security/no-permissive-cors': ['error', { allowInTests: true }]| Option | Type | Default | Description |
|---|---|---|---|
allowInTests | boolean | true | Skip *.spec.ts / *.test.ts / *.e2e-spec.ts |
When Not To Use It
If the service is a genuinely public, unauthenticated, read-only API where any
origin reading responses is intended. Even then, prefer origin: '*' written
explicitly over a bare enableCors(), so the intent is visible to the next
reader.
Further Reading
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.