Dependencias transitivas: el riesgo oculto en la cadena de suministro
Las dependencias transitivas son el código que nunca elegiste, pero que igual es tuyo. Descubre por qué son riesgosas, cómo encontrarlas y cómo corregirlas y monitorearlas.
Las dependencias transitivas son los paquetes indirectos que tu software arrastra a través de sus dependencias directas, y son la mayoría silenciosa de toda base de código moderna. Cuando agregas una biblioteca a tu proyecto, tomas un puñado de decisiones deliberadas. Esas bibliotecas dependen luego de otras bibliotecas, que dependen de aún más, y en pocos niveles el árbol se abre en cientos o incluso miles de paquetes que nunca nombraste, nunca evaluaste y, en muchos casos, de los que nunca oíste hablar. Ese es el riesgo oculto de la cadena de suministro: la mayor parte de lo que entregas es código que no elegiste, y una falla en cualquier punto de ese árbol es una falla que es tuya. Este análisis en profundidad forma parte de nuestra guía más amplia sobre software composition analysis, y se centra específicamente en la capa transitiva, en por qué es tan fácil pasarla por alto y en qué hacer al respecto.
Vale la pena plantear la distinción con claridad. Una direct dependency es la que agregaste deliberadamente, la entrada que puedes señalar en tu manifiesto. Una transitive dependency, a veces llamada indirect dependency o dependencia anidada, es la que se instaló porque algo que agregaste la necesitaba. Elegiste un framework web; él eligió un motor de plantillas; el motor de plantillas eligió una utilidad de parsing de cadenas; y esa utilidad eligió otra cosa más. Cada uno de esos paquetes se ejecuta dentro de tu proceso, con el mismo acceso a la memoria, al entorno y a los secretos que tiene tu propio código. El runtime no distingue entre el código que escribiste, el código que elegiste y el código que simplemente vino de acompañante.
Por qué la mayor parte de tu árbol es transitiva
Sorprende a muchos equipos la primera vez que de verdad lo cuentan. En términos de proporción, las dependencias directas son una pequeña fracción del total; la abrumadora mayoría de los paquetes que se resuelven en una aplicación típica son transitivos. Un proyecto con un par de docenas de dependencias directas puede resolverse fácilmente en muchos cientos o unos pocos miles de paquetes en total una vez que el árbol se expande por completo. Esto no es señal de un proyecto inflado; es simplemente cómo se acumula la reutilización. Cada mantenedor, con razón, construye sobre el trabajo de otros, y ese apalancamiento es justamente lo que hace que el open source sea tan productivo.
La consecuencia para la seguridad es que tu superficie de ataque queda definida mucho más por lo que heredas que por lo que instalas. Puedes ser escrupuloso con el puñado de bibliotecas que agregas, leer su código, verificar a sus mantenedores y aun así terminar ejecutando miles de líneas que nunca has visto. El riesgo oculto no es que las dependencias transitivas sean inherentemente peores que las directas. Es que son invisibles por defecto. No aparecen en tu manifiesto, rara vez aparecen en la revisión de código y, sin herramientas, nunca aparecen en el modelo mental que alguien tiene de la aplicación.
La vulnerabilidad sigue siendo tuya
Aquí viene la parte incómoda. Cuando se publica un CVE contra un paquete cinco niveles más abajo en tu árbol, es tan problema tuyo como una falla en el código que escribiste tú mismo. Si ese paquete hace parsing de entradas no confiables, deserializa datos o gestiona una solicitud de red en una ruta que tu aplicación ejercita, un atacante puede alcanzarlo. El exploit no verifica quién agregó la dependencia.
Lo que hace que las vulnerabilidades transitivas sean singularmente incómodas es que no tienes relación directa con el paquete afectado. No puedes simplemente subir su versión en tu manifiesto, porque no está en tu manifiesto. La versión que estás ejecutando fue elegida por una de tus dependencias directas, o por una de las de estas. En la práctica, estás esperando a que una cadena de mantenedores que nunca conociste actualice cada uno por turno. Por eso las fallas en la cadena de suministro de software figuran hoy entre las categorías más apremiantes de la seguridad de aplicaciones, reflejadas en el OWASP Top 10 2025 bajo software supply chain failures (A03). El riesgo no es hipotético ni raro; es estructural, y crece con cada paquete que heredas.
Cómo los gestores de paquetes resuelven el árbol
Para gestionar el riesgo transitivo tienes que entender, al menos a grandes rasgos, cómo se construye. Todo ecosistema tiene un resolver cuya tarea es tomar tus dependencias declaradas, leer lo que cada una requiere y producir un conjunto concreto e instalable de paquetes en versiones específicas. Los detalles difieren, pero la forma es consistente en las principales cadenas de herramientas: el mundo JavaScript con npm, yarn y pnpm; la JVM con Maven y Gradle; Python con pip y poetry; Go con su sistema de módulos (Go modules); y sus equivalentes en otros lugares.
Las decisiones del resolver importan enormemente para la seguridad. Cuando dos paquetes en tu árbol piden versiones solapadas pero distintas de la misma dependencia, el resolver tiene que elegir, y su elección determina si terminas ejecutando una versión parcheada o una vulnerable. Algunos ecosistemas aplanan el árbol e intentan satisfacer a todos con una única versión compartida; otros permiten que múltiples versiones del mismo paquete coexistan más adentro del árbol. De un modo u otro, la versión que efectivamente aterriza a menudo no es la que ningún paquete individual solicitó explícitamente. Es un resultado negociado, y puede cambiar la próxima vez que instales, a menos que la hayas fijado.
Por qué importan los lockfiles
Eso es exactamente lo que hace un lockfile. Un manifiesto declara lo que quieres, a menudo como un rango: cualquier versión compatible a partir de cierta base. Un lockfile registra lo que de verdad obtuviste: la versión exacta de cada paquete en el árbol completamente resuelto, directo y transitivo, normalmente junto con un hash de integridad para cada uno. El manifiesto es la intención; el lockfile es la realidad.
Sin un lockfile, dos instalaciones del mismo manifiesto en días distintos pueden producir árboles distintos, porque se publican nuevas versiones de paquetes transitivos constantemente y los rangos flotantes las arrastran en silencio. Eso es malo para la reproducibilidad y peor para la seguridad, porque significa que el código que probaste no es necesariamente el código que entregaste. Con un lockfile confirmado en el control de versiones, todos, incluido tu pipeline de CI/CD y tu build de producción, resuelven el mismo árbol cada vez. Los hashes de integridad añaden una segunda garantía: los bytes que instalas son exactamente los bytes que se registraron, de modo que no se puede cambiar un paquete a tus espaldas aunque se manipule una entrada de registro. Los lockfiles son el hábito más importante para controlar el riesgo transitivo, y no cuestan nada más que la disciplina de confirmarlos.
Corregir una vulnerabilidad en una dependencia transitiva
Cuando un scanner marca un paquete transitivo vulnerable, tienes una escalera de opciones, y conviene probarlas en orden.
La corrección más limpia es actualizar el direct parent. Averigua cuál de tus dependencias directas arrastra el paquete vulnerable y comprueba si una versión más nueva de ese parent depende de una versión parcheada. La mayoría de las veces esta es la respuesta correcta: actualizas una línea en tu manifiesto, el resolver hace el resto y la versión vulnerable desaparece de tu árbol. Te mantiene en combinaciones de paquetes soportadas y probadas.
Cuando el parent aún no se ha puesto al día, la siguiente palanca es una versión forzada. Todo ecosistema importante ofrece una manera de decir "dondequiera que aparezca este paquete en mi árbol, usa al menos esta versión", ya se llame override, resolution, dependency management o una directiva replace. Esto sube quirúrgicamente el paquete transitivo sin esperar a que el parent se actualice. Es potente, así que úsalo con criterio: una versión forzada puede, en principio, romper un parent que esperaba la API más antigua, así que combínala con pruebas y trátala como un puente temporal que retiras en cuanto el parent publique su propia corrección.
Si ninguna de estas funciona, escalas. Puedes reemplazar la direct dependency problemática por una alternativa mejor mantenida que no cargue con el problema, lo cual da más trabajo, pero a menudo es la jugada más sana a largo plazo. Como verdadero último recurso, puedes hacer un fork y parchear la dependencia tú mismo, asumiendo que ahora te haces cargo del mantenimiento de ese fork hasta que el upstream se recupere. Hacer un fork es una opción real cuando no hay nada más disponible, pero su costo es continuo, así que resérvala para casos en los que el riesgo de verdad lo justifique.
Los ataques que cabalgan sobre la capa transitiva
Las dependencias transitivas no son solo una fuente pasiva de bugs heredados; son un objetivo activo, porque cuanto más profundo se ubica un paquete, menos escrutinio recibe. Varios patrones de ataque a la cadena de suministro explotan exactamente esto.
Dependency confusion engaña a un resolver para que arrastre un paquete público malicioso en lugar de uno privado interno, publicando un paquete con el mismo nombre y un número de versión más alto en un registro público. Como los resolvers pueden preferir la versión más alta, el código del atacante se cuela como si fuera tuyo. Typosquatting se apoya en un nombre mal escrito o parecido, de modo que un solo carácter equivocado en un manifiesto, o en el manifiesto de una de tus dependencias, sustituye en silencio por código hostil. Los paquetes maliciosos y secuestrados toman el control de proyectos legítimos y ampliamente usados, ya sea por un mantenedor que se corrompe o por un atacante que compromete la cuenta de un mantenedor, y luego publican una actualización envenenada que fluye aguas abajo hacia el árbol de todos como una rutinaria subida de versión transitiva. El protestware es el caso en que un mantenedor sabotea o degrada deliberadamente su propio paquete para dejar un mensaje, y los usuarios heredan el daño.
Lo que une a todos estos es que llegan por el mismo canal de actualización confiable del que dependes para las correcciones legítimas, y la mayoría de las veces aterrizan en la capa transitiva, donde nadie está mirando. Fijar versiones y exigir hashes de integridad es tu defensa más fuerte: si instalas versiones exactas y registradas con hashes verificados, un lanzamiento malicioso sorpresa no puede entrar en silencio en tu build, y obtienes un momento revisable antes de que ingrese cualquier código nuevo. Los rangos flotantes, en cambio, son una invitación abierta a que la próxima versión publicada, contenga lo que contenga, pase a formar parte de tu aplicación automáticamente. Para un tratamiento más completo sobre cómo gobernar todo el patrimonio de open source, consulta nuestra guía sobre gestión de vulnerabilidades en open source.
Ver el grafo completo con SCA y un SBOM
No puedes gestionar lo que no puedes ver, y el rasgo que define al riesgo transitivo es que es invisible sin herramientas. Ese es el trabajo de la software composition analysis. Una herramienta de SCA capaz resuelve tu dependency tree de la misma forma que lo hace tu gestor de paquetes, la recorre hasta su profundidad total y mapea cada paquete, directo y transitivo, con vulnerabilidades conocidas, obligaciones de licencia y señales de mantenimiento. Lo fundamental es que te muestra la ruta: no solo que un paquete vulnerable está presente, sino cuál de tus dependencias directas lo introdujo, que es precisamente la información que necesitas para elegir una corrección.
Ese inventario completo es también lo que captura una software bill of materials. Un SBOM es una lista formal, legible por máquina, de todo lo que hay en tu aplicación, transitivas incluidas, y se está convirtiendo rápidamente en una expectativa básica de clientes, auditores y reguladores. Cuando estalla una vulnerabilidad importante, la diferencia entre los equipos que responden "¿estamos afectados?" en minutos y los que pasan días haciendo grep por las builds es casi siempre si tenían o no un SBOM preciso y el grafo detrás de él.
Por qué el monitoreo continuo es innegociable
Un escaneo de dependencias es una instantánea, y el árbol no se queda quieto. Cada día se divulgan nuevas vulnerabilidades contra paquetes que ya entregas y que no has tocado. Un código que estaba limpio cuando lo publicaste puede volverse vulnerable de la noche a la mañana sin que cambie una sola línea de tu lado, simplemente porque un investigador publicó un hallazgo contra algo en lo profundo de tu árbol.
El monitoreo continuo es lo que convierte eso de una crisis en una rutina. Al mantener una vista en vivo de tu grafo de dependencias resuelto y cotejarlo con los avisos a medida que aparecen, te enteras de una vulnerabilidad transitiva recién divulgada cuando se divulga, no cuando se explota. Esta es la extensión natural de incorporar la seguridad a todo el ciclo de vida, la misma filosofía que describimos en desarrollo seguro de software: desplaza el descubrimiento del riesgo de dependencias hacia la izquierda, dentro del desarrollo y del CI/CD, y sigue vigilando en producción para que nada de lo que surja más tarde pase inadvertido.
Cómo ayuda Rainforest
Rainforest ofrece software composition analysis que trata la capa transitiva como una preocupación de primer orden, y no como una ocurrencia tardía. Resuelve tu grafo de dependencias completo, directo e indirecto, vincula cada paquete marcado con el direct parent que lo introdujo para que la remediación sea obvia y genera un SBOM que puedes compartir con confianza. Como se ejecuta en el CI/CD y monitorea tu árbol resuelto de forma continua, las vulnerabilidades recién divulgadas salen a la luz incluso en código que no has cambiado, y los problemas se detectan en los pull requests antes de que lleguen a producción. Y como es parte de una única plataforma en lugar de un silo aislado, tu riesgo de open source queda junto al resto del panorama de seguridad de tu aplicación, y no en un informe separado que nadie lee. Puedes explorar los detalles en nuestra página de software composition analysis.
El riesgo oculto de las dependencias transitivas no es que sean peligrosas por naturaleza; es que son fáciles de ignorar hasta el momento en que una de ellas aparece en la portada. Fija tus versiones, confirma tus lockfiles, mapea el grafo completo y vigílalo de forma continua, y el árbol que nunca elegiste se convierte en algo que de verdad puedes ver y defender. Si quieres ver tu propio grafo de dependencias, con transitivas y todo, mapeado y monitoreado, agenda una demostración.
Preguntas frecuentes
¿Qué es una dependencia transitiva?
Una transitive dependency, también llamada indirect dependency o dependencia anidada, es un paquete que se instala no porque tú lo hayas agregado, sino porque una de tus dependencias directas lo necesita. Cuando agregas una biblioteca, esta trae sus propias dependencias, que traen las suyas, y así sucesivamente. El resultado es un árbol que se abre varios niveles en profundidad, y los paquetes por debajo del nivel superior son todos transitivos. Se ejecutan con los mismos privilegios que tu propio código, aunque tú nunca los hayas elegido.
¿Por qué las dependencias transitivas son un riesgo de seguridad?
Porque la mayor parte del código de una aplicación típica llega de forma transitiva, la mayor parte de tu riesgo vive en código que nunca seleccionaste ni revisaste. Una vulnerabilidad en lo profundo del árbol sigue siendo tuya, y un atacante puede alcanzarla si tu aplicación ejercita la ruta afectada. La dificultad es que no tienes relación directa con el paquete, así que no puedes simplemente actualizarlo en tu manifiesto y, sin herramientas, estas dependencias son invisibles en la revisión de código y en tu modelo mental de la aplicación.
¿Cómo corrijo una vulnerabilidad en una dependencia transitiva?
Empieza por actualizar el direct parent que arrastró el paquete vulnerable, ya que un parent más nuevo suele depender de una versión parcheada y el resolver hace el resto. Si el parent aún no se ha actualizado, fuerza una versión segura usando el mecanismo de override, resolution, dependency management o replace de tu ecosistema, respaldado por pruebas. Si eso no es viable, reemplaza la direct dependency por una alternativa mejor mantenida y, como último recurso, haz un fork y parchea el paquete tú mismo, asumiendo el costo continuo de mantenimiento.
¿Qué es dependency confusion?
Dependency confusion es un ataque a la cadena de suministro que engaña a un resolver de paquetes para que instale un paquete público malicioso en lugar de uno privado, interno y legítimo. El atacante publica un paquete en un registro público usando el mismo nombre que tu paquete interno, pero con un número de versión más alto. Como los resolvers pueden preferir la versión más alta, el paquete hostil se arrastra hacia tu build como si fuera el real. Poner los paquetes internos en scopes (scoping) y controlar la resolución de registros son defensas clave.
¿Por qué importan los lockfiles para la seguridad?
Un manifiesto declara las versiones que quieres, a menudo como un rango, mientras que un lockfile registra las versiones exactas que de verdad resolviste en todo el árbol, directo y transitivo, normalmente con un hash de integridad para cada una. Sin un lockfile, los rangos flotantes pueden arrastrar paquetes transitivos distintos y más nuevos en cada instalación, de modo que el código que probaste puede no ser el código que entregaste. Un lockfile confirmado hace que las builds sean reproducibles, y los hashes garantizan que los bytes que instalas coincidan con lo que se registró, bloqueando la sustitución silenciosa.

Escrito por
Bruno Baldo
CMO
Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Software Composition Analysis (SCA): La Guía Completa
El Software Composition Analysis (SCA) detecta dependencies open source vulnerables y riesgosas. Aprende cómo funciona, priorización, SBOMs y CI/CD.

Gestión de vulnerabilidades de código abierto en tus dependencias
Guía práctica de gestión de vulnerabilidades open source: halla dependencias vulnerables, prioriza con CVSS, EPSS, KEV y reachability, y remedia CVEs.

SBOM (Software Bill of Materials): Qué Es y Por Qué Necesitas Uno
Un software bill of materials (SBOM) es un inventario completo de los componentes de tu app. Conoce formatos de SBOM, generación, VEX y por qué lo necesitas.
