- Uploads: Server entfernt EXIF/XMP/Text (GPS-Standort, Kamera, Uhrzeit) per Pillow; Drehung von Hochkant-Fotos wird vorher angewendet, Farbprofil bleibt; GIFs unverändert (neue Abhängigkeit pillow) - Produktvorschläge pro Liste (Tabelle list_products): Einträge privater Listen tauchen bei anderen weder in Vorschlägen noch beim Rezeptabgleich auf; "Vorschläge verwalten" löscht nur aus eigenen sichtbaren Listen - Artikel: Bilder nur noch aus den eigenen Uploads (kein Tracking-Pixel fremder Server); Bereinigung in htmlclean.py - /api/docs und /api/openapi.json nur noch im Entwicklungsmodus (RELOAD=1 oder P42_API_DOCS=1) - Migration Schritt 2: bestehende Vorschläge den Listen zuordnen, Artikel neu bereinigen, gespeicherte Bilder mit Metadaten säubern - Tests in test_datenschutz.py (u. a. Rezept-Zuordnung, Umstellung) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
33 lines
1.2 KiB
Python
33 lines
1.2 KiB
Python
"""Artikel-HTML bereinigen: nur einfache Formatierung, Links und Bilder aus den eigenen Uploads.
|
|
|
|
Bilder von fremden Servern sind nicht erlaubt: Beim Lesen würde der Browser sie dort abrufen und so
|
|
IP-Adresse und Lesezeitpunkt verraten (Tracking-Pixel). Externe Links bleiben erlaubt (erst ein Klick ruft sie auf).
|
|
"""
|
|
import re
|
|
|
|
import nh3
|
|
|
|
ALLOWED_TAGS = {"p", "br", "h2", "h3", "strong", "b", "em", "i", "u", "s", "ul", "ol", "li", "blockquote", "hr", "a", "img"}
|
|
ALLOWED_ATTRS = {"a": {"href"}, "img": {"src", "alt"}}
|
|
UPLOAD_SRC = re.compile(r"/uploads/[a-f0-9]{24}\.(?:jpg|png|gif|webp)")
|
|
|
|
|
|
def _attribute_filter(tag: str, attr: str, value: str) -> str | None:
|
|
if tag == "img" and attr == "src" and not UPLOAD_SRC.fullmatch(value):
|
|
return None
|
|
return value
|
|
|
|
|
|
def sanitize(content: str) -> str:
|
|
cleaned = nh3.clean(
|
|
content,
|
|
tags=ALLOWED_TAGS,
|
|
clean_content_tags={"script", "style"},
|
|
attributes=ALLOWED_ATTRS,
|
|
attribute_filter=_attribute_filter,
|
|
url_schemes={"http", "https", "mailto"},
|
|
link_rel="noopener noreferrer",
|
|
)
|
|
# Bilder, deren Quelle entfernt wurde, ganz weglassen
|
|
return re.sub(r"<img(?![^>]*\ssrc=)[^>]*>", "", cleaned).strip()
|