Cada vez es más común usar dependencias y paquetes externos en los proyectos, ya que esto puede provocar enormes agujeros de seguridad cuando no se gestiona correctamente. A menudo nos cuesta encontrar el equilibrio adecuado entre las actualizaciones periódicas y la estabilidad cuando nos enfrentamos a las dependencias de los paquetes en nuestro entorno. Por un lado, está el enfoque «más reciente y mejor», en el que intentas mantenerte al día con las últimas versiones de la biblioteca. Esto lleva mucho tiempo y, a menudo, interrumpe las compilaciones porque se introducen nuevas funciones y cambian los contratos de interfaz, lo que requiere una actualización en el código base. En un extremo, está el enfoque de «no arreglar lo que no está roto». Las versiones de los paquetes se fijan para siempre y solo se actualizan si algo deja de funcionar. Esto no es deseable desde el punto de vista de la seguridad, ya que los parches de seguridad deben aplicarse rápidamente. ¡Ambos escenarios son problemáticos!
Muchos componentes de código abierto no proporcionan parches de seguridad para las versiones anteriores de los paquetes, sino que combinan los parches de seguridad con las nuevas versiones de funciones. Como resultado, no hay otra opción que actualizar a la última versión del paquete, lo que obliga a ajustar el código para que coincida con cualquier interfaz nueva, en lugar de solo un pequeño parche de seguridad. Mantener las dependencias más recientes mientras tanto tampoco funciona, ya que los paquetes suelen tener cadenas de dependencia profundas. No puede actualizar uno sin actualizarlos todos, lo que requiere que todos los proveedores o desarrolladores que publiquen paquetes publiquen las actualizaciones de forma sincronizada. Administrar toda esta complejidad lleva mucho tiempo y es engorroso, especialmente cuando la cantidad de proyectos y repositorios se disgrega cuando un solo monolito se divide en cientos de microservicios.
Un enfoque que alivia un poco la gestión de las dependencias y ayuda a encontrar un mejor equilibrio entre estabilidad y seguridad es la introducción de las versiones de Canary. La idea es usar la infraestructura de CI/CD para actualizar los paquetes automáticamente y luego ejecutar compilaciones y pruebas de código. Las canalizaciones se pueden programar para que actualicen automáticamente los paquetes antes de la fase de construcción, mediante una de estas dos estrategias: la vanguardia o la seguridad ante todo. La estrategia de vanguardia consiste en actualizar a las versiones más recientes de los paquetes, mientras que el enfoque centrado en la seguridad solo se actualiza a la última versión secundaria o parche/parche. Una estrategia de vanguardia es la más simple de implementar y la más agradable de tener, pero a menudo introduce cambios importantes e incompatibilidades. La implementación de un enfoque que priorice la seguridad requiere que los editores de paquetes utilicen el control de versiones semántico y publiquen correcciones para las versiones anteriores de los paquetes, por ejemplo, utilizando Menor más alto parámetro para NuGet, o fijar las versiones principales con versiones semánticas para npm (usar esta calculadora como ayuda).
Independientemente de la estrategia elegida, utilizamos nuestras pruebas unitarias existentes para validar el comportamiento de los paquetes nuevos. Si todo funciona según lo esperado, confirmamos automáticamente las nuevas versiones del paquete en nuestro dominar sucursal. Si algo se rompe en el camino, a menudo debido a la profundidad de los árboles de dependencia, simplemente volvemos a intentarlo en la próxima ejecución programada. ¡A menudo los problemas se resuelven solos con el tiempo! Si algo sigue sin actualizarse durante períodos prolongados, nuestros desarrolladores pueden investigar el problema y actualizar manualmente el código para que coincida con las nuevas interfaces.
Nuestro flujo de trabajo típico de canalización para canary builds tiene este aspecto:

La eficacia de este método es proporcional a la cobertura de las pruebas del código: las pruebas unitarias, de integración y de extremo a extremo ayudan a cambiar el equilibrio hacia la estabilidad, ya que puede estar seguro de que las nuevas versiones de paquetes no han hecho retroceder el comportamiento de sus aplicaciones.
Puedes ajustar la frecuencia de la formación de canarios para adaptarla a tu apetito por el riesgo. No tiene sentido intentar activar actualizaciones de paquetes cada vez que hay un cambio en una sucursal, por lo que las compilaciones canarias programadas funcionan mejor. Los ejecutamos a diario para asegurarnos de recibir todas las actualizaciones de seguridad de manera oportuna con una mínima intervención manual. Puede ajustarlo para adaptarlo al calendario de lanzamiento de su propio software y a los paquetes de los que depende.
Mantenemos un historial de las actualizaciones de las versiones de los paquetes a través de las confirmaciones y los mensajes de confirmación de git, para poder auditar qué se modificó y cuándo.
Este es un script de ejemplo que actualiza todos los paquetes npm a la última versión. Después de actualizar los paquetes, se inicia una compilación normal con todas las pruebas. Si todas las pruebas se realizan correctamente, las actualizaciones del paquete se consideran exitosas y confirman los cambios:

Nexo
A menudo hay razones válidas para incluir ciertos paquetes en la lista negra de actualizaciones, por lo que se recomienda incorporar esto en su proceso. Por ejemplo, la actualización de un paquete en particular puede bloquear las compilaciones de Canary e impedir que se publiquen otras actualizaciones; o un paquete puede interrumpir una funcionalidad que sus conjuntos de pruebas no detectan; o un paquete puede tener errores conocidos, vulnerabilidades o problemas de licencia.
Confiamos en Nexus para que nos ayude a gestionar esto. Nexus es un repositorio compatible con casi todos los gestores de paquetes disponibles. Si bien las versiones de canary mantienen los paquetes actualizados, Nexus se asegurará de que no se utilicen paquetes no deseados. Hemos configurado Nexus para que actúe como repositorio de nuestros propios paquetes y como proxy para todos los repositorios externos. Además, lo usamos para impedir que el cliente administrador de paquetes del agente de compilación descargue cualquier paquete en particular.
Tomemos como ejemplo ciertas versiones del paquete jQuery File Upload, que se sabe que son vulnerables y no deberían estar ni cerca de nuestro código. Los selectores de contenido de Nexus se pueden usar para restringir el acceso a versiones particulares haciendo coincidir los nombres de los paquetes con el lenguaje CSEL de nexus:

He aquí cómo hacerlo.
Primero, crea un selector de contenido:

A continuación, cree un privilegio para acceder a este selector:

A continuación, aplícalo al rol de la cuenta Nexus del agente de compilación:

Inhabilite el acceso anónimo al repositorio y configure el cliente de administración de paquetes para usar Nexus como repositorio global. En el caso de npm, esto se puede hacer por usuario o por proyecto. Cree un archivo.npmrc en la carpeta principal del usuario (para la configuración «por usuario») o en la carpeta raíz del proyecto, por ejemplo:

Cómo se puede mejorar esto
Hay varios paquetes de terceros disponibles que administran el proceso de control de versiones y actualización, como actualizaciones de npm-check o NuKeeper. Otros paquetes y servicios ayudan a identificar solo los paquetes con vulnerabilidades de seguridad, como nvd-cli o Snyk.io. Actualmente solo realizamos pruebas unitarias con paquetes actualizados. El siguiente paso para nuestra evolución de CI/CD sería crear entornos desechables durante la fase de construcción, lo que nos permitiría realizar pruebas de integración completa o de E2E con compilaciones canarias para tener más confianza en que no hay regresiones de código.


.jpg)



.jpeg)
