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).
Allow-list dynamic SQL identifiers and sort direction
Table/column names and ASC/DESC are not bindable — map user choices through a fixed allow-list, never interpolate them.
Bad example
| 1 | function listProducts(sortBy: string, dir: string) { |
| 2 | // ❌ identifiers and direction interpolated from input |
| 3 | return pool.query( |
| 4 | `SELECT * FROM products ORDER BY ${sortBy} ${dir}`, |
| 5 | ); |
| 6 | } |
| 7 | // sortBy = "(CASE WHEN (SELECT ...) THEN price ELSE name END)" -> blind injection / enumeration |
Explanation (EN)
Column names and sort direction can't be passed as bound parameters, so this code interpolates them. An attacker can inject subqueries in the ORDER BY clause to enumerate data (blind injection) even when the WHERE values are parameterized.
Objašnjenje (HR)
Imena stupaca i smjer sortiranja ne mogu se proslijediti kao vezani parametri, pa ih ovaj kod interpolira. Napadač može ubaciti podupite u ORDER BY i enumerirati podatke (slijepa injekcija) čak i kad su WHERE vrijednosti parametrizirane.
Good example
| 1 | const SORTABLE = { |
| 2 | name: 'name', |
| 3 | price: 'price', |
| 4 | created: 'created_at', |
| 5 | } as const; |
| 6 |
|
| 7 | function listProducts(sortBy: string, dir: string) { |
| 8 | // ✅ resolve identifier from a fixed map; reject anything unknown |
| 9 | const column = SORTABLE[sortBy as keyof typeof SORTABLE] ?? 'created_at'; |
| 10 | const direction = dir?.toLowerCase() === 'asc' ? 'ASC' : 'DESC'; |
| 11 | return pool.query( |
| 12 | `SELECT * FROM products ORDER BY ${column} ${direction}`, |
| 13 | ); |
| 14 | } |
Explanation (EN)
User input only selects a key into a server-defined map of safe column names, and direction collapses to one of two literals. The string interpolated into SQL can only ever be a value the developer authored, so injection is impossible.
Objašnjenje (HR)
Korisnički ulaz samo odabire ključ u mapi sigurnih imena stupaca koju definira poslužitelj, a smjer se svodi na jedan od dva literala. String koji se umeće u SQL uvijek može biti samo vrijednost koju je autor napisao, pa je injekcija nemoguća.
Exceptions / Tradeoffs (EN)
None for input-derived identifiers — there is no safe escaping alternative. If the set of valid columns is large, generate the allow-list from your schema/model metadata rather than from request data.
Iznimke / Tradeoffi (HR)
Nema iznimaka za identifikatore izvedene iz ulaza — ne postoji sigurna alternativa escapanju. Ako je skup valjanih stupaca velik, generiraj allow-listu iz metapodataka sheme/modela, ne iz podataka zahtjeva.