· 2 min read · ← All posts
Ardan Labs Go Security AI

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:

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.

← Back to all posts