{"id":13491428,"url":"https://github.com/matthiask/html-sanitizer","last_synced_at":"2025-05-15T15:00:19.822Z","repository":{"id":60775459,"uuid":"89838546","full_name":"matthiask/html-sanitizer","owner":"matthiask","description":"Allowlist-based HTML cleaner","archived":false,"fork":false,"pushed_at":"2024-12-19T08:26:06.000Z","size":268,"stargazers_count":142,"open_issues_count":12,"forks_count":24,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-12T05:08:39.151Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/matthiask.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-30T08:18:39.000Z","updated_at":"2025-04-30T22:29:21.000Z","dependencies_parsed_at":"2022-10-04T17:01:19.939Z","dependency_job_id":"74f8e8ec-47b7-4e6c-a329-32173e563ef0","html_url":"https://github.com/matthiask/html-sanitizer","commit_stats":{"total_commits":251,"total_committers":17,"mean_commits":"14.764705882352942","dds":"0.29083665338645415","last_synced_commit":"440d8b54018971a4006ffaf23187e9da543be1a4"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthiask%2Fhtml-sanitizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthiask%2Fhtml-sanitizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthiask%2Fhtml-sanitizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthiask%2Fhtml-sanitizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matthiask","download_url":"https://codeload.github.com/matthiask/html-sanitizer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364264,"owners_count":22058877,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T19:00:56.921Z","updated_at":"2025-05-15T15:00:18.650Z","avatar_url":"https://github.com/matthiask.png","language":"Python","funding_links":[],"categories":["Libs","Python"],"sub_categories":["Other"],"readme":"==============\nHTML sanitizer\n==============\n\nThis is a allowlist-based and very opinionated HTML sanitizer that\ncan be used both for untrusted and trusted sources. It attempts to clean\nup the mess made by various rich text editors and or copy-pasting to\nmake styling of webpages simpler and more consistent. It builds on the\nexcellent HTML cleaner in lxml_ to make the result both valid and safe.\n\nHTML sanitizer goes further than e.g. bleach_ in that it not only\nensures that content is safe and tags and attributes conform to a given\nallowlist, but also applies additional transforms to HTML fragments.\n\nGoals\n=====\n\n- Clean up HTML fragments using a very restricted set of allowed tags\n  and attributes.\n- Convert *some* tags (such as ``\u003cspan style=\"...\"\u003e``, ``\u003cb\u003e`` and\n  ``\u003ci\u003e``) into either ``\u003cstrong\u003e`` or ``\u003cem\u003e`` (but never both).\n- Absolutely disallow all inline styles.\n- Normalize whitespace by removing repeated line breaks, empty\n  paragraphs and other empty elements.\n- Merge adjacent tags of the same type (such as several ``\u003cstrong\u003e`` or\n  ``\u003ch3\u003e`` directly after each other.\n- Automatically remove redundant list markers inside ``\u003cli\u003e`` tags.\n- Clean up some uglyness such as paragraphs inside paragraphs or list\n  elements etc.\n- Normalize unicode.\n\nUsage\n=====\n\n    \u003e\u003e\u003e from html_sanitizer import Sanitizer\n    \u003e\u003e\u003e sanitizer = Sanitizer()  # default configuration\n    \u003e\u003e\u003e sanitizer.sanitize('\u003cspan style=\"font-weight:bold\"\u003esome text\u003c/span\u003e')\n    '\u003cstrong\u003esome text\u003c/strong\u003e'\n\nSettings\n========\n\n- Bold spans and ``b`` tags are converted into ``strong`` tags, italic\n  spans and ``i`` tags into ``em`` tags (if ``strong`` and ``em`` are\n  allowed at all)\n- Inline styles and scripts will always be dropped.\n- A ``div`` element is used to wrap the HTML fragment for the parser,\n  therefore ``div`` tags are not allowed.\n\nThe default settings are::\n\n    DEFAULT_SETTINGS = {\n        \"tags\": {\n            \"a\", \"h1\", \"h2\", \"h3\", \"strong\", \"em\", \"p\", \"ul\", \"ol\",\n            \"li\", \"br\", \"sub\", \"sup\", \"hr\",\n        },\n        \"attributes\": {\"a\": (\"href\", \"name\", \"target\", \"title\", \"id\", \"rel\")},\n        \"empty\": {\"hr\", \"a\", \"br\"},\n        \"separate\": {\"a\", \"p\", \"li\"},\n        \"whitespace\": {\"br\"},\n        \"keep_typographic_whitespace\": False,\n        \"add_nofollow\": False,\n        \"autolink\": False,\n        \"sanitize_href\": sanitize_href,\n        \"element_preprocessors\": [\n            # convert span elements into em/strong if a matching style rule\n            # has been found. strong has precedence, strong \u0026 em at the same\n            # time is not supported\n            bold_span_to_strong,\n            italic_span_to_em,\n            tag_replacer(\"b\", \"strong\"),\n            tag_replacer(\"i\", \"em\"),\n            tag_replacer(\"form\", \"p\"),\n            target_blank_noopener,\n        ],\n        \"element_postprocessors\": [],\n        \"is_mergeable\": lambda e1, e2: True,\n    }\n\nThe keys' meaning is as follows:\n\n- ``tags``: A ``set()`` of allowed tags.\n- ``attributes``: A ``dict()`` mapping tags to their allowed attributes.\n- ``empty``: Tags which are allowed to be empty. By default, empty tags\n  (containing no text or only whitespace) are dropped.\n- ``separate``: Tags which are not merged if they appear as siblings. By\n  default, tags of the same type are merged.\n- ``whitespace``: Tags which are treated as whitespace and removed from\n  the beginning or end of other tags' content.\n- ``keep_typographic_whitespace``: Keep typographically used space\n  characters like non-breaking space etc.\n- ``add_nofollow``: Whether to add ``rel=\"nofollow\"`` to all links.\n- ``autolink``: Enable lxml_'s autolinker_. May be either a boolean or a\n  dictionary; a dictionary is passed as keyword arguments to\n  ``autolink``.\n- ``sanitize_href``: A callable that gets anchor's ``href`` value and\n  returns a sanitized version. The default implementation checks whether\n  links start with a few allowed prefixes, and if not, returns a single\n  hash (``#``).\n- ``element_preprocessors`` and ``element_postprocessors``: Additional\n  filters that are called on all elements in the tree. The tree is\n  processed in reverse depth-first order. Under certain circumstances\n  elements are processed more than once (search the code for\n  ``backlog.append``). Preprocessors are run before whitespace\n  normalization, postprocessors afterwards.\n- ``is_mergeable``: Adjacent elements which aren't kept ``separate`` are\n  merged by default. This callable can be used to prevent merging of\n  adjacent elements e.g. when their classes do not match\n  (``lambda e1, e2: e1.get('class') == e2.get('class')``)\n\nSettings can be specified partially when initializing a sanitizer\ninstance, but are still checked for consistency. For example, it is not\nallowed to have tags in ``empty`` that are not in ``tags``, that is,\ntags that are allowed to be empty but at the same time not allowed at\nall. The ``Sanitizer`` constructor raises ``TypeError`` exceptions when\nit detects inconsistencies.\n\nAn example for an even more restricted configuration might be::\n\n    \u003e\u003e\u003e from html_sanitizer import Sanitizer\n    \u003e\u003e\u003e sanitizer = Sanitizer({\n    ...     'tags': ('h1', 'h2', 'p'),\n    ...     'attributes': {},\n    ...     'empty': set(),\n    ...     'separate': set(),\n    ... })\n\nThe rationale for such a restricted set of allowed tags (e.g. no\nimages) is documented in the `design decisions`_ section of\ndjango-content-editor_'s documentation.\n\nDjango\n======\n\nHTML sanitizer does not depend on Django, but ships with a module which\nmakes configuring sanitizers using Django settings easier. Usage is as\nfollows::\n\n    \u003e\u003e\u003e from html_sanitizer.django import get_sanitizer\n    \u003e\u003e\u003e sanitizer = get_sanitizer([name=...])\n\nDifferent sanitizers can be configured. The default configuration is\naptly named ``'default'``. Example settings follow::\n\n    HTML_SANITIZERS = {\n        'default': {\n          'tags': ...,\n        },\n        ...\n    }\n\nThe ``'default'`` configuration is special: If it isn't explicitly\ndefined, the default configuration above is used instead. Non-existing\nconfigurations will lead to ``ImproperlyConfigured`` exceptions.\n\nThe ``get_sanitizer`` function caches sanitizer instances, so feel free\nto call it as often as you want to.\n\n\nSecurity issues\n===============\n\nPlease report security issues to me directly at mk@feinheit.ch.\n\n\n.. _bleach: https://bleach.readthedocs.io/\n.. _Django: https://www.djangoproject.com/\n.. _django-content-editor: http://django-content-editor.readthedocs.io/\n.. _FeinCMS: https://pypi.python.org/pypi/FeinCMS\n.. _feincms-cleanse: https://pypi.python.org/pypi/feincms-cleanse\n.. _design decisions: http://django-content-editor.readthedocs.io/en/latest/#design-decisions\n.. _lxml: http://lxml.de/\n.. _autolinker: http://lxml.de/api/lxml.html.clean-module.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthiask%2Fhtml-sanitizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthiask%2Fhtml-sanitizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthiask%2Fhtml-sanitizer/lists"}