pypa / readme_renderer

Safely render long_description/README files in Warehouse
Apache License 2.0
158 stars 88 forks source link

feat: allow admonitions to be rendered #242

Closed miketheman closed 2 years ago

miketheman commented 2 years ago

Fixes #132 Relates to https://github.com/pypa/warehouse/issues/8300

Important commit: 9277f4a59c8ecff42a1c3c85acea5ce749f3056f

Signed-off-by: Mike Fiedler miketheman@gmail.com

miketheman commented 2 years ago

I didn't find a simpler way to allow a subset of of the class attributes through, so if there's a better way, happy to adapt.

miketheman commented 2 years ago

I played around with bleach's behavior to pass a function instead of string values for ALLOWED_ATTRIBUTES, but didn't come up with anything particularly satisfying.

diff --git a/readme_renderer/clean.py b/readme_renderer/clean.py
index 332dd5b..bba5889 100644
--- a/readme_renderer/clean.py
+++ b/readme_renderer/clean.py
@@ -22,6 +22,37 @@ import bleach.linkifier
 import bleach.sanitizer

+def _allow_admonitions(tag: str, name: str, value: str) -> bool:
+    """Helper function to allow a subset of admonition-focused `class` attributes"""
+    # Preserves existing behavior, wildcard is overridden
+    if name in ("id", "align"):
+        return True
+
+    admonition_values = (
+        "attention",
+        "caution",
+        "danger",
+        "error",
+        "hint",
+        "important",
+        "note",
+        "tip",
+        "warning",
+        "admonition",
+        "first",
+        "last"
+    )
+
+    if name == 'class' and all(
+        val.startswith(admonition_values)
+        for val
+        in value.split()
+    ):
+        return True
+
+    return False
+
+
 ALLOWED_TAGS = [
     # Bleach Defaults
     "a", "abbr", "acronym", "b", "blockquote", "code", "em", "i", "li", "ol",
@@ -47,7 +78,7 @@ ALLOWED_ATTRIBUTES = {
     "span": ["class"],
     "th": ["align"],
     "td": ["align"],
-    "div": ["align"],
+    "div": _allow_admonitions,
     "h1": ["align"],
     "h2": ["align"],
     "h3": ["align"],
@@ -55,7 +86,7 @@ ALLOWED_ATTRIBUTES = {
     "h5": ["align"],
     "h6": ["align"],
     "code": ["class"],
-    "p": ["align"],
+    "p": _allow_admonitions,
     "ol": ["start"],
     "input": ["type", "checked", "disabled"],
 }

This would allow me to revert any changes to existing fixtures and remove the `class="section=..." values, preserving the existing behavior, but at the cost of this new function.

miketheman commented 2 years ago

@di any thoughts on this?