Markdown SVG support changes your render security model
SVG support in Markdown improves developer experience, but it also expands the browser attack surface. If your app renders user or model-generated Markdown, review SVG as active content and test the full render pipeline, not one sanitizer in isolation.
Simon Willison recently wrote about upgrades to Markdown handling for SVG. The specific feature is small, but the underlying issue matters if you build AI or developer tools that render untrusted content. Markdown often sits on the boundary between user input and executable browser behavior. SVG sits on the boundary between image and document.
If your product accepts Markdown from users, LLMs, docs pipelines, or plugin ecosystems, your renderer is part of your security model. A permissive renderer improves formatting, but it also expands the attack surface. SVG support is a good example because many teams still treat SVG like a static bitmap. It is not. It is XML with active features, external references, and browser-specific parsing behavior.
For practitioners building AI apps, internal knowledge tools, blockchain dashboards, or developer portals in Go, this is a concrete review point. You need to know which Markdown features your stack accepts, how your sanitizer treats embedded SVG, and where browser rendering differs from your assumptions.
Treat SVG as active content
PNG and JPEG are passive for most web application purposes. SVG is different. It supports:
- Embedded scripts in some contexts
- External resource references
- CSS styling
- Links
- Filters and foreignObject blocks
- Event handler attributes
- IDs and fragment references
Some of these features are blocked when SVG is loaded through an <img> tag. Some are not available after sanitization. Some return when the same file is rendered inline in HTML, or passed through a Markdown engine that emits raw tags.
This is where teams get caught. They approve .svg uploads because the UI says “image upload”. Then a later feature path inlines the asset for styling or optimization. The trust boundary changes, but the old assumption stays in place.
In AI systems, this appears in generated reports, agent traces, notebook exports, and document previews. In blockchain systems, it appears in NFT metadata viewers, governance dashboards, transaction annotation tools, and block explorers that accept community content. The risk is not the asset alone. The risk is the rendering context.
What to inspect:
- Whether your Markdown engine allows raw HTML
- Whether SVG is rendered inline or referenced as a file
- Whether uploaded SVG is sanitized, transformed, or stored as-is
- Whether your CSP treats inline SVG as script-capable content
- Whether downstream PDF export or server-side rendering paths parse the same content differently
A useful rule is simple. If the browser parses the SVG DOM, treat it as active content until proven otherwise.
Verify the full render path, not one library in isolation
Many teams test one sanitizer and stop there. Real systems have a chain:
- Markdown parser
- HTML sanitizer
- Template renderer
- Front-end framework hydration
- Browser parsing
- Optional export path, such as PDF, email, or image snapshot
Each step changes risk. A parser might escape raw HTML. A later formatter might re-enable it. A sanitizer might strip <script> but leave foreignObject, xlink:href, or style-based URL references. A client-side component might inject sanitized output with innerHTML. A PDF exporter built on headless Chromium might resolve external references during print.
This matters in Go stacks because teams often combine libraries with different default models. One service parses CommonMark, another uses Goldmark with custom extensions, and the front end inserts the result into a component with a different sanitization rule. The composition, not the individual package, defines your exposure.
A practical verification flow:
- Build a corpus of SVG payloads with separate features, links, styles,
foreignObject, event attributes, and external references - Run each payload through every content path, user post, comment, AI answer, docs page, email preview, export job
- Diff the HTML before and after sanitization
- Open the final output in the browsers you support
- Monitor network requests during render to catch unexpected fetches
- Snapshot CSP violation reports if you have them enabled
You are looking for mismatches. A single path that preserves more SVG capability than the rest is enough to create a problem.
Focus on the dangerous SVG features
You do not need to ban SVG outright to improve safety. You need to know which constructs are high risk in your environment.
Common problem areas include:
foreignObject, which embeds HTML-like content inside SVGscripttags and event attributes such asonload- External references in
href,xlink:href,use, filters, fonts, and CSSurl() styleblocks with browser-dependent parsing edge cases- SMIL or animation features used to trigger state changes
- ID collisions when multiple SVGs share the same DOM
The last point is easy to miss. Inline SVG becomes part of the page DOM. IDs inside the asset are global within the document. If your app injects multiple user-controlled SVGs, one asset’s fragment references or style rules might interfere with another element on the page. This is less dramatic than script execution, but it still breaks integrity and predictability.
For AI products, generated diagrams deserve special attention. LLMs often emit SVG because it is compact and readable. If you render model output directly, you are accepting structured markup from a probabilistic generator. Treat it like code from an untrusted plugin. Store it, scan it, sanitize it, and render it in the least permissive context available.
Good defaults:
- Prefer file-based image embedding over inline SVG when you do not need DOM access
- Strip
foreignObject, scripts, event handlers, and external references - Restrict allowed elements and attributes to a small allowlist
- Rewrite IDs to unique prefixes if inline rendering is required
- Disallow inline style blocks unless you have audited the parser and sanitizer behavior
Test browser and export differences
The hard part with SVG security is not reading the spec. The hard part is implementation variance.
A payload blocked in one browser might degrade harmlessly there and behave differently in another context. A markdown preview in your web app might be safe, while the same content becomes unsafe in an export pipeline. This is common in systems that offer “download as PDF”, “share as static page”, or “send by email”.
Email is a special case because clients strip or rewrite content in inconsistent ways. PDF generation is another weak spot because renderers sometimes fetch resources during layout. Static site generation also shifts risk because a build step might inline assets, optimize them, or rewrite them in ways your runtime path never does.
For blockchain teams, this matters wherever token or contract metadata is displayed through multiple surfaces. A marketplace card, wallet preview, explorer page, and archived report might all process the same SVG differently. Even if you never touch token prices or trading data, the content supply chain still deserves review.
Verification steps:
- Test Chrome, Firefox, and Safari if your app supports them
- Test both development and production CSP, because local builds are often looser
- Test server-side rendering and client-side hydration separately
- Test print, PDF export, email preview, and mobile webviews
- Record whether rendering triggers network access, script-like behavior, or DOM mutation outside the asset
If you need one operational signal, pick outbound requests during render. Unexpected fetches often reveal hidden capability faster than code review does.
Set policy at the content boundary
The cleanest fix is policy, not patching. Decide what your system accepts at the boundary, then enforce it once.
A strong content policy for Markdown input often includes:
- No raw HTML from untrusted users
- SVG allowed only as uploaded files, not inline markup
- Sanitization with an allowlist, not a blocklist
- Distinct trust levels for staff content, automated content, and public user content
- CSP rules that assume sanitization will fail someday
- Regression tests for known-bad payloads
If you run AI features, add a separate rule for model output. Model-generated Markdown should not bypass the same controls you apply to user input. Teams often trust internal prompts and forget the model still emits untrusted text.
In Go services, keep the policy close to the rendering API. Do not let each handler decide its own sanitizer profile. Build one package for content rendering, one test corpus, and one approval path for any new extension. Markdown tables are low risk. Raw HTML and SVG are not in the same category.
A related operational check is your front-end CSP and headers. If a route renders user Markdown, its browser policy should reflect it. Pigfox’s Security Headers Grader is useful for checking whether the page-level protections match the content risk on routes where user or model output appears.
What to watch next
Markdown feature creep tends to arrive as a usability improvement. SVG support, math rendering, embedded HTML, and diagram extensions all look harmless when reviewed one by one. The risk comes from composition.
Watch for any change that moves content from text into browser-parsed structures. Review it as a security boundary change. If your product renders Markdown from users or LLMs, SVG deserves a place in your standard test corpus, alongside script payloads, hostile links, and export-path regressions.