Alcance y método
Revisión de la versión 9.11.0 en la rama dev (commit 170ee03), escrita desde cero sobre el estado actual del código. Desde la revisión anterior (9.2.3, commit 924aef6) hay 238 commits y 39 hitos cerrados (M5 a M43). Sigue el procedimiento docs/audits/AuditFull.md.
El criterio es el de la lección de la 9.0.0: una norma solo se da por cumplida si una prueba falla al infringirla. Cada afirmación indica su procedencia:
- [test] una prueba automatizada la protege;
- [cmd] un comando reproducible la comprueba hoy, pero nada impide que deje de cumplirse;
- [lectura] revisión del código, sin prueba ni comando;
- [sin prueba] norma escrita que ninguna prueba vigila.
1. Línea base
| Comando | Resultado |
|---|---|
npm run test:all | 702 casos: 623 de node --test y 79 de unittest; todos correctos y 1 omitido (Job Object, solo en Windows) |
npm run test:unit | 180/180 |
npm run test:integration | 273/273 |
npm run test:infrastructure | 28/28 de Node y 79 de Python en 12 archivos (1 omitido) |
npm run test:architecture | 39/39 en 14 archivos |
npm run test:browser | 103/103 en Chromium headless |
npm run build | Sin diferencias en zerochat.py, py/dd-managed-services.py ni los bundles de js/vendor/ |
Tamaño: 64 módulos JavaScript en js/ (27.963 líneas) y 12 herramientas nativas en js/tools/builtin/ (970 líneas); 13 módulos Python en py/ (4.909 líneas); 14 hojas de estilo (8.367 líneas); 834 claves de traducción en cada idioma; 3 bundles de terceros en js/vendor/. [cmd]
La suite tiene menos casos que en la 9.2.3 (864) aunque cubre más funciones: los hitos M24, M32 y M41 retiraron pruebas triviales, duplicadas o de ausencia y dejaron cada comportamiento probado una vez, en el nivel más bajo que lo observa.
2. Normas y pruebas
Cada fila es una norma de AGENTS.md (por sección) o de un README de subsistema, con la prueba que la protege de forma continua.
| Norma | Protección | Estado hoy |
|---|---|---|
| §2 Calidad del código | ||
| Única fuente por decisión y pruebas que importan esa fuente | [test] en los contratos JS–Python: tests/fixtures/mcp/ (estados de servicio, motores, navegadores, nombres públicos de herramientas) la comprueban a la vez las pruebas de Node y de Python; en el resto, [lectura] | Cumple |
Dependencias con resolveDep o la fábrica UMD | [test] test_code_hygiene.js | Cumple |
| Separar dominio, persistencia, adaptadores y presentación | [test] test_persistence_boundary.js y test_state_contract.js; zonas modelo, vista y controlador de los diálogos (M38), [lectura] | Cumple |
| Errores explícitos | [test] test_error_handling.js (sin catch vacíos) y test_backend_hygiene.js (sin capturas genéricas silenciosas en py/) | Cumple |
| Sin código especulativo ni de compatibilidad hacia atrás | [sin prueba] en general; [test] parcial: test_code_hygiene.js detecta funciones que la aplicación no ejecuta | Revisión manual de cada diff |
| Eliminar lo huérfano tras una retirada | [test] test_code_hygiene.js (funciones y variables), test_i18n_literals.js (claves sin uso), test_icon_sprite.js (símbolos y glifos), test_style_hygiene.js (selectores y tokens) | Cumple |
| Sin código de producción que solo usan las pruebas | [test] test_code_hygiene.js | Cumple |
| §3 Seguridad | ||
Datos externos fuera de innerHTML, lista cerrada de usos | [test] test_html_sinks.js (93 usos en 24 archivos) y pruebas de inyección (browser_tool_cards.test.js) | Cumple |
| Comandos, sandbox y datos sensibles con pruebas de límites y autorización | [test] py/test_run_command.py, test_tool_security.js, browser_sandbox_security.test.js, py/test_login.py | Cumple |
Token de sesión obligatorio y escucha en 127.0.0.1 | [test] test_local_server.js, py/test_token.py, py/test_network.py | Cumple |
| §4 Pruebas | ||
| Cada comportamiento se prueba una vez, en el nivel más bajo | [lectura] revisiones de M32 y M41 | Cumple tras la poda de M41 |
Todo tope probado en N y N+1 con su aviso | [test] tope a tope (test_office_parser.js, test_local_server.js con el cuerpo de 1 MiB y el de 4096 bytes sin token, py/test_login.py con los intentos); [sin prueba] que todo tope nuevo la tenga | Cumple en los topes revisados |
| §5 Git y versiones | ||
Versiones solo con npm run bump | [test] test_versioning.js | Cumple |
Promoción a master solo desde dev, con bump y suite en verde | [sin prueba]: es un procedimiento | Cumplido en cada versión desde la 9.2.3 |
| §6 Finalización | ||
| Español e inglés sincronizados en la interfaz y la ayuda | [test] test_i18n.js (mismas claves en los dos idiomas) y test_help_pages.js (traducción, idioma y enlace cruzado de cada página) | Cumple: 834/834 claves |
sitemap.xml regenerado al añadir o retirar páginas | [test] test_help_pages.js | Cumple: 50 páginas |
Backend recompilado tras cambiar py/ | [test] test_backend_hygiene.js (zerochat.py igual a la concatenación de py/) | Cumple |
Memoria de proyecto (.zerochat/) al día | [sin prueba]; task.py la actualiza de forma atómica | Cumple en M42 |
| README de subsistema | ||
Eventos enlazados una vez en mount() y diálogos de ChatDialogs siempre esperados | [test] test_ui_listeners.js y test_code_hygiene.js | Cumple |
Mensajes inyectados al modelo en inglés y texto visible con ChatI18n | [test] test_english_literals.js y test_i18n_literals.js | Cumple |
Colores en tokens.css, sin respaldo en var(), ocultar con hidden | [test] test_style_hygiene.js | Cumple |
Sin emojis; iconos del sprite y de ChatIcons | [test] test_icon_sprite.js | Cumple |
ChatState solo mediante sus mutadores | [test] test_state_contract.js | Cumple |
| Solo las librerías de arranque se cargan con la página y entran en el precache | [test] test_vendor_loading.js | Cumple |
| Orden del prompt de sistema para la caché de contexto | [test] test_context_cache_architecture.js | Cumple |
Proveedores que extienden BaseProviderAdapter | [test] test_providers.js | Cumple |
| Páginas públicas con título y descripción únicos | [test] test_help_pages.js | Cumple |
Backend: zz-main.py último y paridad de definiciones y manejadores de herramientas | [test] test_backend_hygiene.js | Cumple |
| El wheel solo contiene el ejecutable | [test] tests/packaging_smoke_test.py en la release | Cumple en la 9.11.0 |
Recuento: de 29 normas, 23 están protegidas por pruebas, 2 lo están en parte (única fuente por decisión, topes en el borde), 1 se apoya en la lectura de las revisiones de pruebas y 3 no tienen prueba automatizada: la ausencia de código especulativo o de compatibilidad, la promoción a master y la memoria de proyecto. Las dos últimas son procedimientos; la primera solo se vigila revisando los diffs.
3. Seguridad del servidor local
- Token diario obligatorio en cada petición, solo en cabeceras. Caduca al cambiar el día también con el servidor en marcha: el anterior recibe 401 y se genera el nuevo. [test]
test_local_server.js,py/test_token.py - Identificación con la cuenta del sistema: sin token solo se atiende la identificación, con un cuerpo de 4096 bytes como máximo. La contraseña se comprueba con PAM (Linux y macOS) o
LogonUserW(Windows) y nunca se guarda. Tras 5 fallos seguidos, los intentos se rechazan durante 15 minutos sin comprobarlos. [test]py/test_login.py,test_local_server.js - Sin confianza en el proxy inverso: la cabecera con que el proxy enruta no da acceso; detrás de él se exigen igualmente el token y la identificación. [test]
test_local_server.jsybrowser_connection.test.js - API solo por POST JSON-RPC, con orígenes CORS de una lista cerrada; GET solo sirve la interfaz en desarrollo. [test]
test_local_server.js - Firma de cada llamada a herramienta con HMAC, nonce de un solo uso y ventana de 60 s, que tolera ±30 s de desfase de reloj. [test]
test_local_server.js - Cuerpo HTTP acotado a 1 MiB, salvo la exportación de
--prompt --export(64 MB). [test]test_local_server.js - Escucha en bucle local por defecto, con aviso al usar un host que no lo es. [test]
py/test_network.py - Transporte web del servidor con protección anti-SSRF en la URL pedida y en cada redirección, y cuerpo acotado. [test]
py/test_network.py - Comandos acotados: plazo, terminación del grupo de procesos y limpieza de los huérfanos de sesiones anteriores; en Windows, Job Object. [test]
py/test_run_command.py(la prueba del Job Object solo se ejecuta en Windows) - Sandbox de JavaScript en un
iframede origen opaco sin red; la ayuda advierte de que no es una barrera de seguridad. [test]browser_sandbox_security.test.js - Permisos de herramientas: políticas
ask,workspace_trustyallow_all, reglas de directorio y comprobación de traversal. [test]test_tool_security.js
4. Riesgos aceptados
| Riesgo | Situación |
|---|---|
Origen compartido albalday.github.io | Otras páginas del mismo origen podrían leer la cookie de sesión, localStorage e IndexedDB. Aceptado mientras ese origen solo publique páginas propias. |
| Clave del proxy sin cifrar en el navegador | Se guarda en la configuración local (M42-D17): solo abre la puerta del proxy, y el servidor sigue exigiendo el token diario y la identificación. Solo se envía a la URL configurada. |
| Bloqueo de cuentas del sistema | Si el equipo usa un bloqueo por intentos (por ejemplo, pam_faillock) con un límite inferior a 5, los fallos desde la web podrían bloquear la cuenta. La ayuda indica cómo evitarlo. |
--host no local | Expone la ejecución de comandos a la red sin cifrar; el arranque avisa. Para el acceso remoto se documentan el túnel SSH y el proxy inverso con HTTPS. |
| WebLLM desde CDN | El motor se importa de cdn.jsdelivr.net si se utiliza este proveedor. |
| Herramientas web | La búsqueda en DuckDuckGo y la lectura de páginas pasan por el servidor local o, sin él, por Jina Reader. |
| Proveedores remotos | Con una API en la nube los mensajes salen hacia el proveedor configurado; con un LLM local no. |
5. Hallazgos
| Id | Hallazgo | Riesgo y prioridad | Propuesta |
|---|---|---|---|
| F-01 | Las rutas exclusivas de Windows (LogonUserW para la identificación y el Job Object de los comandos) no se ejecutan en ninguna prueba: la CI solo usa ubuntu-latest y la prueba del Job Object se omite. | Estabilidad; media | Un trabajo de CI en windows-latest para las pruebas Python de infraestructura, con un caso de LogonUserW que rechace una contraseña errónea. |
| F-02 | La identificación correcta con PAM no tiene prueba automatizada: las pruebas simulan la comprobación y, a mano, solo se ha probado el rechazo de un usuario inexistente y el uso real del responsable del proyecto. | Seguridad; baja | En CI, un usuario temporal con contraseña conocida que ejecute la comprobación de su propia cuenta. |
| F-03 | js/app.js (1.020 líneas) y js/mcp.js (1.058) siguen creciendo con la coordinación: la sesión con el backend (fragmento de arranque, conexión e identificación) vive en app.js. | Deuda técnica; baja | Llevar la sesión del backend a un módulo de dominio propio cuando cambie de nuevo. |
Resuelto en esta versión: una prueba de arranque del navegador dependía de que no hubiera un zerochat real escuchando en 127.0.0.1:6388; con uno en marcha, su 401 abría el diálogo de identificación. Ahora la prueba corta esa dirección.
6. Dictamen
Infraestructura sólida, con la superficie de red rediseñada sin perder protección. Desde la 9.2.3 el servidor local ha pasado a un transporte solo por POST, con token diario que caduca en marcha, identificación con la cuenta del sistema y acceso remoto por túnel o proxy inverso; cada uno de esos cambios llegó con sus pruebas. La suite es más pequeña y más rápida que en la 9.2.3 y cubre más funciones. Lo pendiente es la cobertura de Windows (F-01), que hoy solo se prueba a mano.
Autoría y entorno de verificación
- Revisión y redacción: Claude Opus 5.5 en Claude Code, a partir de las instrucciones del responsable del proyecto y del procedimiento de
docs/audits/AuditFull.md. - Entorno: Linux x86_64, Node.js 24.21.0, Python 3.14.8, Playwright 1.62.1 con Chromium headless.
- Reproducción: los comandos de la línea base.