FlawOpen

flawopen.com/XSS/Markdown rendering

Is Markdown rendering safe from XSS?

Reference guide
Short answer

Treat rendered Markdown like untrusted HTML. A parser may preserve raw HTML because the syntax permits it; parsing Markdown does not sanitize the resulting HTML. Think of the parser as a translator that faithfully copies dangerous markup into another format, not as a security guard.

Where the trust boundary is

The CommonMark specification says tag-shaped raw HTML is rendered without escaping. Actual behavior depends on the parser, options, plugins, and host framework. For example, Marked explicitly warns that it does not sanitize generated HTML. Markdown syntax itself does not prove that input is safe, and a sanitizer must match the final HTML sink.

A safe rendering pipeline

UNSAFE — render and insert untrusted output
const html = marked.parse(userMarkdown);
container.innerHTML = html; // parser output is not sanitized
HARDENED — sanitize immediately before the HTML sink
import DOMPurify from "dompurify";

const html = marked.parse(userMarkdown);
container.innerHTML = DOMPurify.sanitize(html, {
  USE_PROFILES: { html: true }
});

The example uses DOMPurify's HTML-only profile because this view needs HTML, not SVG or MathML. Choose the smallest feature set your product needs. Do not mutate sanitized markup or pass it through another HTML-transforming library before insertion; either step can invalidate the sanitizer's guarantees. Prefer a framework's safe text rendering when rich HTML is unnecessary. Add Content Security Policy and Trusted Types as defense in depth, not as substitutes for sanitization.

Checks for a production review

FAQ

Does disabling raw HTML make Markdown safe?

It removes one input feature, but does not by itself establish safety. Review links, images, extensions, generated HTML, and the final insertion context; sanitize output when it reaches an HTML sink.

Can I sanitize first and then modify the HTML?

Avoid it. Changes after sanitization can introduce unsafe markup or invalidate the sanitizer's decisions. Perform transformations before sanitizing, then insert the sanitized result without further HTML rewriting.

Does DOMPurify make every output safe in every context?

No sanitizer is context-independent. Use it for HTML fragments according to its documented threat model, keep it current, choose a narrow policy, and do not reuse the result in a different context such as a JavaScript or CSS string.

References

See also Cross-Site Scripting (XSS, CWE-79): How It Works and How to Prevent It in Every Language — flawopen.com Is Svelte Safe from XSS by Default? Is SVG Upload an XSS Risk? Vue v-html XSS Risk and Safe Rendering Is WKWebView loadHTMLString Safe for Untrusted Content? Reflected vs. Stored vs. DOM-Based XSS Android addJavascriptInterface Security Risks