Skip to content

Markdown rendering silently deletes text in angle brackets #11655

Description

@rjsparks

ietf.utils.markdown.markdown() passes its output through clean_html(), whose bleach Cleaner is built with strip=True, so anything that parses as a tag but is not allowlisted is deleted together with its text. Plain prose in angle brackets (e.g. a draft filename) silently disappears from the rendered page. 16 of 869 rendered wg/rg charters lose text this way (all concluded groups), and any future charter or markdown-rendered field is exposed. URLs and emails in angle brackets are unaffected because autolinks run before sanitising. Open question: clean_html() is shared with sanitize_html / sanitize_fragment (ietf_filters.py, meeting/utils.py), which take genuine HTML where escaping stray tags may be noisier than dropping them — change only the two markdown cleaners, or all callers?

Task

  • Build the two cleaners used by ietf/utils/markdown.py with strip=False so disallowed tags are escaped rather than deleted (script tags become text, allowed markup passes unchanged)

Agent-assisted triage

  • Suggested size: S
  • Suggested priority: major
  • Suggested lane: standard
  • Affected apps: doc/, group/
  • Updated with agent assistance on 2026-09-04

Original report

Describe the issue

ietf.utils.markdown.markdown() passes its output through clean_html(), whose
bleach Cleaner is built with strip=True (ietf/utils/html.py). Anything that
parses as a tag but is not on the allowlist is removed along with its text, so
plain prose in angle brackets disappears from the rendered page with no trace.

To reproduce

from ietf.utils.markdown import markdown
markdown("See <draft-ietf-wnils-whois-03.txt> for details.")
# '<p>See  for details.</p>'

Impact

Scanning all 869 rendered wg/rg charters, 16 groups lose text this way,
including:

  • find — <draft-ietf-wnils-whois-03.txt>, <draft-ietf-wnils-whois-mesh-01.txt>
  • tip — <draft-lyon-itp-nodes-01.txt> (3 occurrences)
  • urn — <draft-daigle-urnframework-00.txt>, <draft-daniel-naptr-01.txt>
  • aaarg — <Filename>
  • netvc — <video>

All are concluded groups today, but the same applies to any future charter,
group description, or other markdown-rendered field. URLs and email addresses in
angle brackets are unaffected — those are handled as autolinks before
sanitizing.

Suggested fix

Build the cleaners with strip=False, which escapes disallowed tags instead of
deleting them:

<p>See &lt;draft-ietf-wnils-whois-03.txt&gt; for details.</p>

This is still safe — <script>alert(1)</script> is escaped to text, not
executed — and allowed tags, attributes, and links pass through unchanged.

Note this affects every clean_html() caller, not just markdown: the
sanitize_html / sanitize_fragment filters in
ietf/doc/templatetags/ietf_filters.py and ietf/meeting/utils.py. Those take
input that is genuinely HTML, where escaping stray tags may be noisier than
dropping them, so it may be preferable to change only the two cleaners used by
ietf/utils/markdown.py.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions