HTML responses & XSS prevention
Ce contenu n’est pas encore disponible dans votre langue.
The html tagged template escapes every interpolation by default, so
building an HTML response is safe without thinking about it. completeHtml
sends it with the right content-type.
import { completeHtml, html, Status } from 'actor-ts/http';
get((req) => { const name = req.query.name ?? 'world'; // untrusted return completeHtml(Status.OK, html` <h1>Hello, ${name}!</h1> `);});If name is <script>alert(1)</script>, the output is
Hello, <script>alert(1)</script>! — inert.
The pieces
Section titled “The pieces”| Export | What it does |
|---|---|
escapeHtml(s) | Escape & < > " ' and the backtick. |
html`…` | Tagged template; escapes interpolations, returns SafeHtml. |
SafeHtml | Brand for already-safe HTML; interpolated verbatim. |
rawHtml(s) | Wrap a string you have already made safe. |
completeHtml(status, body, headers?) | text/html; charset=utf-8 + X-Content-Type-Options: nosniff. |
Interpolation rules for html`…`: a SafeHtml value is inserted
verbatim (so fragments nest), arrays are rendered item-by-item,
null/undefined become the empty string, and everything else is coerced
to a string and escaped.
const items = ['<a>', '<b>'];const list = html`<ul>${items.map((i) => html`<li>${i}</li>`)}</ul>`;// <ul><li><a></li><li><b></li></ul>Untrusted markup
Section titled “Untrusted markup”Escaping neutralises markup — it does not make attacker-authored HTML
safe to render as HTML. If you must accept rich HTML (comments, wiki
markup), sanitise it with a dedicated, battle-tested library such as
sanitize-html or DOMPurify
before wrapping the result in rawHtml. A conservative built-in sanitizer
is tracked separately.
Where to next
Section titled “Where to next”- Error handling — safe error pages.
- Security headers — CSP, nosniff, and friends.
- Static files — the directory listing escapes filenames the same way.
