Dependências e pacotes
Versionamento semântico, arquivos de trava e o risco de segurança das dependências transitivas.
O código que você não escreveu
Um backend moderno usa bibliotecas para quase tudo: servidor HTTP, acesso ao banco, validação, datas, logs, clientes de outros serviços. Cada uma é uma dependência, e o gerenciador de pacotes cuida de baixá-las e de manter as versões certas.
| Ecossistema | Gerenciador | Arquivo de dependências | Arquivo de trava |
|---|---|---|---|
| Node | npm, pnpm, Yarn | package.json | package-lock.json, pnpm-lock.yaml, yarn.lock |
| Python | pip, Poetry, uv | pyproject.toml, requirements.txt | poetry.lock, uv.lock, requirements.txt com versões e hashes |
| Go | Go modules | go.mod | go.sum |
| Java | Maven, Gradle | pom.xml, build.gradle | Trava do Gradle; no Maven, versões exatas no próprio pom.xml |
| C# | NuGet | .csproj | packages.lock.json, quando ativado |
Dependências diretas e indiretas
As dependências que você declara são as diretas. Cada uma tem as suas próprias dependências, que têm outras, e assim por diante. Essas são as indiretas, ou transitivas.
Medimos com um projeto Node vazio e uma única dependência, o framework Express na versão 4: 1 dependência direta trouxe 68 pacotes para a pasta node_modules.
seu projeto
└── express (direta)
├── body-parser
│ ├── bytes
│ ├── raw-body
│ │ └── ...
├── cookie
├── send
│ └── ...
└── ... mais dezenas de pacotesCada um desses 68 pacotes foi escrito por alguém, roda com as mesmas permissões que o seu código e pode ter defeitos ou vulnerabilidades. Você escolheu um; confia em todos.
Versionamento semântico
A maioria dos ecossistemas segue o versionamento semântico (semver): um número no formato MAIOR.MENOR.CORREÇÃO, com uma promessa para cada parte.
| Mudança | Exemplo | Promessa |
|---|---|---|
| CORREÇÃO | 4.17.0 → 4.17.1 | Só correções; nada muda para quem usa |
| MENOR | 4.17.0 → 4.18.0 | Recursos novos, compatíveis com o que existia |
| MAIOR | 4.17.0 → 5.0.0 | Mudanças que podem quebrar o código de quem usa |
O arquivo de dependências costuma declarar um intervalo de versões aceitas, e não uma versão exata:
| Notação (npm) | Aceita | Exemplo com 4.17.0 |
|---|---|---|
4.17.0 | Só essa versão | 4.17.0 |
~4.17.0 | Correções | 4.17.x |
^4.17.0 | Correções e versões menores | 4.x, a partir de 4.17.0 |
* ou latest | Qualquer uma | Qualquer versão, inclusive a 5 |
O ^ é o padrão do npm install. A promessa do semver é uma intenção do autor do pacote, e não uma garantia: versões menores às vezes quebram coisas, por engano.
O arquivo de trava
Se o arquivo de dependências aceita um intervalo, o que é instalado depende do dia em que se instala. Testamos: um package.json pedindo express: ^4.17.0, instalado sem arquivo de trava, recebeu a versão 4.22.3, cinco versões menores depois da declarada. Outra pessoa, em outro dia, receberia outra.
O arquivo de trava (lockfile) registra a versão exata de cada pacote, direto e indireto, e um hash do conteúdo. Com ele, todas as instalações, na sua máquina, na do colega, no CI e na imagem de produção, recebem exatamente os mesmos pacotes.
| Sem arquivo de trava | Com arquivo de trava |
|---|---|
| Cada instalação pode receber versões diferentes | Todas recebem as mesmas versões |
| "Funciona na minha máquina" | O que passou no CI é o que vai para produção |
| Uma versão nova com defeito entra sem ninguém saber | Versões só mudam quando alguém atualiza a trava, numa mudança revisada |
Os comandos de instalação exata recusam um arquivo de trava que não bate com o arquivo de dependências. No teste, depois de acrescentar uma dependência no package.json sem atualizar a trava, o npm ci parou com erro, em vez de instalar algo diferente do registrado.
Integridade: o pacote é o mesmo?
O arquivo de trava também guarda um hash do conteúdo de cada pacote. Se o pacote baixado for diferente do registrado, por um registro comprometido ou um ataque no caminho, a instalação falha.
Testamos nos dois ecossistemas que tornam isso mais visível:
| Teste | Resultado |
|---|---|
Go, com o hash de uma dependência alterado no go.sum | verifying github.com/google/uuid@v1.6.0: checksum mismatch e SECURITY ERROR |
pip com --require-hashes e um hash errado para o requests | THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE |
Os riscos da cadeia de suprimentos
As dependências são a cadeia de suprimentos do software. Os ataques a ela cresceram muito nos últimos anos, porque comprometer um pacote popular atinge milhares de sistemas de uma vez.
| Risco | Como acontece |
|---|---|
| Vulnerabilidade conhecida | Uma versão antiga tem uma falha já publicada. No teste, o Express 4.17.0 apareceu no npm audit com 7 vulnerabilidades, 4 delas de severidade alta |
| Script de instalação | O pacote executa código no momento da instalação, com acesso a arquivos e variáveis de ambiente |
| Nome parecido (typosquatting) | Um pacote malicioso com nome quase igual ao de um popular, esperando um erro de digitação |
| Mantenedor comprometido | A conta de quem publica é invadida, e uma versão nova traz código malicioso |
| Pacote abandonado | Ninguém corrige mais as falhas; às vezes o nome é assumido por outra pessoa |
| Confusão de dependência | Um pacote público com o mesmo nome de um pacote interno da empresa é instalado no lugar dele |
O segundo risco merece atenção. Criamos um pacote local com um script postinstall. Ao instalá-lo, o script executou na hora e gravou um arquivo com o diretório do usuário e as 40 variáveis de ambiente do processo, onde costumam estar tokens e senhas. Com --ignore-scripts, nada foi executado.
Por que isso importa quando a IA escreve o código
Assistentes sugerem dependências com naturalidade, e às vezes sugerem pacotes que não existem. Quem publica pacotes maliciosos já explora isso, registrando os nomes que os modelos costumam inventar. Outros problemas comuns:
- Uma dependência nova para algo que a biblioteca padrão já faz.
- Versões antigas, do período em que o modelo foi treinado, com vulnerabilidades já corrigidas.
npm installno Dockerfile e no CI, em vez da instalação exata com o arquivo de trava.- O arquivo de trava fora do repositório, ou apagado para "resolver" um conflito.
- Atualizações grandes, de várias versões maiores de uma vez, sem ler o que mudou.
O próximo nível compara as estratégias de versões, atualização e segurança.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Dependências em cinco minutos
Explique em no máximo cinco minutos de leitura como funcionam as dependências e os gerenciadores de pacotes, para alguém que instala pacotes sem pensar no que vem junto. Cubra só o essencial: dependências diretas e indiretas, versionamento semântico, o papel do arquivo de trava e por que instalar um pacote pode ser um risco de segurança. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
Versionamento semântico e arquivos de trava
Quero entender versionamento semântico e arquivos de trava de verdade. Aja como um mentor. Pergunte primeiro qual ecossistema eu uso (npm, pip, Go, Maven, NuGet). Conduza assim, uma etapa por vez: 1. O formato MAIOR.MENOR.CORREÇÃO e a promessa de cada parte. 2. Os intervalos de versão no meu ecossistema (^, ~, versões exatas) e o que cada um aceita. 3. Por que o mesmo arquivo de dependências instala versões diferentes em dias diferentes. 4. O que o arquivo de trava guarda e como ele resolve isso. 5. Os comandos de instalação exata e por que eles falham quando a trava não bate. Em cada etapa, me proponha um exemplo e peça que eu preveja o resultado.
A árvore de dependências
Quero entender o tamanho real das dependências do meu projeto. Pergunte qual ecossistema e qual é o arquivo de dependências do meu projeto. Explique: 1. A diferença entre dependências diretas e indiretas. 2. Como listar a árvore completa no meu ecossistema e contar os pacotes. 3. Como descobrir por que um pacote indireto está ali (qual direta o trouxe). 4. O que acontece quando duas dependências precisam de versões diferentes do mesmo pacote. 5. Por que cada pacote indireto é um risco, mesmo que eu nunca o use diretamente. Depois me ajude a analisar a árvore do meu projeto e apontar dependências que poderiam ser removidas.
Ataques à cadeia de suprimentos
Quero entender os ataques à cadeia de suprimentos de software. Explique cada um com um caso real conhecido, se houver, e como ele funciona: 1. Vulnerabilidades em versões antigas. 2. Scripts de instalação maliciosos. 3. Nomes parecidos com pacotes populares. 4. Contas de mantenedores comprometidas. 5. Confusão de dependência entre pacotes internos e públicos. 6. Pacotes inventados por assistentes de IA e depois registrados por atacantes. Para cada um, diga qual defesa reduz o risco. Depois me descreva três situações e pergunte qual ataque cada uma permite. Corrija as minhas respostas.