CVE-2024-34078: Insufficient Information (NVD-CWE-noinfo)
value: html-sanitizer is an allowlist-based HTML cleaner. If using `keep_typographic_whitespace=False` (which is the default), the sanitizer normalizes unicode to the NFKC form at the end. Some unicode characters normalize to chevrons; this allows specially crafted HTML to escape sanitization. The problem has been fixed in 2.4.2.

CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-06T15:15Z
Repository: https://github.com/matthiask/html-sanitizer
Language: Python
Files changed: CHANGELOG.rst, html_sanitizer/sanitizer.py, html_sanitizer/tests.py
Commit date: 2024-05-05 21:08:14 +0200
Commit message: Normalize unicode earlier

Diff: diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1939570..68c06f1 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,10 +3,15 @@ Change log
 ==========
 
 Next version
 ============
 
+- **Vulnerability:** Fixed an issue where normalizing unicode too late in the
+  process would keep disallowed tags when using specially crafted HTML. Fixed
+  in 2.4.2.
+
+
 2.4 (2024-04-01)
 ================
 
 - Fixed an edge case where ``br`` tag attributes weren't removed if the br tag
   appears first.
diff --git a/html_sanitizer/sanitizer.py b/html_sanitizer/sanitizer.py
index 2e274c6..26dcba1 100644
--- a/html_sanitizer/sanitizer.py
+++ b/html_sanitizer/sanitizer.py
@@ -248,10 +248,16 @@ class Sanitizer:
         Removes everything not explicitly allowed in ``self.allowed_tags``.
 
         Requires ``lxml`` and, for especially broken HTML, ``beautifulsoup4``.
         """
 
+        # normalize unicode
+        if self.keep_typographic_whitespace:
+            html = unicodedata.normalize("NFC", html)
+        else:
+            html = unicodedata.normalize("NFKC", html)
+
         html = normalize_overall_whitespace(
             html,
             keep_typographic_whitespace=self.keep_typographic_whitespace,
             whitespace_re=self.whitespace_re,
         )
@@ -418,12 +424,6 @@ class Sanitizer:
         html = re.sub(r"<([^/>]+)/>", r"<\1 />", html)
 
         # remove wrapping tag needed by XML parser
         html = re.sub(r"^<div>|</div>$", "", html)
 
-        # normalize unicode
-        if self.keep_typographic_whitespace:
-            html = unicodedata.normalize("NFC", html)
-        else:
-            html = unicodedata.normalize("NFKC", html)
-
         return html
diff --git a/html_sanitizer/tests.py b/html_sanitizer/tests.py
index 2700e83..a16213f 100644
--- a/html_sanitizer/tests.py
+++ b/html_sanitizer/tests.py
@@ -653,5 +653,15 @@ git commit
                 ("<p><br hello=\"alert('world');\"/><br></p>", ""),
                 ('<p hello="world"></p>', ""),
                 ("<br hello=\"alert('world');\"/><br>", "<br>"),
             ]
         )
+
+    def test_normalize_early(self):
+        self.run_tests(
+            [
+                (
+                    "\uff1cimg src=x onerror=\uff02alert(window.location)\uff02\uff1e",
+                    "",
+                ),
+            ]
+        )

Vulnerable code:         # normalize unicode
        if self.keep_typographic_whitespace:
            html = unicodedata.normalize("NFC", html)
        else:
            html = unicodedata.normalize("NFKC", html)

Fixed code: - **Vulnerability:** Fixed an issue where normalizing unicode too late in the
  process would keep disallowed tags when using specially crafted HTML. Fixed
  in 2.4.2.


        # normalize unicode
        if self.keep_typographic_whitespace:
            html = unicodedata.normalize("NFC", html)
        else:
            html = unicodedata.normalize("NFKC", html)


    def test_normalize_early(self):
        self.run_tests(
            [
                (
                    "\uff1cimg src=x onerror=\uff02alert(window.location)\uff02\uff1e",
                    "",
                ),
            ]
        )