Mais 123% CVE comunicadas por dia em 2026 do que no ano passado.

Dependências

O que é uma dependência transitiva?

Uma dependência transitiva é um pacote que o seu software usa porque outro pacote precisa dele. Nunca o declarou, mas está naquilo que distribui, com as suas próprias vulnerabilidades e licença. Veja como as visualizar, corrigir e provar quais entraram em cada versão.

Atualizado a

A vulnerable transitive dependency and its fix path An app declares two direct dependencies, express and hbs. Express brings in body-parser and send, which bring in raw-body and mime-types. Hbs brings in handlebars, which is vulnerable. The path from the app through hbs to handlebars is highlighted: upgrade hbs, or override the handlebars version. your project direct transitive, depth 1 depth 2 my-app express hbs body-parser send handlebars vulnerable, required by hbs raw-body mime-types Fix path: upgrade hbs, or override the handlebars version

Dependências diretas e transitivas

Uma dependência direta é aquela que declara no seu manifesto. Uma dependência transitiva é aquela que as suas dependências declaram, e as dependências destas, até onde a árvore chegar. O gestor de pacotes resolve toda a árvore ao instalar e normalmente regista o resultado num lockfile.

Tome como exemplo uma aplicação que declara um único pacote, express. Executar npm ls --all mostra o que essa única linha traz consigo:

my-app
└─┬ express            ← direta: declarou-a
  ├─┬ body-parser      ← transitiva, profundidade 1
  │ └── raw-body       ← transitiva, profundidade 2
  ├── cookie           ← transitiva, profundidade 1
  └─┬ send             ← transitiva, profundidade 1
    └── mime-types     ← transitiva, profundidade 2

A árvore real é mais longa. A maioria dos pacotes de uma aplicação típica chega desta forma, o que significa que a maior parte do código que distribui foi escolhida pelo gestor de pacotes, não por si.

Dependência direta Dependência transitiva
Quem a escolheu A sua equipa, no manifesto O gestor de pacotes, a partir dos manifestos das suas dependências
Onde está listada Manifesto e lockfile Lockfile, se existir
Quem controla a versão Cabe-lhe a si O intervalo de versões do pacote pai, a menos que o substitua
Como corrigir uma vulnerabilidade Atualizá-la Atualizar o pacote pai ou substituir a versão

Porque são importantes as dependências transitivas

Trazem os mesmos riscos que as dependências diretas, com menos visibilidade e menos controlo.

  • Vulnerabilidades. Quando o Log4Shell, um zero-day no log4j, foi divulgado, a equipa Open Source Insights da Google encontrou 35.863 artefactos do Maven Central que dependiam do código log4j afetado. Cerca de 7.000 dependiam dele diretamente. A maioria trazia-o como dependência transitiva, e muitos não podiam ser corrigidos até que um pacote intermédio lançasse primeiro uma correção.
  • Ataques à cadeia de abastecimento. A backdoor do xz utils (CVE-2024-3094) chegou ao OpenSSH sem que o OpenSSH dependesse do xz. Várias distribuições Linux aplicam um patch ao OpenSSH para suportar a notificação do systemd, e a libsystemd depende da liblzma, a biblioteca fornecida pelo xz.
  • Licenças. Cada pacote transitivo tem a sua própria licença. Uma licença copyleft três níveis abaixo é tão vinculativa como uma que tenha adicionado por si.
  • Alterações incompatíveis. Um pacote pai que aceita um intervalo de versões deixa entrar uma nova versão transitiva na instalação de raiz seguinte, mesmo que nada tenha mudado no seu manifesto.

Como listar as suas dependências transitivas

Todos os principais gestores de pacotes conseguem imprimir a árvore resolvida:

Ecossistema Comando
npmnpm ls --all
pnpmpnpm list --depth Infinity
Python (uv)uv tree
Python (pip)pipdeptree
Gogo mod graph
Rustcargo tree
Mavenmvn dependency:tree
Gradlegradle dependencies

Para saber porque é que um determinado pacote está presente, peça o caminho até ele: npm explain <package>, pnpm why, yarn why ou cargo tree -i <crate>. Para trabalhar com os caminhos em vez de os imprimir, consulte os grafos de dependências.

Como corrigir uma dependência transitiva vulnerável

  1. Atualize o pacote pai. Encontre a dependência direta que traz o pacote e passe para uma versão que exija uma versão corrigida. É a correção mais limpa, porque os responsáveis pela manutenção do pacote pai testaram-na.
  2. Atualize o lockfile. Se o intervalo de versões do pacote pai já permitir a versão corrigida, basta atualizar a entrada do lockfile, por exemplo com cargo update -p <crate> ou um novo npm update.
  3. Substitua a versão. Se o pacote pai não tiver lançado uma correção, force a versão por si: overrides no npm e no pnpm, resolutions no Yarn, um ficheiro de restrições com o pip ou um requisito mais alto em go.mod. Teste-a, porque o pacote pai nunca foi lançado com essa versão.
  4. Substitua ou remova o pacote pai. Se nada disto funcionar, a dependência que traz o pacote pode já não compensar.

Algumas ferramentas de análise de composição de software acrescentam análise de alcançabilidade, que verifica se o seu código pode sequer chamar a função vulnerável, para decidir qual destas correções vem primeiro. Consulte ferramentas de SCA comparadas para ver as opções.

Porque é que o lockfile não conta toda a história

