When you render HTML that came from a user — a comment, a profile bio, a chat message — you can't trust it. A <script> tag or an onclick="..." attribute slipped into that markup runs in your page with your user's session. A sanitizer defends against this by allowing only a known-safe set of tags and attributes through and removing everything else. You'll implement a simplified one that cleans a DOM subtree in place.
type Options = {
allowedTags: string[]; // tag names to keep, e.g. ['p', 'a', 'b']
allowedAttributes: string[]; // attribute names to keep, e.g. ['href', 'id']
};
function htmlSanitizer(root: Element, options: Options): Element;
You receive a root element whose subtree may contain untrusted markup. Mutate that subtree in place and return the same root. Comparisons of tag and attribute names are case-insensitive.
const root = document.createElement('div');
root.innerHTML = '<p onclick="steal()">hi</p><script>alert(1)</script>';
htmlSanitizer(root, { allowedTags: ['p'], allowedAttributes: [] });
root.innerHTML;
// → '<p>hi</p>'
// the <script> is gone; the <p> stays but its onclick is stripped
const root = document.createElement('div');
root.innerHTML = '<a href="/home" onclick="evil()">go</a>';
htmlSanitizer(root, { allowedTags: ['a'], allowedAttributes: ['href'] });
root.innerHTML;
// → '<a href="/home">go</a>' (href kept, onclick dropped)
allowedTags, remove the element and its whole subtree. Don't unwrap it or keep its children.allowedAttributes. This is what neutralizes onclick, onerror, style, and friends.<script> or an onerror arbitrarily deep in the tree — you must clean the whole subtree, not just direct children.DIV and div, HREF and href, as the same name.root; don't build and return a new one.