Files
NovaWoW/forum/sanitize.py
T
Inna 2c30844bf7 Añade foro de comunidad integrado (app forum) con el diseño de NovaWoW
- app 'forum': portada (categorías/foros), ver foro y tema (paginado), crear tema,
  responder, editar/borrar posts y moderación (fijar, bloquear, mover, borrar/restaurar)
- lee/escribe la BD acore_web (5ª conexión, DB_NAME_WEB) por SQL directo, tablas
  forum_categories/forums/forum_topics/forum_posts/forum_reads (portado de NovaWeb-main)
- identidad = cuenta de juego en sesión; permisos simplificados por gmlevel
  (FORUM_MOD_GMLEVEL) en vez de la matriz group_level×permission del original
- saneado del HTML de posts con nh3 (evita el XSS del original); POST+CSRF en formularios
- plantillas con los partials del sitio; enlace 'FOROS' del menú apunta al foro interno
- sql/forum_schema.sql y docs/FORO.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:10:55 +00:00

53 lines
1.5 KiB
Python

"""
Saneado del HTML de los posts del foro (el editor del cliente puede enviar HTML).
El proyecto original guardaba el HTML sin sanear y lo renderizaba con |raw
(XSS almacenado). Aquí lo saneamos con nh3 (allowlist) al guardar, de modo que
la plantilla puede renderizarlo con |safe sin riesgo.
"""
import nh3
# Etiquetas permitidas (formato básico de foro)
_ALLOWED_TAGS = {
"p", "br", "hr", "span", "div",
"strong", "b", "em", "i", "u", "s", "strike", "sub", "sup",
"ul", "ol", "li", "blockquote", "code", "pre",
"h1", "h2", "h3", "h4", "h5", "h6",
"a", "img",
"table", "thead", "tbody", "tr", "th", "td",
}
_ALLOWED_ATTRIBUTES = {
"a": {"href", "title", "target"},
"img": {"src", "alt", "title", "width", "height"},
"span": {"style"},
"div": {"style"},
"td": {"colspan", "rowspan"},
"th": {"colspan", "rowspan"},
}
# Solo esquemas de URL seguros
_ALLOWED_URL_SCHEMES = {"http", "https", "mailto"}
def clean_post_html(html):
"""Devuelve el HTML saneado y seguro para renderizar con |safe."""
if not html:
return ""
return nh3.clean(
html,
tags=_ALLOWED_TAGS,
attributes=_ALLOWED_ATTRIBUTES,
url_schemes=_ALLOWED_URL_SCHEMES,
link_rel="noopener noreferrer nofollow",
)
def plain_length(html):
"""Longitud del texto plano (sin etiquetas), para validar longitud mínima."""
if not html:
return 0
text = nh3.clean(html, tags=set(), attributes={})
return len(text.strip())