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).
Validate and coerce all request params, query and headers — not just the body
Route params, query strings and headers are untrusted and arrive untyped; parse and constrain them through a schema before use, and never assume a query value is a string.
Bad example
| 1 | app.get('/api/orders', async (req, res) => { |
| 2 | // page is `string | string[] | undefined`; Number('') === 0, Number(['1','2']) === NaN |
| 3 | const page = Number(req.query.page); |
| 4 | const skip = page * 20; // NaN or huge values silently flow into the query |
| 5 |
|
| 6 | // ?status[$ne]=shipped -> req.query.status is an OBJECT, not a string |
| 7 | const orders = await Order.find({ status: req.query.status }); |
| 8 | res.json(orders.slice(skip, skip + 20)); |
| 9 | }); |
Explanation (EN)
Express parses `?status[$ne]=x` into a nested object, so a string-typed parameter can arrive as `{ $ne: 'x' }` and inject operators into a Mongo/NoSQL query (operator injection). Numeric params via `Number()` silently yield `NaN`, `0`, or absurd offsets. The TypeScript type `string` is a lie at the HTTP boundary — the runtime value can be an array or object.
Objašnjenje (HR)
Express parsira `?status[$ne]=x` u ugniježđeni objekt, pa string-tipizirani parametar može stići kao `{ $ne: 'x' }` i ubaciti operatore u Mongo/NoSQL upit (operator injection). Numerički parametri preko `Number()` tiho daju `NaN`, `0` ili apsurdne offsete. TypeScript tip `string` je laž na HTTP granici — runtime vrijednost može biti polje ili objekt.
Good example
| 1 | import { z } from 'zod'; |
| 2 |
|
| 3 | const OrdersQuery = z.object({ |
| 4 | page: z.coerce.number().int().min(1).max(1000).default(1), |
| 5 | // Constrain to a known set; an array/object value fails the enum check. |
| 6 | status: z.enum(['pending', 'shipped', 'delivered']).optional(), |
| 7 | }); |
| 8 |
|
| 9 | app.get('/api/orders', async (req, res) => { |
| 10 | const result = OrdersQuery.safeParse(req.query); |
| 11 | if (!result.success) return res.status(400).json({ error: result.error.flatten() }); |
| 12 | const { page, status } = result.data; |
| 13 |
|
| 14 | const orders = await Order.find( |
| 15 | status ? { status } : {}, // status is now guaranteed to be a known string literal |
| 16 | ) |
| 17 | .skip((page - 1) * 20) |
| 18 | .limit(20); |
| 19 | res.json(orders); |
| 20 | }); |
Explanation (EN)
A schema coerces `page` to a bounded integer and constrains `status` to a literal set, so an object/array payload (`status[$ne]=...`) fails validation instead of becoming a query operator. Coercion plus min/max kills the `NaN`/overflow class of bugs. The downstream query now receives only the narrow, known-shaped values the schema guarantees.
Objašnjenje (HR)
Shema prisiljava `page` na omeđeni cijeli broj i ograničava `status` na skup literala, pa objekt/polje payload (`status[$ne]=...`) padne na validaciji umjesto da postane operator upita. Koercija plus min/max ubija klasu bugova `NaN`/overflow. Upit nizvodno sada prima samo uske, poznatog oblika vrijednosti koje shema jamči.
Notes (EN)
Maps to OWASP A03:2021 Injection (incl. NoSQL operator injection) and the OWASP Input Validation / Mass Assignment cheat sheets. Pairs with using parameterized queries or a query-sanitizer like express-mongo-sanitize.
Bilješke (HR)
Mapira se na OWASP A03:2021 Injection (uklj. NoSQL operator injection) i OWASP Input Validation / Mass Assignment cheat sheetove. Ide uz parametrizirane upite ili sanitizer poput express-mongo-sanitize.
Exceptions / Tradeoffs (EN)
Where supported, disabling rich query parsing globally (e.g. Express `query parser` set to 'simple') reduces the operator-injection surface, but you should still validate types and ranges per route.
Iznimke / Tradeoffi (HR)
Gdje je podržano, globalno onemogućavanje bogatog parsiranja upita (npr. Express `query parser` na 'simple') smanjuje površinu za operator-injection, ali i dalje trebaš validirati tipove i raspone po ruti.