Sanitize untrusted HTML

Clean untrusted HTML against an allowlist with turbohtml.clean.sanitize(), the bleach.clean successor, keeping only a safe subset of tags, attributes, and URL schemes.

Sanitize untrusted HTML

To clean user-submitted HTML the way bleach.clean did, use turbohtml.clean.sanitize(). A Policy says what to keep (here the relaxed preset for typical user content), and a non-overridable baseline drops scripting and javascript: URLs no matter what the policy allows:

from turbohtml.clean import sanitize, Policy

print(sanitize("<p>Hi <a href='javascript:alert(1)'>link</a></p><script>evil()</script>", Policy.relaxed()))
<p>Hi <a>link</a></p>&lt;script&gt;evil()&lt;/script&gt;

Trust the first pass, do not reparse

Sanitizing is a single parse pass, like DOMPurify. Its output is safe to insert into a DOM as it stands. Do not parse it again with a different engine or in a different context and then trust that second tree.

HTML parsing is not a fixpoint: serialize a tree and reparse it, and you can get a different tree. In foreign content a </p> or </br> end tag builds an HTML element inside the SVG/MathML root, while the matching start tag on a later parse breaks out of it; a raw carriage return in text also becomes a newline on reparse. sanitize resolves all of that in its one pass, so the string it returns is inert. Reparsing and reserializing that string can yield a different string that is still inert. The guarantee is inertness, not byte-stable output across a round trip.