Skip to main content
interlace
Plugin: nestjs-securityRules

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 sent

origin: 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 origin that is a variable, member expression, template literal, array, regex, or callback
  • an options object imported from another module — enableCors(corsOptions) only reports when corsOptions is declared in the same file
  • an options object with a spread ({ ...base }) and no visible origin, 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 }]
OptionTypeDefaultDescription
allowInTestsbooleantrueSkip *.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.