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.
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.
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 ficheiropnpm-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 para1.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 para2.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.