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).
Enforce size and count limits on uploads before buffering the body
Declare per-file size, file-count, and total-request caps in the upload parser/middleware so oversized payloads are rejected with 413 before they hit memory or disk.
Bad example
| 1 | import multer from 'multer'; |
| 2 |
|
| 3 | // No limits: a single request can stream gigabytes into memory/disk. |
| 4 | const upload = multer({ storage: multer.memoryStorage() }); |
| 5 |
|
| 6 | app.post('/avatar', upload.single('file'), (req, res) => { |
| 7 | // req.file.buffer may be enormous; the process can OOM before this runs. |
| 8 | saveAvatar(req.user.id, req.file.buffer); |
| 9 | res.sendStatus(204); |
| 10 | }); |
Explanation (EN)
With no `limits`, Multer buffers the entire upload regardless of size, and `memoryStorage` keeps it all in RAM. An attacker (or a buggy client) can exhaust memory with one request, or send many files in one multipart body. This is a classic uncontrolled-resource-consumption DoS.
Objašnjenje (HR)
Bez `limits`, Multer baferira cijeli upload bez obzira na velicinu, a `memoryStorage` drzi sve u RAM-u. Napadac (ili neispravan klijent) moze iscrpiti memoriju jednim zahtjevom ili poslati mnogo datoteka u jednom multipart tijelu. Ovo je klasicni DoS nekontroliranom potrosnjom resursa.
Good example
| 1 | import multer from 'multer'; |
| 2 |
|
| 3 | const upload = multer({ |
| 4 | storage: multer.diskStorage({ destination: '/var/app/tmp' }), |
| 5 | limits: { |
| 6 | fileSize: 2 * 1024 * 1024, // 2MB per file — sized for avatars, not video |
| 7 | files: 1, // exactly one file per request |
| 8 | fields: 5, // cap non-file fields too |
| 9 | }, |
| 10 | }); |
| 11 |
|
| 12 | app.post('/avatar', (req, res) => { |
| 13 | upload.single('file')(req, res, (err) => { |
| 14 | if (err?.code === 'LIMIT_FILE_SIZE') return res.status(413).json({ message: 'File too large.' }); |
| 15 | if (err) return res.status(400).json({ message: 'Invalid upload.' }); |
| 16 | saveAvatar(req.user.id, req.file!.path); |
| 17 | res.sendStatus(204); |
| 18 | }); |
| 19 | }); |
Explanation (EN)
Explicit `fileSize`, `files`, and `fields` limits make Multer abort early and emit `LIMIT_FILE_SIZE`, which is mapped to 413. Limits are chosen per route based on the real use case. Pair a body-size limit at the reverse proxy (e.g. nginx `client_max_body_size`) so oversized requests die before reaching Node.
Objašnjenje (HR)
Eksplicitni `fileSize`, `files` i `fields` limiti tjeraju Multer da rano prekine i emitira `LIMIT_FILE_SIZE`, koji se mapira na 413. Limiti se biraju po ruti prema stvarnom slucaju koristenja. Dodaj limit velicine tijela na reverznom proxyju (npr. nginx `client_max_body_size`) da preveliki zahtjevi umru prije nego stignu do Nodea.
Notes (EN)
Defense in depth: enforce limits at the proxy AND in app code, because the proxy guards the process and the app guards business rules.
Bilješke (HR)
Obrana u dubinu: namece limite na proxyju I u kodu aplikacije, jer proxy stiti proces a aplikacija stiti poslovna pravila.
Exceptions / Tradeoffs (EN)
Legitimately large uploads (video, datasets) should use chunked/resumable uploads or direct-to-storage pre-signed URLs with the limit enforced by the storage provider, rather than raising the in-process buffer cap to a dangerous value.
Iznimke / Tradeoffi (HR)
Legitimno veliki uploadi (video, skupovi podataka) trebaju koristiti chunked/resumable upload ili izravni pre-signed URL prema pohrani s limitom koji namece pruzatelj pohrane, umjesto da se in-process bafer podigne na opasnu vrijednost.