DEV Community

Cover image for NuGet e o que existe por trás das dependências
Bea Tavernaro
Bea Tavernaro

Posted on

NuGet e o que existe por trás das dependências

🇺🇸 Prefer reading in English? Read the English version here.

Nos últimos três dias dos meus 30 dias de .NET, vimos o que acontece quando executamos um dotnet run, exploramos a estrutura de um projeto .NET e aprendemos a utilizar a CLI sem depender do Visual Studio.

Mas tem uma coisa que aparece em praticamente qualquer projeto real e que costuma ir no automático: os pacotes NuGet.

Quando precisamos de alguma funcionalidade, procuramos uma biblioteca que resolva nosso problema, instalamos o pacote e seguimos desenvolvendo. Eu mesma já fiz isso inúmeras vezes sem pensar muito no que estava acontecendo por trás daquela instalação, principalmente quando o pacote é conhecido e utilizado por milhares de outros desenvolvedores. Aliás, cuidado com isso.

Só que instalar um pacote não significa necessariamente adicionar uma única biblioteca ao nosso projeto. Às vezes, estamos trazendo várias outras dependências junto com ele, e nem sempre sabemos quais são ou de onde vieram.

Então, hoje vamos entender melhor como funciona o NuGet, de onde vêm essas dependências e como podemos verificar se os pacotes que utilizamos estão atualizados e seguros.

O que é o NuGet?

O NuGet é o gerenciador de pacotes do ecossistema .NET. Ele permite que desenvolvedores distribuam, compartilhem e reutilizem bibliotecas em diferentes projetos, facilitando o gerenciamento de dependências. Se você já trabalhou com JavaScript ou Python, pode pensar nele como o equivalente ao npm ou ao pip. Esses pacotes podem ser disponibilizados em diferentes fontes, sendo o NuGet.org o repositório público mais conhecido.

Se abrirmos a página do Dapper no NuGet.org, por exemplo, podemos encontrar informações sobre o pacote, suas versões disponíveis, os frameworks com os quais é compatível, suas dependências e os links para o repositório e a documentação. É interessante dar uma olhada nessas informações antes de instalar uma biblioteca, principalmente para verificar se ela atende às necessidades do nosso projeto e conhecer melhor o que estamos adicionando.

Dapper page

No Dia 02, vimos que podemos adicionar o Dapper ao nosso projeto utilizando o terminal. Depois da instalação, o pacote aparece no nosso .csproj através de um PackageReference, que informa ao .NET qual dependência estamos utilizando e qual versão foi declarada para o projeto.

Dapper at csproj

Até aqui parece bastante simples, mas tem uma parte importante desse processo que nem sempre fica visível quando olhamos apenas para o .csproj.

Dependências que trazem outras dependências

Quando instalamos um pacote NuGet, podemos estar adicionando muito mais do que aquela biblioteca que escolhemos. Um pacote também pode depender de outros pacotes para funcionar corretamente, e esses outros pacotes podem ter suas próprias dependências e assim por diante.

É aqui que precisamos entender a diferença entre dependências diretas e transitivas.

Dapper dependencies

Uma dependência direta é aquela que adicionamos explicitamente ao nosso projeto, como fizemos com o Dapper. Já uma dependência transitiva é aquela que chega ao projeto porque outro pacote precisa dela para funcionar.

Podemos visualizar essas dependências utilizando o comando:

dotnet package list --include-transitive
Enter fullscreen mode Exit fullscreen mode

Result

O resultado apresenta os pacotes de nível superior (top-level), que são as dependências diretas, e os pacotes transitivos utilizados pelo projeto. Dependendo das bibliotecas instaladas, essa lista pode ser enorme.

E por que isso importa tanto?

Porque, quando adicionamos uma dependência ao nosso projeto, também estamos confiando, em certa medida, nos pacotes dos quais ela depende. Isso não significa que dependências transitivas sejam ruins, afinal, elas fazem parte do funcionamento normal dos gerenciadores de pacotes, mas precisamos entender que elas também fazem parte da nossa aplicação.

Um exemplo de como isso pode se tornar um problema aconteceu em março de 2026, envolvendo o pacote Axios, bastante utilizado para realizar requisições HTTP em aplicações JavaScript.

Nesse caso, um invasor conseguiu publicar versões comprometidas do Axios no npm. Essas versões incluíam uma nova dependência chamada plain-crypto-js, que continha código malicioso. Assim, quem instalasse uma das versões afetadas poderia acabar trazendo e executando esse código sem ter escolhido instalar diretamente o pacote malicioso.

