FOSFENO/docs/seguridad.md
hacklab f017246f0e Capas independientes, projectM en el mapping y catalogo de la galeria
Mapping
- Los clips salian del reves: mapper.js activaba UNPACK_FLIP_Y_WEBGL, pero el
  modelo va con origen arriba-izquierda y texImage2D ya sube la primera fila
  en t=0, asi que el flip la mandaba al final. Comprobado renderizando el
  mapper real en Chromium headless con una fuente mitad roja / mitad azul.
- projectM se puede usar YA en una capa: los .milk de data/presets-projectm se
  traducen a Butterchurn en el navegador al elegirlos (milkdrop-preset-
  converter). La capa MilkDrop gana 'biblioteca' (butter | projectm) sin
  cambiar su firma, para no recrear el contexto WebGL al cambiar de una a otra.
  Medido: 100/100 presets de una muestra convierten, 84/84 de los que llevan
  shaders warp/comp; ~7 ms por preset.
- Cada capa tiene su pestana arriba y su propia vista, y '+ CAPA' crea una y
  entra en ella: con varias capas, ir y volver a MAPPING no era viable.
- Dos capas MilkDrop sin preset ya no salen identicas (cogian el indice 0):
  cada instancia elige uno al azar y lo escribe en el estado.
- Una capa nueva ya no nace en la fuente "motor", que es el lienzo del motor
  activo y hacia que dos capas ensenaran lo mismo.

Galeria de visuales
- scripts/catalogar-visuales.py: ficha de cada clip (pelicula, personajes,
  duracion) y, sobre todo, si es una silueta de verdad y cuanta figura tiene.
  Distingue silueta de corte crudo por el negro puro del fondo: 0,63-0,90
  frente a 0,04-0,06, sin zona gris.
- El panel lista los clips agrupados por pelicula, con nombre legible y aviso
  de los que casi no tienen figura, en vez del nombre del archivo.
- Soporte de siluetas con canal alfa (.webm VP9): transparencia de verdad, sin
  recorte por luminancia. El shader del mapper ya la respeta.

Documentacion
- docs/visuales.md nuevo; pendiente.md al dia con lo hecho y lo que queda.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 14:56:01 +02:00

4.2 KiB

Seguridad

FOSFENO es un aparato de directo, no un servicio en internet. Está pensado para una red local: tú, tu móvil y el proyector. Esta página cuenta qué asume el diseño, qué pasa si la red no es de fiar, y cómo cerrarlo.

El modelo de amenaza, en una frase

Cualquiera que alcance http://<ip-de-fosfeno>/ manda sobre las visuales. Por defecto no hay contraseña: es lo cómodo para casa, un ensayo o una red tuya, y es como funcionó desde el principio.

Eso está bien en tu salón. En la wifi de un bar o de un festival significa que un desconocido puede:

  • Apagar o reiniciar el equipo en mitad de la sesión.
  • Cambiar de motor, de preset y de mapping.
  • Ejecutar código en el escenario, porque el editor de Hydra/GLSL es precisamente eso, y el navegador del kiosko arranca con permiso automático de cámara y micrófono.
  • Llenarte la tarjeta subiendo archivos a la galería.

No es un fallo escondido: es la consecuencia de no pedir credenciales. La solución está abajo y son dos líneas de configuración.

La clave compartida

En config.json:

"auth": {
  "token": "loquesea-largo-y-tuyo"
}

O sin tocar el archivo, por variable de entorno:

FOSFENO_TOKEN=loquesea-largo-y-tuyo ./fosfeno

Con la clave puesta:

  • El código QR del proyector ya la lleva dentro. Escaneas y entras, igual que siempre. No hay que teclear nada.
  • Quien solo conozca la IP no se conecta: el WebSocket rechaza la conexión, así que no puede ni mirar el estado ni mandar órdenes.
  • Las subidas a la galería también la piden.
  • El escenario la recibe del propio servidor al abrirse. No hay nada que configurar en el navegador.

La contrapartida: si escribes la dirección a mano tendrás que añadir ?k=tu-clave al final. Por eso el QR es el camino cómodo.

Con token vacío (el valor de fábrica) todo funciona exactamente como antes. Actívala cuando pinches fuera de casa.

Lo que ya está cerrado

Estas no dependen de que actives nada:

Path traversal Las rutas de archivos (/panel, /stage, /lib, /data, /pm/variantes) resuelven y comprueban que no salen de su carpeta. Probado con ../, %2e%2e%2f, doble codificación y rutas absolutas
Nombres de archivo subidos Pasan por secure_filename y una lista blanca de extensiones. No se puede escribir fuera de data/videos ni subir un .sh
Inyección de comandos Ningún shell=True, ningún os.system. Todos los procesos se lanzan con lista de argumentos
Rutas de presets de projectM La categoría y el visual vienen del panel, así que se validan contra la carpeta de presets antes de usarse
XSS en el panel Los nombres de clips, capas y favoritos se pintan con textContent, nunca con innerHTML
CORS El WebSocket solo acepta su mismo origen. Antes admitía cualquiera, así que una web abierta en el móvil de alguien de la sala podía mandar órdenes
Tamaño de subida 512 MB por archivo (antes 4 GB, suficiente para llenar una microSD en tres peticiones)
Payloads del WebSocket Los ajustes se filtran por lista blanca de claves y las capas del mapping se sanean antes de guardarse, así que no se puede meter basura en el estado ni en data/mapping.json

Lo que NO deberías hacer

  • No lo expongas a internet. Nada de abrir el puerto 80 en el router ni ponerle un túnel. No está pensado para eso y la clave compartida no es autenticación seria.
  • No dejes el apagado remoto a mano de una red pública sin clave. El instalador da permiso sudo sin contraseña para reboot y poweroff (install.sh), que es lo que hace que el botón del panel funcione.
  • No pongas datos personales en data/videos. Cualquiera que entre al panel puede listarlos y reproducirlos.

Lo que no se sube al repositorio

.gitignore deja fuera lo de cada instalación: .venv/, *.log (el log del servidor lleva IPs), data/mapping.json, data/pm-favoritos.json, data/videos/* y data/presets-projectm/. En el repo solo van código, documentación y los presets de fábrica.

Si encuentras algo

Es un proyecto pequeño de un hacklab: abre una incidencia en el Gitea del proyecto contando qué has visto y cómo reproducirlo.