Português (Portugal)
  • pnpm
  • segurança
  • dependências
  • npm
  • yarn

Atualizar dependências transitivas com o PNPM: Corrigir as vulnerabilidades de segurança sem quebrar nada

Corrigir vulnerabilidades de segurança pode ser uma tarefa frustrante, especialmente quando envolve dependências transitivas. Aprende como as atualizar sem afetar as tuas dependências diretas.

Gao
Gao
Founder

Hoje em dia, vulnerabilidades de segurança são uma questão comum no desenvolvimento de software. Felizmente, temos ferramentas como o GitHub Dependabot para nos ajudar a manter nossas dependências atualizadas com detecção automatizada e pull requests.

Dependabot

No entanto, nem sempre funciona como esperado. Como algumas dependências são transitivas, atualizá-las pode ser uma tarefa difícil para essas ferramentas, pois elas não sabem o impacto das mudanças e que decisão tomar diante de conflitos. Precisamos lidar com esses casos manualmente.

Métodos que não funcionam

  • Comandos oficiais como pnpm up irão desorganizar o teu ficheiro pnpm-lock.yaml. Há um problema sobre isso, que ainda está aberto à data deste escrito.
  • Instalar a versão mais recente da dependência direta que contém a dependência transitiva alvo pode não funcionar. Se a dependência direta não atualizou as definições de versão, a dependência transitiva não será atualizada pois já foi resolvida e bloqueada no ficheiro pnpm-lock.yaml.

A solução

O campo overrides é um recurso poderoso do PNPM que te permite substituir algumas resoluções de versão. Vamos usar esse recurso para atualizar dependências transitivas e fazer a mudança o mais mínima possível.

Vamos usar o alerta do Dependabot acima como exemplo. Ele nos diz que o pacote follow-redirects tem uma vulnerabilidade de segurança, e a versão corrigida é 1.15.6. No entanto, a dependência direta gatsby usa axios que depende de [email protected].

Passo 1: Encontrar a dependência transitiva

Existem muitas maneiras de localizar a dependência transitiva, mas eu recomendaria a maneira mais direta: procurar no ficheiro pnpm-lock.yaml.

E o "pnpm why"?

O comando pnpm why <package> é, de facto, útil. No entanto, ele pode te confundir neste caso. Por exemplo, quando eu executo pnpm why follow-redirects, aqui está uma parte da saída:

Na verdade, só há uma resolução para follow-redirects no ficheiro pnpm-lock.yaml. O comando pnpm why pode mostrar-te vários caminhos que dependem da mesma versão do pacote.

Passo 2: Adicionar os overrides

O caso mais fácil é quando só existe uma resolução no ficheiro pnpm-lock.yaml. Podes adicionar o override diretamente ao ficheiro package.json:

Se estiveres em um workspace, deves adicionar o override no ficheiro package.json da raiz do workspace.

Passo 3: Aplicar as mudanças

Executa pnpm install para aplicar as mudanças. Podemos ver que o pacote follow-redirects é atualizado para 1.15.6 no ficheiro pnpm-lock.yaml com mudanças mínimas.

Agora podes remover o campo overrides do ficheiro package.json para mantê-lo limpo. Em seguida, executa pnpm install novamente para validar que as mudanças podem ser aplicadas sem o campo overrides.

Resolução de problemas

Os passos acima são o caso ideal. As coisas podem nem sempre correr conforme o esperado. Aqui estão algumas dicas para a resolução de problemas:

A versão é revertida após remover o campo "overrides"

Isto pode acontecer quando o pacote dependente tem uma versão fixa ou uma gama que não inclui a versão alvo. Verifica o ficheiro package.json do pacote dependente, neste caso, axios, para ver se isso se aplica.

Se sim, precisas manter o campo overrides no ficheiro package.json até o pacote dependente atualizar as definições de versão.

Existem várias resoluções para a dependência transitiva

À medida que o projeto cresce, o ficheiro pnpm-lock.yaml pode flutuar com várias resoluções para o mesmo pacote. Por exemplo, podem existir duas versões principais do mesmo pacote foo:

O relatório de vulnerabilidade mostra que [email protected] tem um problema de segurança que é corrigido em 1.0.1, e [email protected] não é afetado. Não poderíamos simplesmente adicionar um override para foo:

  • Se adicionarmos um override "foo": "^1.0.1", o [email protected] será reduzido para 1.0.1. Isso pode quebrar o projeto, já que o [email protected] pode ter alguns novos recursos que são usados nos pacotes dependentes.
  • Se adicionarmos um override "foo": "^2.0.0", o [email protected] será atualizado para 2.0.0. Isso pode quebrar o projeto, já que o [email protected] pode ter algumas mudanças que não são compatíveis.

Pressupondo que foo siga a Semantic Versioning, podemos adicionar um override assim:

Isso só vai atualizar o [email protected] para 1.0.1 e manter o [email protected] inalterado.

Conclusão

Antes de o PNPM suportar diretamente a atualização de dependências transitivas, o campo overrides é uma boa solução para corrigir vulnerabilidades de segurança sem quebrar nada. Espero que este artigo te ajude a lidar com esses casos de forma mais eficiente. Na Logto, usamos este método para manter nossas dependências atualizadas e seguras.