O caso foi documentado pelo Google Threat Intelligence Group e é um exemplo de ataque à cadeia de suprimentos de software, também conhecido como supply chain attack.

Apesar de ter acontecido no ecossistema npm, o exemplo ajuda a entender um risco que também existe no NuGet e em outros gerenciadores de pacotes.

Não basta conhecer apenas a biblioteca que instalamos, precisamos considerar também as dependências que vêm junto com ela.

dotnet restore — de onde vêm os pacotes?

Sabendo agora que um pacote pode depender de vários outros, surge outra questão: como o .NET encontra todas essas dependências e consegue fazer nosso projeto funcionar? Podemos utilizar o comando dotnet restore .

dotnet restore
Enter fullscreen mode Exit fullscreen mode

Quando executamos esse comando, o .NET analisa as dependências declaradas no projeto, resolve quais versões precisam ser utilizadas e verifica as fontes de pacotes configuradas para encontrar o que for necessário.

O NuGet também utiliza um cache global de pacotes, que normalmente fica armazenado na pasta .nuget/packages do diretório do usuário. Isso significa que, se uma versão de um pacote já foi baixada anteriormente, ela pode ser reutilizada por outros projetos sem precisar ser baixada novamente.

Smart

É por isso que não precisamos enviar todas as bibliotecas utilizadas pela aplicação para o Git. Quando outra pessoa clona nosso repositório, o .NET consegue ler as dependências declaradas e restaurar os pacotes necessários, desde que tenha acesso às fontes onde eles estão disponíveis.

Como saber se nossas dependências estão atualizadas e seguras?

Instalar uma biblioteca e nunca mais olhar para ela pode parecer inofensivo, principalmente quando tudo continua funcionando, mas os pacotes que utilizamos também recebem atualizações, correções de bugs e correções de vulnerabilidades de segurança.

Através da .NET CLI, conseguimos identificar os pacotes que precisam dessas atualizações sem precisar acessar a página NuGet de cada um. O comando dotnet package list --outdated mostra quais pacotes possuem versões mais recentes disponíveis:

Result

Esse comando compara os pacotes utilizados pelo projeto com as versões disponíveis nas fontes configuradas e apresenta aqueles que podem ser atualizados.

Mas existe uma diferença importante entre um pacote estar desatualizado e um pacote estar vulnerável.

Nem toda versão antiga apresenta um problema de segurança e nem toda atualização precisa ser aplicada imediatamente, já que algumas podem introduzir mudanças incompatíveis com o nosso código.

dotnet package list --vulnerable --include-transitive
Enter fullscreen mode Exit fullscreen mode

O comando acima consulta informações de vulnerabilidades disponíveis nas fontes de auditoria e apresenta os pacotes afetados, permitindo que identifiquemos problemas que precisam de atenção.

E aqui vale lembrar do caso do Axios que vimos anteriormente. Essas verificações são importantes, mas não significam que estamos completamente protegidos contra qualquer pacote malicioso. Elas dependem de vulnerabilidades que já foram identificadas e registradas, então um pacote recém-comprometido pode ainda não aparecer em nenhuma dessas consultas.

Por isso, manter nossas dependências seguras não significa simplesmente atualizar tudo para a versão mais recente ou executar um comando de vez em quando.
Precisamos avaliar quais bibliotecas estamos utilizando, acompanhar atualizações importantes e entender o impacto que essas dependências podem ter na aplicação.

Concluindo

Na correria do desenvolvimento, é muito fácil instalar um pacote para resolver algum problema e nunca mais pensar nele. Se a biblioteca funciona, o projeto compila e a funcionalidade está pronta, normalmente seguimos para a próxima task sem olhar muito para o que foi adicionado.

Mas hoje vimos que existe bastante coisa acontecendo por trás de uma simples instalação. O NuGet precisa resolver dependências, encontrar os pacotes necessários, restaurar seus arquivos e garantir que tudo esteja disponível para o processo de build. E, como já diria o Tio Ben: "Com grandes poderes vêm grandes responsabilidades". Essa facilidade de contar com código pronto vem com a responsabilidade de acompanhar as bibliotecas que utilizamos.

Não precisamos desconfiar de cada pacote que instalamos, mas entender de onde vêm nossas dependências e como podemos verificar sua situação já é um passo importante para manter nossos projetos mais seguros e fáceis de manter.

Ah, vale a pena dar uma olhada na página de estatísticas do NuGet!

Até amanhã!

Referências

Top comments (0)