Guía definitiva para administrar las dependencias de las gemas de Ruby
5 de agosto de 2022
0 minutos de lecturaRuby, al igual que otros lenguajes de programación, cuenta con todo un ecosistema de bibliotecas de código abierto de terceros, conocidas como gems o, a veces, Ruby gems. La comunidad crea estas gems, que están disponibles en RubyGems.org, el registro oficial de bibliotecas de Ruby. Como ocurre en otros ecosistemas de código abierto, los actores de amenazas pueden publicar código malicioso de forma intencional, por ejemplo, con puertas traseras o que recopila credenciales. Por eso, es fundamental prestar atención a cómo administras y auditas tus gems de código abierto de Ruby.
En este artículo, repasaré los conceptos y las herramientas que conforman el ecosistema de dependencias de Ruby y responderé algunas de las preguntas frecuentes de los desarrolladores de Ruby.
El lenguaje de programación Ruby
Quizás hayas oído hablar de Ruby on Rails (RoR), el popular framework de desarrollo web que contribuyó mucho al éxito y la popularidad de Ruby. RoR se basa en Ruby, un lenguaje dinámico, multiplataforma e interpretado. Sus archivos de código fuente se reconocen fácilmente por la extensión .rb.
¿Qué son las gems de Ruby?
Las gems son bibliotecas de código fuente empaquetadas, modulares e independientes, que se pueden reutilizar fácilmente en distintos proyectos.
¿Qué es RubyGems y en qué se diferencia de Bundler?
RubyGems es el registro central de paquetes donde se comparten gems de Ruby de código abierto de terceros y también es el administrador oficial de paquetes de Ruby, conocido como gem cuando se usa desde la interfaz de línea de comandos.

