feat: version 3.0.0 -- Correcciones de auditoria de seguridad, accesibilidad y redundancia
Permisos (superadmin, viewer, busqueda, tareas recurrentes), bootstrap atomico, revocacion de JWT por version de sesion (028), control optimista de edicion (029), logo sin SVG + CSP, ingesta endurecida, correos escapados, borradores, paginacion de proyectos, accesibilidad de dialogos/controles e i18n. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,13 +24,22 @@ Navegador (superadmin/admin/it_manager) --JWT--> /api/devices -> menú "Equipos
|
||||
|
||||
- **Handshake:** el agente primero manda un `contact` (JSON) o `PROLOG` (XML). Se responde sin
|
||||
guardar nada (`{"status":"ok","expiration":"24"}` o `<REPLY><RESPONSE>SEND</RESPONSE>…`).
|
||||
- **Ingesta autenticada por token**, no por JWT: tabla `inventory_tokens` (solo hash sha256). El
|
||||
- **Ingesta autenticada por token**, no por JWT: tabla `inventory_tokens` (hash sha256 para validar
|
||||
y copia cifrada para recuperar el instalador). El
|
||||
agente lo manda como contraseña Basic (`user`/`password` del `agent.cfg`), `Bearer`,
|
||||
`X-Ingest-Token` o `?token=`. Sin token válido → `401`.
|
||||
- Compatibilidad: `?token=` sigue aceptado para agentes instalados; la aplicación censura ese
|
||||
parámetro en sus logs. El proxy inverso debe censurarlo también en sus propios access logs.
|
||||
- **Pendiente de seguridad:** hoy un instalador puede usar el mismo token en varios equipos.
|
||||
Vincular automáticamente token y `deviceid` rompería esos agentes ya instalados. Antes de
|
||||
activarlo se necesita inventariar instalaciones y migrar a un token por equipo; luego añadir
|
||||
la asociación y rechazar inventarios de otro `deviceid`.
|
||||
- **Rutas:** `POST /api/inventory` (propia; pasa por el Custom Location `/api` de NPM sin cambios)
|
||||
y alias `POST /front/inventory.php` (ruta nativa de GLPI). Se montan ANTES de `express.json`
|
||||
porque usan cuerpo crudo.
|
||||
- **Límites:** cuerpo máx. 15 MB (`INVENTORY_MAX_BYTES`), inflado máx. 4× (anti zip-bomb).
|
||||
- Cada IP tiene límite de 120 peticiones/minuto; máximo dos ingestiones simultáneas. El token se
|
||||
valida antes de leer el cuerpo y el parseo se ejecuta en un worker con límite de tiempo.
|
||||
- **Upsert por `deviceid`** (estable), nunca por hostname. Componentes se reemplazan completos en
|
||||
una transacción; el diff (RAM, disco agregado/retirado, software instalado/desinstalado, SO,
|
||||
hostname) queda en `device_changes`.
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
- **Rate limiting** en `POST /api/auth/login`.
|
||||
- Sirviendo la SPA: CSP (helmet) — `script-src 'self'` (sin inline), `style-src 'self' 'unsafe-inline'`,
|
||||
`img-src 'self' data: blob:`, `object-src 'none'`.
|
||||
- La SPA de producción recibe CSP desde `frontend/public/serve.json` (el servidor `serve` copia
|
||||
ese archivo al build); el backend usa Helmet en `/api`. En Vite dev no se aplica el header del
|
||||
servidor `serve`, para permitir el módulo de recarga de React. El logo de empresa solo admite
|
||||
PNG/JPG/WEBP comprobados por firma y se entrega con `nosniff`.
|
||||
|
||||
### 1.6 Frontend / caché
|
||||
- El JWT en `localStorage` es un trade-off aceptado; mantener CSP y sanitización.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Sesión, retorno al contexto y borradores
|
||||
|
||||
Solución compartida para toda la app (frontend). Los proyectos y comentarios también verifican
|
||||
la versión en el backend (migración `029_optimistic_versions.sql`).
|
||||
|
||||
## Piezas
|
||||
|
||||
| Archivo | Qué hace |
|
||||
|---|---|
|
||||
| `utils/jwt.js` | Lee `exp` y `sub` del JWT (sin verificar firma; eso lo hace el backend). |
|
||||
| `context/auth-context.jsx` | Vigila el vencimiento: temporizador al instante exacto, chequeo cada 30 s y al volver a la pestaña/ventana (`visibilitychange`, `focus`, `pageshow`). El navegador congela temporizadores en segundo plano, por eso el chequeo al volver es el que manda. |
|
||||
| `services/api.js` | Nunca envía una petición con token vencido: corta la sesión antes. Un `401` con token también corta. |
|
||||
| `utils/session.js` | Al vencer guarda `{ userId, ruta completa }` (`plc_return_ctx`, 24 h). Tras el login solo se usa si entra **el mismo usuario**; otro usuario la descarta. |
|
||||
| `utils/drafts.js` + `hooks/useDraftForm.js` | Borradores por formulario (ver abajo). |
|
||||
| `components/NavigationGuard.jsx` | Aviso al salir con cambios (`useBlocker`), advertencia nativa en F5/cierre, `DraftMark`, `useGuardedClose`. |
|
||||
| `components/DraftNotice.jsx` | Aviso dentro del formulario: cambios recuperados / conflicto con el servidor. |
|
||||
| `app/App.jsx` | Router de datos (`createBrowserRouter`, mismas rutas): requisito de `useBlocker`. |
|
||||
|
||||
Pestañas y vistas que viven en la URL (para volver exactamente ahí): proyecto `?tab=`, actividad
|
||||
abierta `?activity=&activityTab=`, Organización `?tab=`, Equipos `?tab=&device=&deviceTab=`.
|
||||
|
||||
## Comportamiento
|
||||
|
||||
- **Vencimiento:** se cierra la sesión al instante (aunque no haya peticiones) y aparece el login con
|
||||
el aviso "Tu sesión venció…". Al volver a entrar, el mismo usuario regresa a la misma ruta, pestaña
|
||||
y popup, con la edición reabierta y sus cambios. Salir con "Cerrar sesión" no guarda ruta de retorno.
|
||||
- **Borradores:** clave `plc_draft:v1:<usuario>:<formulario>:<entidad>` en `localStorage`, vigencia 7
|
||||
días. Solo se guardan los campos permitidos de cada formulario (`draftAllowedFields`); el texto
|
||||
libre de notas, comentarios, descripciones y observaciones no se conserva. Sobreviven a cambiar
|
||||
de pestaña/menú, F5 y vencimiento. Se borran al guardar, descartar o cerrar sesión
|
||||
voluntariamente. Solo se restauran si el registro sigue en lo que el backend
|
||||
devuelve para ese usuario (si se borró o perdió acceso, no se abre nada).
|
||||
- **Secretos:** nunca se guardan campos cuyo nombre parezca contraseña/token/PIN, archivos ni
|
||||
campos fuera de la lista permitida. Al leer borradores antiguos se eliminan los campos excluidos.
|
||||
- **Marcas:** campo modificado en rojo con el texto "· modificado"; pestaña, sección plegable,
|
||||
botón "Nuevo …", tarjeta (Notas) y menú lateral con punto rojo + texto para lector de pantalla.
|
||||
- **Salir con cambios** (menú, pestaña, cerrar popup, plegar sección): diálogo *Seguir editando* /
|
||||
*Dejar pendiente* / *Descartar cambios*. Con "Dejar pendiente", al volver se reabre la edición con
|
||||
los valores recuperados. F5 o cerrar la pestaña: advertencia nativa del navegador.
|
||||
- **Integridad:** los borradores no se guardan automáticamente en el backend; Guardar envía los
|
||||
campos del formulario con la versión leída. Si el registro cambió, se conserva el borrador y se
|
||||
pide recargar para revisar la versión actual. En Info de proyecto/actividad, Equipos, SMTP y
|
||||
Empresa además se consulta el servidor justo antes de guardar.
|
||||
|
||||
## Formularios sin recuperación (a propósito)
|
||||
|
||||
| Formulario | Motivo |
|
||||
|---|---|
|
||||
| Contraseñas (todo el módulo, incluido PIN y grupos) | Los datos están protegidos por PIN y cifrados; guardarlos en el navegador saltaría esa protección. |
|
||||
| Cambiar contraseña (menú del usuario) | Solo contiene secretos. |
|
||||
| Login, registro, olvidé/restablecer clave, aceptar invitación, restablecer PIN | Pantallas públicas: no hay usuario autenticado para aislar el borrador y contienen claves/tokens. |
|
||||
| Documentos (subir archivo), logo y nombre de Empresa | Archivos y texto libre no se guardan en el navegador. |
|
||||
| Equipos → Agentes (token de instalación) | Token secreto; generar uno es una acción de un paso. |
|
||||
| Mover actividad, agregar dependencia, compartir nota, compartir Equipos | Acciones de una sola elección, sin captura de datos: se repiten en un clic. |
|
||||
| Clave y texto libre en "Nuevo usuario"; clave SMTP | Solo se recuperan rol/departamento; las claves y el texto libre se vuelven a escribir. |
|
||||
| Apariencia, filtros, selector de tarjetas del Panel | Se aplican al instante; no queda nada pendiente. |
|
||||
|
||||
## Límites conocidos
|
||||
|
||||
- Los borradores son de **este navegador**: no se sincronizan entre equipos.
|
||||
- En un equipo compartido, los borradores de un usuario quedan en el navegador hasta guardarlos,
|
||||
descartarlos, cerrar sesión voluntariamente o que venzan (7 días). Tras vencimiento de sesión
|
||||
se conservan para permitir el retorno al contexto.
|
||||
- Todos los formularios de edición de entidades, empresa y SMTP envían `editVersion`; el backend
|
||||
rechaza con `409` una versión obsoleta. El borrador se conserva y el aviso pide revisar la
|
||||
versión actual. Miembros no edita una entidad mediante formulario: sus acciones aplican cambios
|
||||
puntuales de acceso.
|
||||
- Los popups son modales: con uno abierto no se puede usar el menú lateral; se sale cerrándolo (pasa
|
||||
por el mismo aviso) o con atrás del navegador.
|
||||
Reference in New Issue
Block a user