#################### From sanitize-html #################### .. package-meta:: npm sanitize-html apostrophecms/sanitize-html `sanitize-html `_ is the standard Node HTML sanitizer: you declare the tags, attributes, URL schemes, and CSS properties you trust, and everything else is stripped. Its distinguishing feature is ``transformTags`` -- a map that renames an element (and optionally rewrites its attributes) during the sanitize pass, with a ``simpleTransform(tagName, attribs, merge)`` helper for the common rename-and-add case. Projects that ran sanitization in a Node service, or in a Python service shelling out to Node, reach for it because bleach and its successors historically had no equivalent rename step. turbohtml's :mod:`turbohtml.clean` sanitizer covers the same allowlist surface behind a frozen, thread-safe :class:`~turbohtml.clean.Policy`, and ``Policy.transform_tags`` is the direct port of ``transformTags``: it renames HTML elements during the same single C walk, with :class:`~turbohtml.clean.Transform` playing the role of ``simpleTransform``. Moving the sanitize step into Python drops the Node subprocess, and the second language with it. **************************** turbohtml vs sanitize-html **************************** .. list-table:: :header-rows: 1 :widths: 22 39 39 - - Dimension - turbohtml - sanitize-html - - Runtime - Python, filtering in a C extension - Node.js, filtering in JavaScript over htmlparser2 - - Rename step - ``Policy.transform_tags`` map, ``Transform`` for rename-and-add - ``transformTags`` map, ``simpleTransform`` for rename-and-add - - Rename safety - Renamed element re-checked against the allowlist and the unconditional baseline - Renamed element re-checked against ``allowedTags``/``allowedAttributes`` - - Configuration - One frozen ``Policy``, reusable across threads - A plain options object per call - - Typing - Fully annotated, ``py.typed`` - TypeScript definitions - - Dependencies - None (self-contained C extension) - htmlparser2, and its transitive tree Feature overlap =============== The transform surface ports one-to-one: - ``transformTags: { b: 'strong' }`` (rename) -> ``transform_tags={"b": "strong"}``. - ``transformTags: { center: sanitizeHtml.simpleTransform('div', { class: 'x' }) }`` (rename and add attributes) -> ``transform_tags={"center": Transform("div", {"class": "x"})}``. - ``allowedTags`` / ``allowedAttributes`` -> ``Policy.tags`` / ``Policy.attributes``. - ``allowedSchemes`` -> ``Policy.url_schemes``; ``allowedStyles`` -> ``Policy.allowed_styles``. Both apply the rename before the allowlist, so a transform decides an element's name but never its safety: mapping a tag to ``script`` still drops it, and an attribute added by a transform is scrubbed like the element's own. turbohtml keeps that guarantee under an *unconditional* baseline -- ``on*`` handlers, scripting elements, and ``javascript:`` URLs are removed regardless of the policy -- so a transform can smuggle neither a disallowed tag nor an unscrubbed attribute. The one shape difference: ``transformTags`` also accepts an arbitrary callback returning ``{ tagName, attribs, text }``, and a ``'*'`` entry that runs on every tag. ``transform_tags`` is a declarative per-tag map (a string or a :class:`~turbohtml.clean.Transform`); it renames and adds attributes without running caller code mid-walk, and per-tag rules cover the presentational-tag modernization ``simpleTransform`` was built for. Only HTML elements are transformed. Porting a transform =================== A sanitize-html config that modernizes legacy presentational tags: .. code-block:: javascript const clean = sanitizeHtml(dirty, { allowedTags: ["strong", "em", "div"], allowedAttributes: { div: ["class"] }, transformTags: { b: "strong", i: "em", center: sanitizeHtml.simpleTransform("div", { class: "legacy" }), }, }); ports to: .. testcode:: from turbohtml.clean import sanitize, Policy, Transform policy = Policy( tags=frozenset({"strong", "em", "div"}), attributes={"div": frozenset({"class"})}, transform_tags={ "b": "strong", "i": "em", "center": Transform("div", {"class": "legacy"}), }, ) print(sanitize("
bold and italic
", policy)) .. testoutput::
bold and italic
Performance =========== turbohtml renames in the same C walk that sanitizes, so a transform adds no extra pass. The table times both libraries end-to-end on a document dense in the presentational tags a transform rewrites; the sanitize-html figure is its Node runner over stdin, the cost a Python service pays to reach it as a subprocess. .. bench-table:: :file: bench/sanitize-html.json