Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
CORS middleware hides the response, it doesn't block the request
A CORS allowlist only stops the browser from letting JS read a cross-origin response — it never stops the request itself from reaching the server, so state-changing endpoints still execute for a cross-origin form POST. Protect mutating routes with an explicit Origin check or SameSite cookies, not CORS config alone.
Bad example
| 1 | const portfolioCors = cors({ origin: getAllowedCorsOrigins(), credentials: true }); |
| 2 |
|
| 3 | router.post('/portfolios', portfolioCors, createPortfolio); |
| 4 | router.put('/portfolios/:id', portfolioCors, updatePortfolio); |
Explanation (EN)
The cors() middleware only decides which response headers to send. A cross-origin <form method="post"> or a fetch without credentials-reading intent still hits createPortfolio and mutates data — the browser just won't let attacker-controlled JS read the response body. Relying on the allowlist alone leaves mutating routes open to cross-site request forgery.
Objašnjenje (HR)
cors() middleware odlučuje samo koja se response zaglavlja šalju. Cross-origin <form method="post"> ili fetch i dalje stiže do createPortfolio i mijenja podatke — preglednik samo ne dopušta napadačevom JS-u da pročita response. Oslanjanje isključivo na allowlist ostavlja mutating rute otvorene za CSRF.
Good example
| 1 | const allowed = getAllowedCorsOrigins(); |
| 2 |
|
| 3 | const requireAllowedOrigin: RequestHandler = (req, res, next) => { |
| 4 | if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next(); |
| 5 | const origin = req.get('origin'); |
| 6 | if (!origin) return next(); |
| 7 | const ok = allowed.some((a) => (typeof a === 'string' ? a === origin : a.test(origin))); |
| 8 | return ok ? next() : res.sendStatus(403); |
| 9 | }; |
| 10 |
|
| 11 | router.post('/portfolios', portfolioCors, requireAllowedOrigin, createPortfolio); |
Explanation (EN)
Explicitly reject mutating requests whose Origin header isn't in the allowlist (browsers send Origin on cross-origin POSTs, including form submissions, so no client change is needed), or assert SameSite=Lax/Strict on the session cookie so the browser refuses to attach it cross-site in the first place.
Objašnjenje (HR)
Eksplicitno odbaci mutating zahtjeve čiji Origin header nije na allowlisti (preglednici šalju Origin i na cross-origin POST, uključujući form submit, pa nije potrebna promjena na klijentu), ili postavi SameSite=Lax/Strict na session cookie kako ga preglednik uopće ne bi slao cross-site.
Notes (EN)
CORS is a browser-enforced response-reading restriction, not a server-side access-control mechanism. Never treat an origin allowlist as sufficient protection for state-changing endpoints.
Bilješke (HR)
CORS je ograničenje čitanja response-a koje provodi preglednik, a ne server-side kontrola pristupa. Origin allowlist nikad ne tretiraj kao dovoljnu zaštitu za mutating endpointe.