Skip to main content

Pruebas de seguridad mejoradas para proyectos de Gradle basados en Git mediante archivos de bloqueo

Escrito por

Antonio Gomes

Blog illustrations Gradle

7 de diciembre de 2020

0 minutos de lectura

Durante el último año, hemos trabajado arduamente para mejorar las pruebas de los proyectos de Gradle importados desde repositorios de Git, con el objetivo de hacerlas más confiables, precisas y escalables.

Entendimos que analizar un manifiesto de Gradle en lugar de un archivo de bloqueo de Gradle sería una batalla interminable que siempre perderíamos. Intentar interpretar el modelo de complejidad de Gradle analizando un archivo build.gradle es un enfoque no determinista que depende de muchas suposiciones. No siempre se tendrían en cuenta las configuraciones que fuerzan estrictamente una versión determinada o una versión máxima de una o varias dependencias, ni aquellas cuyas versiones se definen en otros archivos. Además, este enfoque nunca declarará explícitamente qué resolverá y usará Gradle en distintos contextos de configuración, como las fases de compilación, prueba o ejecución. Todo esto puede generar resultados imprecisos o incompletos.

Por eso, distintas herramientas de dependencias recurren al bloqueo de dependencias para ayudar a los desarrolladores a lograr compilaciones reproducibles. Algunos ejemplos son npm (package-lock.json), yarn (yarn.lock), RubyGems (Gemfile.lock) y ahora también Gradle con gradle.lockfile.

Cómo generar un archivo de bloqueo de Gradle

Así se ve un archivo gradle.lockfile típico:

Archivo de bloqueo de dependencias de Gradle que muestra advertencias sobre archivos generados y entradas bloqueadas de las versiones 2.12.1 de Log4j API y Log4j Core.

No contiene una estructura de árbol o de grafo; en cambio, cumple su función al incluir todas las dependencias directas y transitivas que se usan, e indicar sus versiones y las configuraciones a las que pertenecen.

Para generar un archivo de bloqueo en tu proyecto único o monorepositorio (que contiene distintos subproyectos), edita el archivo build.gradle con las siguientes instrucciones:

Editor de build.gradle con tema oscuro que muestra la configuración de bloqueo de dependencias y una tarea para resolver y escribir los bloqueos de dependencias

Como puedes ver, le pedimos a Gradle que active dependencyLocking para todas las configuraciones disponibles, por ejemplo, annotationProcessor, compilación, ejecución, etc.

Una vez que agregamos el código, Gradle podrá entender lo que queremos hacer.

En este ejemplo, tenemos una sola configuración, pero normalmente podemos tener varias y terminar con varios archivos de bloqueo, uno por configuración. Para tener una única fuente de verdad, veamos cómo generar un solo archivo de bloqueo por proyecto.

Cómo generar un solo archivo de bloqueo

Con una vista previa de una función de Gradle 7.0, que ya está disponible para usuarios de Gradle 6.0 o posterior, podemos generar un solo gradle.lockfile por proyecto que contenga todos los distintos estados de bloqueo de las diferentes configuraciones. Este enfoque de un solo archivo de bloqueo será el predeterminado a partir de Gradle 7.0.

Editor de código que muestra settings.gradle con la vista previa de la función ONE_LOCKFILE_PER_PROJECT de Gradle activada.

Una vez definida esta regla, el siguiente paso es ejecutar en la terminal el comando gradle resolveAndLockAll --write-locks, que generará el siguiente archivo de bloqueo:

Visual Studio Code muestra un proyecto de Gradle y el archivo gradle.lockfile con entradas de dependencias, junto con el comando `gradle resolveAndLockAll --write-locks`.

En lo anterior, podemos ver que:

  • cada línea sigue representando una sola dependencia en la notación GAV group:artifact:version.

  • cada dependencia tiene una lista de las configuraciones a las que pertenece.

  • la última línea, empty, contiene la lista de configuraciones disponibles que no tienen dependencias.

Cómo el archivo de bloqueo de Gradle te facilita la vida

El bloqueo de dependencias puede evitar errores inesperados causados por una dependencia transitiva que no puedes controlar cuando usas versiones dinámicas de dependencias (por ejemplo, 1.+ o [1.0,2.0). Este es un caso común que puede ocurrir en cualquier momento sin que te des cuenta.

Aquí tenemos un pequeño archivo build.gradle en el que declaramos una dependencia con una versión dinámica:

Editor de código con tema oscuro que muestra un archivo de compilación de Gradle con Java, Maven Central y una dependencia del procesador de anotaciones Log4j.

Al ejecutar el comando gradle -q dependencies, el resultado no es 2.11 ni 2.14. Como podemos ver en la imagen a continuación, Gradle resolvió las dependencias a la versión 2.13.

Salida de terminal que muestra la resolución de dependencias de Gradle para la configuración annotationProcessor, incluidas las versiones 2.13.3 de Log4j core y API.

Ahora, al verificar las dependencias disponibles en el archivo de bloqueo, podemos confirmar que el resultado del comando coincide con el estado del archivo de bloqueo de la aplicación de nuestro ejemplo.

Captura de pantalla de un archivo de bloqueo de dependencias de Gradle con una advertencia de que las modificaciones manuales pueden interrumpir la compilación, seguida de entradas de dependencias de Log4j.

¡Es hora de ver los resultados!

Aquí tenemos un caso en el que no pudimos analizar el archivo build.gradle, que contenía dependencias con versiones dinámicas. Comparemos los resultados usando un gradle.lockfile.

Panel de proyectos de Snyk que muestra el archivo build.gradle de unparsed-gradle-repo y su pestaña Dependencies

Como podemos ver, los resultados al usar el archivo gradle.lockfile son mucho mejores: pudimos identificar 24 dependencias frente a 0 en el caso anterior, incluidas las directas y transitivas, y reportar un total de 33 problemas.

Vista de dependencias del proyecto build.gradle que muestra las pruebas del archivo de bloqueo de Gradle, así como vulnerabilidades y problemas de licencias entre los paquetes listados

Si quieres profundizar en el bloqueo de dependencias en Gradle, consulta la documentación oficial.

¡Esperamos que te guste esta función de Snyk! ¿Todavía no tienes una cuenta de Snyk? Regístrate gratis.

Empieza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag viendo nuestro taller virtual introductorio a pedido.

Leer más

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

feature insights context
Blog

¿La prevención es, en esencia, un problema ya resuelto?

La prevención en el código generado por agentes está resuelta desde el punto de vista arquitectónico, pero elegir controles que protejan la seguridad sin ralentizar el desarrollo sigue siendo el desafío.

Live Stream

Agentes de remediación, sin misterios: por qué solucionar es mejor que encontrar

Descubre cómo el agente de remediación de Snyk utiliza inteligencia de seguridad, análisis de explotabilidad y validación para convertir las vulnerabilidades en solicitudes de incorporación de cambios listas para fusionarse.