RubyGems es una excelente manera de permitir que los desarrolladores aprovechen un ecosistema de cientos de miles de bibliotecas de código abierto y las usen en sus proyectos.
La CLI gems de RubyGems viene preinstalada con un entorno de Ruby funcional, para que los desarrolladores puedan empezar de inmediato a instalar sus dependencias de código abierto favoritas. Por ejemplo:
Sin embargo, la CLI gems administra cada dependencia por separado: las obtiene de un registro y las instala en el sistema de archivos para que estén disponibles y se puedan importar fácilmente a los proyectos de código Ruby.
El proyecto Bundler, que técnicamente es una gem de Ruby y se instala como tal, facilita la instalación de gems una por una y permite tener un árbol de dependencias determinista y coherente mediante un archivo de manifiesto llamado Gemfile y su archivo de bloqueo correspondiente, Gemfile.lock. Este último fija las dependencias de todo el árbol y garantiza que las instalaciones sigan siendo coherentes con el tiempo.
Como Bundler es una gem, empezar a usarlo es tan fácil como:
Este comando pone la herramienta de línea de comandos bundler a disposición de los desarrolladores.
El Gemfile de Ruby
El archivo llamado Gemfile es un archivo de texto de manifiesto que describe todas las dependencias que debe usar el proyecto, así como otras instrucciones de metadatos que expone la herramienta bundler.
Un Gemfile sencillo sería así:
El Gemfile anterior define la entrada source, que es el repositorio remoto desde el que Bundler descargará todas las gems. Cada línea gem define una biblioteca de código abierto, la versión que se debe obtener y la fuente donde encontrarla.
A continuación, se muestra un ejemplo más complejo de cómo se obtienen las Ruby Gems desde distintas ubicaciones:
La documentación oficial de Bundler en Git describe estas fuentes con más detalle.
¿Qué contiene una gem de Ruby?
Una gem de Ruby es código fuente de Ruby empaquetado y modular que pueden reutilizar otras gems de Ruby, aplicaciones de Ruby o, en general, los usuarios que interactúan con ella desde la línea de comandos, como vimos antes con Bundler.
Las gems de Ruby suelen seguir una convención que incluye el código fuente de Ruby, pruebas, posibles archivos ejecutables disponibles en la ruta de archivos del usuario y, lo más importante, el archivo de especificación de la gem de Ruby, conocido como Gemspec.
Algunos directorios, como test/ y bin/, deberían ser bastante fáciles de entender, ya que contienen el código de prueba de esta dependencia y el archivo ejecutable que se incluirá e instalará con ella. Otros archivos que cabe mencionar son Gemfile y Gemfile.lock, que vimos al presentar Bundler para administrar las gems de Ruby y su archivo de bloqueo, con el fin de garantizar instalaciones deterministas y seguras que se puedan repetir.
El Rakefile es un archivo asociado con el proyecto de código abierto Rake, una herramienta de compilación para proyectos de Ruby similar a make. De ahí que su nombre sea parecido a Makefile. Este es un ejemplo de su contenido:
El Rakefile importa las clases necesarias de la gem Rake y define una tarea test. Cuando se ejecuta el comando rake test en este directorio, se le indica al programa Rake que busque, mediante un patrón glob, todos los archivos de prueba en la carpeta test/ y luego los ejecute. Para obtener más información sobre el formato del Rakefile, consulta la documentación del repositorio del proyecto Rake.
Otro archivo nuevo que quizás hayas notado en el árbol de archivos anterior es el archivo gemspec, que suele llevar el mismo nombre que el paquete, por ejemplo, base64ness.gemspec. El gemspec describe la gem de Ruby y proporciona metadatos sobre esta dependencia. También ayuda al registro RubyGems a hacer un seguimiento de esta información, analizarla y ponerla a disposición de quienes buscan gems de Ruby.
Este es un ejemplo del archivo base64ness.gemspec de la gem de Ruby base64 mencionada anteriormente:
El sitio web de RubyGems ofrece documentación completa sobre la clase Specification mencionada antes, que incluye metadatos adicionales para una gem de Ruby, funciones ampliadas que permiten a los responsables de Ruby definir ejecutables y agrupar extensiones de C en gems de Ruby, las cuales, mediante compilación cruzada, ofrecen capacidades de módulos nativos, entre otras cosas.
Consejo: si te interesa saber qué contiene una gem de Ruby en particular, puedes descomprimirla en un directorio y examinar el código fuente así:
Controlar la instalación de dependencias de gems de Ruby
Las dependencias se pueden agrupar de forma lógica en categorías específicas según su función. Por ejemplo, puedes tener dependencias de gems de Ruby de producción y gems de Ruby de desarrollo, que solo instalarías mientras desarrollas de forma local.
Para diferenciarlas, puedes definir el siguiente manifiesto Gemfile:
Luego puedes instalar dependencias por categoría con Bundler. Por ejemplo, el siguiente comando solo instalaría las dependencias de producción:
¿Por qué necesitamos un Gemfile.lock?
Si solo pudieras definir tus dependencias mediante el manifiesto de paquetes Gemfile de bundler, tendrías que enfrentar las siguientes limitaciones:
Solo se documentan las dependencias directas de gems de Ruby.
Estas dependencias directas de gems de Ruby usan una versión del paquete poco específica y definida de forma imprecisa. Podrían obtener la versión más reciente o solo la más reciente dentro de un rango de versiones semánticas.
Aunque fijes las versiones de estas dependencias directas de gems de Ruby, cada nueva instalación podría resolver una versión inesperada de una gem dependiente.
Esto significa que, sin un archivo de bloqueo que fije todas las dependencias anidadas de las gems de Ruby instaladas para tu proyecto, se instalarían versiones indeterminadas de las gems. Probablemente sea lo último que quieras que ocurra en un entorno automatizado de CI o compilación, o con otros desarrolladores de Ruby que colaboran contigo en el proyecto.
Por eso existe Gemfile.lock:
Revisemos la especificación y el formato de este archivo de bloqueo de dependencias de gems:
La directiva
GEMinicia el bloque que enumera todas las dependencias de gems de Ruby en un árbol anidado, con las dependencias directas y transitivas y sus versiones. Estas aparecen bajo la directivaspecs. La directivaremoteindica a la herramienta bundler desde dónde obtener estas gems de Ruby para instalarlas.La directiva
PLATFORMScontiene una lista abierta de plataformas de destino para las que se requiere compilar gems nativas de Ruby, debido, por ejemplo, a la cadena de compilación cruzada necesaria para las extensiones de Ruby escritas en C.La directiva
RUBY VERSIONes opcional y especifica la versión del entorno de ejecución de Ruby que se usó al crear este archivo de bloqueoGemfile.lock.La directiva
BUNDLED WITHespecifica la versión de Bundler que se usó para crear el archivo de bloqueo. Los desarrolladores que usan versiones anteriores deberían considerar actualizarla para estar al día y mantener resultados de instalación de gems de Ruby coherentes y consistentes.
bundler-audit y por qué necesitamos mejorar la seguridad de las gems de Ruby
Un gran ecosistema conlleva una gran responsabilidad. Así es. Aquí es donde entra en juego el proyecto comunitario bundler-audit. Es una gem de Ruby que analiza las dependencias especificadas en el archivo de bloqueo del proyecto (Gemfile.lock), las compara con una base de datos de gems de Ruby vulnerables e informa si encuentra versiones afectadas por una o más vulnerabilidades.
Cómo empezar a analizar vulnerabilidades de gems de Ruby con bundler-audit
Para empezar a analizar vulnerabilidades con bundler-audit, primero lo instalamos, como cualquier otra gem de Ruby:
Una vez instalado, bundler-audit debe descargar avisos de seguridad de fuentes como ruby-adivsory-db, una base de datos mantenida por la comunidad que obtiene datos de GitHub Advisory y de la base de datos Open Source Vulnerability, mantenida por Google:
Si todo sale bien, el comando bundler-audit debería mostrar algo parecido a lo siguiente para indicar que está todo actualizado:
Después, podemos ejecutar bundle-audit sin argumentos adicionales para buscar vulnerabilidades de seguridad. En mi proyecto local de Ruby on Rails, mostró lo siguiente:
Parece que todo está bien, pero esto puede ser engañoso y exponerte a riesgos innecesarios. De hecho, se trata de un falso negativo, porque sí existen vulnerabilidades de seguridad en algunas de las versiones de dependencias que tengo instaladas.
Con todo el respeto por el trabajo de la comunidad para crear herramientas de seguridad, es probable que no cuente con suficiente personal ni recursos para ofrecer una cobertura adecuada de vulnerabilidades. Aquí es donde entra en juego el escáner de seguridad de Snyk Open Source.
Snyk ofrece una herramienta CLI gratuita, respaldada por una empresa de seguridad que prioriza a los desarrolladores y cuenta con una amplia base de datos de informes de vulnerabilidades en gems de Ruby. Puedes instalarla para analizar tus dependencias.
Cómo proteger las gems de Ruby gratis con Snyk
Probemos Snyk y veamos qué vulnerabilidades de seguridad encuentra en nuestro proyecto de Rails. Si tienes una MacBook Pro M1 como yo, ejecuta lo siguiente:
Esto descargará el binario independiente de Snyk, lo hará ejecutable y lo agregará a tu ruta. Si necesitas otro método de instalación, como brew para macOS o scoop para Windows, consulta la documentación de inicio con la CLI de Snyk.
Una vez instalada la CLI de Snyk, podemos ejecutarla así:
Si es la primera vez que la usas, verás el siguiente mensaje:
A continuación, debes escribir snyk auth y seguir las instrucciones para copiar una URL de autenticación en el navegador. Inicia sesión o regístrate para recibir tu clave de API. Enseguida volverás a la línea de comandos y podrás continuar con el análisis:
Vaya, 2 problemas y 59 rutas vulnerables… no estamos tan seguros como pensábamos. Por ahora, parece que no hay versiones más recientes a las que actualizar estas bibliotecas en el árbol de dependencias anidadas. Para asegurarnos de que Snyk supervise continuamente el Gemfile de nuestro proyecto y busque nuevas correcciones de versiones que podamos aplicar, podemos hacer lo siguiente:
Ve a https://apps.snyk.io y conecta Snyk al repositorio de código fuente en GitHub o en otro lugar. Esto permitirá que Snyk supervise este proyecto en segundo plano.
Ejecuta el comando
snyk monitor, que tomará una instantánea de los archivosGemfileyGemfile.lockactuales y los supervisará continuamente. Ten en cuenta que debes ejecutarlo desde una compilación automatizada o de CI para que, cada vez que se actualicen esos archivos de manifiesto de paquetes de Ruby, se envíe una nueva instantánea a Snyk para que la supervise y le dé seguimiento.
¿Debo usar bundle-audit o Snyk?
Usa ambos. No se trata de una respuesta diplomática, sino de que realmente se complementan de las siguientes maneras:
Snyk ofrece una amplia base de datos de vulnerabilidades de seguridad en el ecosistema de Ruby, seleccionadas y actualizadas manualmente, a menudo incluso antes que bundle-audit y otras herramientas.
Bundle-audit realiza otras comprobaciones además de analizar vulnerabilidades, como verificar que tus gemas de Ruby no se descarguen de fuentes inseguras, como
http://ygit://, que permitirían un ataque de intermediario o de inyección en el archivo de bloqueo para alterar el código fuente que recibes y ejecutas.
Resumen
Aprendimos a administrar las gemas de Ruby como dependencias de proyectos, por qué es importante usar un archivo de bloqueo y describimos otros aspectos y prácticas recomendadas, como usar dependencias de Ruby de forma segura mediante su análisis con herramientas como Snyk y bundler-audit.
Otros recursos sobre Ruby:
Si también desarrollas en JavaScript, te recomiendo leer qué es package-lock.json y cómo funciona un archivo de bloqueo para los paquetes de Yarn y npm.
Empieza con los desafíos de Capture the Flag
Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.



