14:00 UTC del 21 de octubre. Esa era la hora que el equipo de seguridad de PHP había fijado para una release de emergencia que corregía dos vulnerabilidades distintas, cada una con un 9.2 en CVSS. Ese mismo día, un poco antes, Laravel había publicado 12.68.0 y 13.29.0 para cerrar un agujero en el token guard de la API que permitía a un atacante autenticarse con la cuenta de otra persona en MySQL y MariaDB. Así que, si tenías una API en Laravel sobre un servidor MySQL, tu lista de tareas era una actualización del framework y otra del runtime, ambas críticas, antes de que se te enfriara el café.

Han pasado ocho días. Sinceramente, me encantaría ver la distribución de cuánto tardó cada uno de vosotros en llevar las dos a producción, porque sospecho que se divide en dos grupos casi sin nada en medio. Un grupo hizo merge de un composer update, vio el CI en verde, reconstruyó la imagen con la nueva versión menor de PHP y se fue a casa. El otro grupo sigue en un hilo de tickets preguntando quién es el responsable del entorno de staging.

Lo que diferencia a esos grupos no es la diligencia. Todo el mundo leyó el mismo aviso. No conozco a nadie que se encogiera de hombros ante un bypass de autenticación. Lo que los separa es si el equipo se cree de verdad su suite de tests. Si una ejecución en verde significa «estamos bien», subir una versión menor del framework es cosa de quince minutos. Si significa «probablemente bien, pero acuérdate de cuando se rompieron los cron jobs», esa misma actualización lleva una semana, porque ahora alguien tiene que recorrer la aplicación a mano, y la gente está ocupada, y así es como una vulnerabilidad ya parcheada sigue viva en tu stack días después de que el arreglo sea público.

El mejor contraargumento merece que lo escuchemos con justicia: correr es la forma de romper producción, y un parche que no has probado en staging es una apuesta en sí mismo. Las versiones menores a veces cambian el comportamiento. La gente de operaciones que insiste en dejar reposar los cambios en staging tiene cicatrices que lo justifican, y no voy a fingir que un diff de dos líneas en composer.lock no tenga riesgo. Pero fíjate en para qué sirve realmente ese periodo de reposo. Es un sustituto de la confianza que no construiste en la suite. Staging detecta lo que tus tests no cubren, así que cuanto más lento es tu despliegue, más te está hablando de huecos en la cobertura y no del parche.

Por eso el enlace menos vistoso del resumen de noticias de ese día es el más útil. Rob Allen explicó cómo encontrar tus tests de PHPUnit más lentos haciendo que PHPUnit genere un log JUnit XML con --log-junit, para tener los tiempos de cada test y poder ordenarlos, y apuntó que PHPUnit 13 puede producir salida en formato Open Test Reporting con el mismo fin. Suena a tarea de mantenimiento. En realidad es trabajo de seguridad. Un pipeline que tarda cuarenta minutos se ejecuta menos, se salta cuando hay prisa y nadie encuentra el momento de añadir el test de regresión que habría convertido el día del parche en algo aburrido.

Hay una segunda lección, más discreta, en el propio bug de Laravel, que solo afectaba a instalaciones con MySQL y MariaDB. Si tu CI lo ejecuta todo contra SQLite en memoria porque es rápido, habrías tenido la suite en verde sobre una base de datos que no estaba afectada mientras producción usaba una que sí lo estaba. La velocidad importa, pero tiene que ser velocidad sobre tu stack real. Un MySQL en contenedor dentro del pipeline te cuesta unos segundos; un bypass de autenticación específico de la base de datos que no puedes reproducir en los tests te cuesta muchísimo más.

Nada de esto es una crítica al ecosistema. Los mantenedores de Laravel publicaron arreglos para dos ramas principales muy rápido, el equipo de PHP anunció la ventana de la release con antelación para que pudiéramos organizarnos, y así es como se ve un proyecto maduro. El cuello de botella del 21 de octubre no estaba upstream. Estaba en el espacio entre «existe un arreglo» y «mis servidores lo ejecutan», y ese espacio es responsabilidad nuestra. Los de Go presumen de desplegar un único binario estático; nosotros podemos presumir de un composer update y una actualización de la imagen base, siempre que podamos fiarnos del resultado.

Así que esto es lo que de verdad me gustaría saber, en los comentarios: ¿cuánto tiempo real pasó desde el aviso hasta producción en tu aplicación PHP principal, medido con honestidad el 21 de octubre, y qué cosa concreta de tu pipeline arreglarías primero para reducirlo a la mitad?