"""Backend HTML sanitization for scraped Post.description. Provenance descriptions come from gallery-dl (untrusted third-party HTML). We render them via v-html in the UI sub-slice, so they MUST be sanitized server-side to a tight allowlist. nh3 (Rust/ammonia bindings) is used; bleach is deprecated upstream and intentionally not used. """ import re import nh3 # A faithful-but-safe set: enough to reproduce the SEMANTIC look of a scraped # post body (headings, lists, quotes, code, inline images, links, line breaks + # inline emphasis) without layout-injection wrappers. `div`/`span` are # deliberately NOT allowed — their text is preserved by nh3 and they only carry # source-site layout/styling we don't want to import (a test pins this). ALLOWED_TAGS = { "p", "a", "br", "em", "strong", "b", "i", "u", "s", "ul", "ol", "li", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "code", "img", "figure", "figcaption", } ALLOWED_ATTRIBUTES = { "a": {"href", "title", "target"}, "img": {"src", "alt", "title", "width", "height"}, } # http/https for hotlinked source images today; relative URLs (e.g. a local # `/api/...` src once inline images are captured) pass through nh3 untouched. ALLOWED_URL_SCHEMES = {"http", "https"} def sanitize_post_html(raw: str | None) -> str | None: """Return sanitized HTML, or None for null/empty/whitespace input. - keeps only ALLOWED_TAGS / ALLOWED_ATTRIBUTES - only http/https hrefs survive (javascript: etc. dropped) -