CreaRack-SL

Servicio: Sanitizador SVG (allowlist, defusedxml)

Descripción

Módulo: racks/utils/svg_sanitizer.py

Sanitizador de SVG con estrategia allowlist usando defusedxml.ElementTree, integrado en el flujo de importación de stencils. Elimina vectores de XSS en SVG subido por usuario, generado por conversión Visio, o manipulado en requests.

Funciones públicas

sanitize_svg(svg_text: str) → str

Recibe string SVG, devuelve versión limpia. Lanza ValueError si no es SVG válido.

Lógica:

  1. Parse defensivo con defusedxml.ElementTree (bloquea billion-laughs, external entities)
  2. Verifica raíz es <svg>
  3. Limpia atributos peligrosos (recursivo DFS)
  4. Re-serializa con namespaces registrados

Elementos permitidos (21 elementos):

  • Geometría: path, rect, circle, ellipse, line, polyline, polygon
  • Texto: text, tspan, textPath
  • Contenedor: g, defs, symbol, use
  • Gradientes/pattern: linearGradient, radialGradient, stop, pattern
  • Clipping/masking: clipPath, mask
  • Otros: marker, image, switch, title, desc

Atributos bloqueados (eliminados sin excepción):

  • Handlers: on* (onload, onclick, etc.)
  • Valores con javascript: scheme
  • href/xlink:href externo en <use> (solo #fragment permitido)
  • href no-raster en <image> (solo data:image/* permitido)
  • style inline con url(...) no-interno

sanitize_svg_file(path: str) → None

Limpia un fichero SVG in-place. Lanza ValueError si no es SVG válido.

Uso:

try:
    sanitize_svg_file("/path/to/stencil.svg")
except ValueError as e:
    # Fichero no es SVG válido
    logger.error("Invalid SVG: %s", e)

Puntos de aplicación

  1. Subida directa de SVG (POST /api/racks/visio/analyze): SVG de usuario entra crudo → sanitizar antes del preview
  2. SVG generado por libvisio-ng (task convert_visio_async): cada página convertida → sanitizar antes de quedar disponible
  3. Gate final en confirm (POST /api/racks/visio/confirm): second pass, cubre sesiones manipuladas

Defensa en profundidad: si la lógica aguas arriba fallara, confirm atrapa XSS residual.

Detalles de implementación

Localización de nombres de tags/atributos

  • Soporta namespaces SVG (http://www.w3.org/2000/svg) y XLink (http://www.w3.org/1999/xlink)
  • Tags con {namespace}localname se extraen correctamente

Bloqueo de referencias externas

Regex: url\s*\(\s*['\"]?\s*(?!#) — detecta url(http://...), url(/path), etc. pero permite url(#interno)

Recursos embebidos legítimos

  • <image href="data:image/png;base64,..."> — permitido, frecuente en stencils de fabricante (Cisco, APC embeben rasters así)
  • <use href="#local-symbol"> — fragmentos internos OK

Testing

Cubierto en tests/api/test_visio_import_v2.py:

  • Sanitización de SVG con múltiples vectores (script, foreignObject, handlers, javascript:, external URLs)
  • Preservación de geometría y recursos legítimos (paths, text, embedded rasters)
  • Validación de reformat (no-SVG, XML inválido rechazados con ValueError)
  • Bloqueo de <use href="http://...">
  • Preservación de <use href="#interno">

Véase también

  • [[feature—racks—import-visio-v2]]
  • [[entity—racks—service—convert-visio-async]]
  • [[decision—20260713—visio-confirm-hardening]]
  • [[concept—saas—security]]