C.W.K.
Stream
Lesson 07 of 08 · published

HTML in Markdown — Power and Danger

~12 min · markdown, html, security, xss

Level 0Plaintext
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Markdown is a superset of HTML, with consequences

CommonMark allows raw HTML inline and as block elements. Inline tags (<span>, <kbd>, <sub>, <sup>, <abbr>, <cite>) work where Markdown emphasis works. Block tags (<div>, <table>, <details>) need a blank line before and after to be treated as a block.

Useful HTML in Markdown

  • Image sizing — Markdown has no width attribute; use <img src="..." width="400" alt="...">.
  • Collapsible sections<details><summary>Click</summary>...</details>. Renders on GitHub.
  • Keyboard shortcuts<kbd>Cmd</kbd>+<kbd>K</kbd> shows a styled key.
  • Subscript / superscriptH<sub>2</sub>O, x<sup>2</sup>.
  • Complex tables — when GFM tables can't (rowspan, colspan, block content in cells), drop to <table>.

The XSS warning

If your site renders user-supplied Markdown (comments, wiki, forum posts) you must sanitize the output HTML. Without sanitization, <script>alert(1)</script> in a comment becomes a real script tag in the page. GitHub strips dangerous tags; many static-site generators do not (they assume the author is trusted). Know which side of the line your renderer is on.

Principle: trusted-author Markdown can include any HTML. Untrusted-author Markdown must be sanitized server-side with a known-good library (rehype-sanitize, DOMPurify, bleach, sanitize-html). 'I'll sanitize on the client' is an XSS vulnerability waiting for a refactor.

Code

Useful inline HTML·markdown
Press <kbd>Cmd</kbd>+<kbd>K</kbd> to open the palette.

The formula is H<sub>2</sub>O, not H<sup>2</sup>O.

See <abbr title="Common Mark">CM</abbr> spec for details.
Collapsible details (renders on GitHub)·markdown
<details>
<summary>Click to expand the long stack trace</summary>

```
Traceback (most recent call last):
  File "app.py", line 42, in <module>
    main()
  File "app.py", line 37, in main
    raise ValueError("boom")
ValueError: boom
```

</details>
Sized image·markdown
<img src="./architecture.png" alt="System architecture diagram" width="600">
Sanitization in code (rehype, JS)·javascript
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

const html = String(
  await unified()
    .use(remarkParse)
    .use(remarkRehype, { allowDangerousHtml: true })
    .use(rehypeSanitize)  // strips <script>, on* attrs, etc.
    .use(rehypeStringify)
    .process(userMarkdown)
)

External links

Exercise

Open a stale README and find one place where Markdown isn't enough — an image you wish were smaller, a stack trace you want collapsible, a keyboard shortcut you want styled. Write the HTML inline and verify it renders on GitHub. Then ask yourself: if a stranger could write this Markdown for my site, would the same HTML still be acceptable? That gap is your sanitization decision.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.