Una compuerta que nunca corre no es una compuerta
Una revisión del pipeline de CI de Greenhouse destapó el tipo de hallazgos que son individualmente pequeños pero preocupantes en conjunto: una lista de 24 comandos de Tauri duplicados a mano entre el límite IPC de TypeScript y el registro de handlers en Rust, sin nada que detectara desvíos entre ambos. Una prueba de contraste de color con Chromium real que existía en el repositorio pero no corría en ningún lado de CI. Ninguna auditoría de dependencias. Un flujo de release que construía y subía un .dmg sin nunca abrirlo. El ticket de seguimiento era simple: recorrer la lista.
La mayor parte salió como se esperaba. La prueba de contrato IPC son unas pocas líneas de regex comparando dos archivos entre sí. La prueba de humo de release monta el .dmg, revisa la firma de código, lanza la app con una ruta de vault descartable para saltarse el diálogo de onboarding, y confirma que sigue viva cinco segundos después. Algo directo.
La auditoría de dependencias es donde se puso interesante. El ticket nombraba un advisory específico para ignorar, GHSA-wrw7-89jp-8q8g en el crate glib. Lo busqué, lo mapeé a RUSTSEC-2024-0429, escribí la entrada de ignore, corrí cargo deny check advisories para confirmar que funcionaba, y obtuve un resultado limpio con una advertencia: “advisory was not encountered”. El crate que preocupaba al ticket ya ni siquiera está en el grafo de dependencias actual. Pero el chequeo real encontró dieciocho otros advisories que no había ido a buscar. Dos de ellos eran vulnerabilidades reales, no solo advertencias de código desactualizado, bugs de tiempo cuadrático y de asignación sin límite en quick-xml, traídos de forma transitiva mediante el crate plist que usa Tauri para el empaquetado en macOS. Ambos se arreglaron gratis con un cambio de versión solo en Cargo.lock, sin tocar el manifiesto. Los otros dieciséis eran advisories de tipo unmaintained contra los bindings GTK3 de Linux del propio Tauri y una dependencia vieja de Unicode, nada que se pueda arreglar desde este repositorio, así que esos quedaron con ignores documentados individualmente, cada uno con su razón, en vez de una excepción general.
La parte que más se me quedó grabada, sin embargo, fue la compuerta de contraste en sí, exactamente lo que motivaba el ticket: un chequeo que existe pero no corre. Conecté npm run a11y:contrast a CI, lo corrí localmente primero para asegurarme de que iba a pasar, y no pasó. Fallaron dos pruebas. No por inestabilidad, no por el entorno, simplemente estaban mal. Un commit anterior había agregado una llamada a getConfigStages() en el dashboard y en el asistente de onboarding para que los nombres de etapa se pudieran resolver desde la configuración en vivo en vez de adivinarlos a partir de un ID crudo. Las pruebas basadas en jsdom que corren en CI ya tenían mocks para esa llamada. La prueba de contraste con Chromium real, que no corre en ningún lado, no los tenía. Entonces un componente falló al intentar hacer .map() sobre una lista de configuración nula, y el otro deshabilitó en silencio su propio botón “Next” para siempre porque la promesa que esperaba se resolvía en null en vez de un arreglo vacío. Nadie lo notó, porque no había ningún paso de CI para notarlo. Ese es todo el argumento de este ticket resumido en un bug: una compuerta que existe en el repositorio pero no corre en ningún lado es funcionalmente idéntica a no tener compuerta, salvo que además da una falsa sensación de que sí existe.
Un hallazgo menor de regalo en el camino: el mismo archivo de pruebas no tenía configuración headless en su config de Playwright, así que cada corrida local de esa prueba abría una ventana real y visible de Chromium y robaba el foco. Nadie la había corrido suficientes veces seguidas como para notar lo molesto que era, hasta esta semana.
Seis de siete piezas se resolvieron limpiamente solo con investigación y verificación local. La séptima, conectar Chromium al runner de CI, necesitaba un dato que el agente trabajando en esto no podía obtener desde dentro del repositorio: si el runner de Linux autoalojado ya tenía las bibliotecas compartidas a nivel de sistema operativo que necesita un navegador headless. Esa la pude responder directamente, porque soy quien administra el host: la misma máquina ya corre Chromium headless para los chequeos de accesibilidad de otro sitio, así que el arreglo fue simplemente descargar el binario del navegador, sin paquetes nuevos del sistema. Vale la pena recordar que algunas preguntas de verdad necesitan a la persona con acceso SSH, y sale más rápido preguntar que adivinar y enterarse con un CI en rojo.
Lecturas relacionadas
Cuando una app no puede quitar un permiso que ya otorgó
El scope del protocolo de assets de Tauri tiene un allow_directory y ningún revoke, así que cambiar de vault pasó a requerir un reinicio, cambiando el cambio instantáneo por una propiedad de seguridad que realmente se sostiene.
Tres puertas que nunca estuvieron cerradas
Un CSP nulo, un valor de configuración unido sin validar a una ruta del sistema de archivos, y un selector de vault que aceptaría la carpeta de inicio como vault, nada roto todavía, todo sin cerrar.
Agregar Dependabot a Greenhouse (y la trampa del workspace de Rust que salió a la luz)
Escaneo de dependencias sin un nuevo job de CI, PRs de actualización agrupadas, y el detalle del workspace virtual que hace que la entrada de cargo apunte al directorio equivocado.