A maioria das ferramentas constrói a sua visão das dependências transitivas a partir do lockfile. Isso funciona quando o lockfile existe, está atualizado e é a única coisa a partir da qual o build instala. Na CI, muitas vezes não é o caso:

  • Sem lockfile. Um requirements.txt com intervalos de versões ou um projeto que nunca fez commit do seu lockfile é resolvido de raiz em cada build, pelo que as versões transitivas podem mudar entre builds.
  • Caches. As caches de CI restauram pacotes de execuções anteriores em vez de os obter, e o que contêm nem sempre corresponde ao que diz o lockfile atual. Consulte o que a cache do GitHub Actions esconde.
  • Ferramentas de build. Os linters, os compiladores, os geradores de código e os passos de configuração trazem as suas próprias árvores de dependências para o build. Não estão no manifesto da sua aplicação, mas são executados com acesso ao seu código-fonte e aos seus segredos.
  • Scripts de instalação e descargas. O script de instalação de um pacote pode obter mais código, e um passo de build pode descarregar diretamente um binário. Nenhum deles deixa uma entrada num lockfile.

O único registo completo de que dependências transitivas entraram numa versão é aquele que é feito enquanto essa versão é compilada. A página SBOM geradas durante o build explica a diferença entre registar um build e analisar os seus ficheiros depois.

Dependências transitivas e o CRA

O Cyber Resilience Act da UE exige que os fabricantes elaborem uma SBOM que abranja, "no mínimo, as dependências de nível superior dos produtos" (Anexo I, parte II, ponto 1). As dependências transitivas não são mencionadas, pelo que o mínimo legal é mais baixo do que muitos guias sugerem. Mas o CRA também exige que identifique e trate as vulnerabilidades dos componentes que o seu produto contém, e um pacote transitivo vulnerável é um deles. Uma SBOM que para no primeiro nível deixa a maior parte da sua árvore de dependências fora desse processo. Consulte requisitos de SBOM do CRA para mais pormenores.

Como o CRACI acompanha as dependências transitivas

O CRACI é um runner de GitHub Actions que regista o build em vez de o analisar. Enquanto cada job é executado, um proxy que reconhece pacotes regista cada pacote que o job obtém, a qualquer profundidade da árvore, incluindo pacotes restaurados de caches de CI. Isso dá uma visão mais completa das dependências em uso do que as ferramentas baseadas em scanners.

  • Cada profundidade, indicada com honestidade. A SBOM de cada job indica um estado de completude, para que saiba quando um registo não conseguiu ver tudo.
  • A árvore, visível. Um grafo de dependências mostra as camadas de dependências da SBOM, em várias vistas.
  • Exportações que as incluem. As exportações em CycloneDX e SPDX incluem as dependências transitivas, cada uma com a sua licença declarada.
  • Que builds foram afetados. Quando surge uma nova vulnerabilidade, o histórico de builds mostra que builds anteriores continham o pacote vulnerável, direto ou transitivo, para que saiba durante que período o distribuiu (consulte somos afetados por uma CVE?). O histórico segue a sua retenção, 180 dias por defeito.
  • Policy gates. Um build que contenha uma CVE específica pode ser bloqueado antes de ser distribuído.

O CRACI não faz análise de alcançabilidade. Se precisar de saber se o seu código chama uma função vulnerável, combine-o com uma ferramenta de SCA que o faça. Consulte geração de SBOM no CRACI ou marque uma demo para registar um dos seus próprios builds.

Dependências transitivas: perguntas frequentes

O que é uma dependência transitiva?

Uma dependência transitiva é um pacote que o seu software usa porque outro pacote precisa dele. Se a sua aplicação depende da biblioteca A e a A depende da biblioteca B, então a B é uma dependência transitiva, ou indireta, da sua aplicação. Nunca a declarou, mas é distribuída com o seu software.

Qual é a diferença entre uma dependência direta e uma dependência transitiva?

Uma dependência direta é aquela que declara no seu manifesto, como package.json, pyproject.toml ou go.mod. Uma dependência transitiva é aquela que as suas dependências diretas declaram, a qualquer profundidade. As dependências diretas são escolhidas por si; as transitivas são escolhidas pelo gestor de pacotes quando resolve a árvore.

Uma dependência transiente é o mesmo que uma dependência transitiva?

Sim, na prática. "Dependência transiente" é um lapso comum por "dependência transitiva". O termo correto é transitiva, da relação transitiva: se A depende de B e B depende de C, então A depende de C.

Como corrijo uma vulnerabilidade numa dependência transitiva?

Atualize primeiro a dependência direta que a traz, se existir uma versão que inclua a correção. Caso contrário, force a versão corrigida com o mecanismo de substituição do seu gestor de pacotes, como npm overrides, Yarn resolutions ou um ficheiro de restrições do pip, e teste se o pacote pai continua a funcionar com ela.

Como listo todas as dependências transitivas?

Todos os gestores de pacotes conseguem imprimir a árvore resolvida: npm ls --all, pnpm list --depth Infinity, uv tree ou pipdeptree, go mod graph, cargo tree, mvn dependency:tree ou gradle dependencies. Para saber o que um build de produção realmente instalou, registe o próprio build.

O Cyber Resilience Act da UE exige dependências transitivas na SBOM?

O regulamento fixa o mínimo em "no mínimo, as dependências de nível superior dos produtos" (Anexo I, parte II, ponto 1). As dependências transitivas não são mencionadas, mas um pacote transitivo vulnerável continua a estar no seu produto, e o CRA exige que trate as vulnerabilidades dos componentes que distribui.

Veja cada dependência que o seu build trouxe

Execute um workflow de GitHub Actions no CRACI e obtenha uma SBOM registada de cada pacote que obteve, a qualquer profundidade da árvore.

Marcar demo