sanitize-html drops GFM markdown table tags when using an explicit allowedTags list
Markdown GFM tables silently degrade into orphan lines of cell text when the rendered HTML passes through sanitize-html with an EXPLICIT allowedTags list. The symptom reads like a markdown-parser bug, not a sanitizer one: the cell text is all present and inline markup inside cells (<strong>, <code>, <a>) survives, so a 10-column table renders as ~130 stray lines of text. Grepping the served HTML shows zero <table>/<thead>/<tbody>/<tr>/<th>/<td> tags while every other block element is intact, because sanitize-html drops disallowed tags but keeps their children (nonTextTags is what discards children, and table elements are not in it). The trap is that sanitize-html's DEFAULT allowedTags already contains table/thead/tbody/tr/th/td, so tables work until someone replaces the defaults with an explicit list, which projects commonly do to permit highlight.js output ('span' + class attribute). Tables then break in the same commit that fixes syntax highlighting, and nothing errors or warns. Observed 2026-08-13 on a marked + highlight.js + sanitize-html pipeline rendering user-submitted markdown.
Diagnose by grepping the SERVED html, not the markdown: if /</?table/i and /</?t[rdh][ >]/i are both false while the cell text and inline <strong> are present, it is the sanitizer, not the markdown parser.
Fix: extend the explicit allowlist rather than relying on defaults:
allowedTags: [...yourList, 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption'], allowedAttributes: { th: ['align'], td: ['align'], code: ['class'], span: ['class'] }
The 'align' attribute matters because marked emits align="left|center|right" for GFM ':---' / '---:' column alignment; without it the table renders but every column is default-aligned. No 'style' attribute is needed, so the security posture is unchanged.
Prevention: when you move from sanitize-html's defaults to an explicit allowlist, start from sanitizeHtml.defaults.allowedTags and concat your additions (allowedTags: sanitizeHtml.defaults.allowedTags.concat(['span'])) instead of hand-writing the list. Add a render test whose fixture contains one GFM table and one fenced code block; both classes of regression are invisible in unit tests that assert on markdown input.