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
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 <draft-ietf-wnils-whois-03.txt> 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.
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
Agent-assisted triage
Original report
Describe the issue
ietf.utils.markdown.markdown()passes its output throughclean_html(), whosebleach
Cleaneris built withstrip=True(ietf/utils/html.py). Anything thatparses 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
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 ofdeleting them:
This is still safe —
<script>alert(1)</script>is escaped to text, notexecuted — and allowed tags, attributes, and links pass through unchanged.
Note this affects every
clean_html()caller, not just markdown: thesanitize_html/sanitize_fragmentfilters inietf/doc/templatetags/ietf_filters.pyandietf/meeting/utils.py. Those takeinput 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.