ASVS 5.0 V1.3.5 (Level 2) requires that the application sanitizes or disables user supplied scriptable or expression based content, such as Markdown, CSS, XSL stylesheets and BBCode. These formats look like plain text features but can carry raw HTML, remote loads or code execution.
Why it matters
Markdown allows inline HTML in many renderers, so a comment containing a script tag survives conversion. User supplied CSS can leak data through attribute selectors and background URLs, and can overlay fake login forms. XSLT stylesheets can read files and call extension functions in some processors. BBCode parsers often build links from user values without checking the scheme.
How to implement it
Treat the renderer's output as untrusted HTML and sanitize that output, and turn off every feature you do not need.
import { marked } from 'marked';
import DOMPurify from 'dompurify';
const html = marked.parse(userMarkdown, { gfm: true, breaks: false });
const safe = DOMPurify.sanitize(html, { USE_PROFILES: { html: true } });
import markdown_it, nh3
md = markdown_it.MarkdownIt("commonmark", {"html": False}) # raw HTML disabled
safe = nh3.clean(md.render(user_markdown))
For CSS, do not accept free form stylesheets. Offer a set of theme choices or a few validated properties. For XSL, do not run user stylesheets, and if you must, use a processor with extensions and file access disabled. For BBCode, restrict URLs to http and https.
How to test it
Submit Markdown containing <script>alert(1)</script>, [x](javascript:alert(1)) and ). Submit CSS with background:url(https://attacker.example/?x). Rendered output must contain no script, no event handlers and no external loads.
Common mistakes
- Sanitizing the Markdown source instead of the HTML output.
- Leaving raw HTML on because "our users are trusted".
- Allowing style attributes in the sanitizer allowlist.
Where this goes wrong in practice
A documentation or comments feature adds Markdown because it is friendly, and the renderer's default allows raw HTML. Months later, someone posts a comment with an image tag carrying an event handler, and every reader of the thread runs it. The Markdown specification allows inline HTML, so this is expected behavior, not a bug in the library. The fix is in your configuration and in sanitizing the output.
Themes and custom CSS features have a quieter version of the problem. Attribute selectors combined with background URLs can send one character at a time of a page's token to an attacker's server. If you offer styling, expose a small list of properties, validate values against a pattern, and generate the CSS on the server.
Also review wiki style features that accept template tags or macros. They are a scripting language by another name, and they need the same treatment as any other expression language.
Quick check
Where do you sanitize Markdown?
On the generated HTML, after rendering, since that is what the browser will interpret.