Atualize dependências transitivas com PNPM: Corrija as vulnerabilidades de segurança sem quebrar nada
Corrigir vulnerabilidades de segurança pode ser uma tarefa frustrante, especialmente quando envolve dependências transitivas. Aprenda como atualizá-las sem afetar suas dependências diretas.
Atualmente, vulnerabilidades de segurança são um problema comum no desenvolvimento de software. Felizmente, temos ferramentas como GitHub Dependabot para nos ajudar a manter nossas dependências atualizadas com detecção automática 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 o que decidir fazer em caso de conflitos. Precisamos lidar com esses casos manualmente.
Métodos que não funcionam
- Comandos oficiais como
pnpm up
irão bagunçar seu arquivopnpm-lock.yaml
. Há um problema sobre isso que ainda está aberto no momento em que este artigo foi escrito. - Instalar a versão mais recente da dependência direta que possui a dependência transitiva desejada 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 travada no arquivo
pnpm-lock.yaml
.
A solução
O campo overrides
é um recurso poderoso no PNPM que permite substituir algumas resoluções de versão. Vamos usar esse recurso para atualizar dependências transitivas e fazer a mudança da forma mais mínima possível.
Vamos usar o alerta do Dependabot mencionado acima como exemplo. Ele nos informa 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: pesquisar no arquivo pnpm-lock.yaml
.
E quanto ao "pnpm why"?
O comando pnpm why <package>
é realmente útil. No entanto, ele pode confundir você nesse caso. Por exemplo, quando eu executo pnpm why follow-redirects
, aqui está uma parte da saída:
Na verdade, há apenas uma resolução para follow-redirects
no arquivo pnpm-lock.yaml
. O comando pnpm why
pode mostrar vários caminhos que dependem da mesma versão do pacote.
Passo 2: Adicione os overrides
O caso mais fácil é quando existe apenas uma resolução no arquivo pnpm-lock.yaml
. Você pode adicionar a substituição diretamente ao arquivo package.json
:
Se você estiver em um workspace, deve adicionar o override ao arquivo package.json
da raiz do workspace.
Passo 3: Aplique as mudanças
Execute pnpm install
para aplicar as mudanças. Podemos ver que o pacote follow-redirects
foi atualizado para 1.15.6
no arquivo pnpm-lock.yaml
com mudanças mínimas.
Agora você pode remover o campo overrides
do arquivo package.json
para mantê-lo limpo. Em seguida, execute pnpm install
novamente para validar se as mudanças podem ser aplicadas sem o campo overrides
.
Solução de problemas
Os passos acima são o caso ideal. As coisas nem sempre acontecem como o esperado. Aqui estão algumas dicas para a solução de problemas:
A versão é revertida após remover o campo "overrides"
Isso pode acontecer quando o pacote dependente tem uma versão fixa ou um intervalo que não inclui a versão desejada. Verifique o arquivo package.json
do pacote dependente, neste caso, axios
, para ver se isso se aplica.
Se for o caso, você precisará manter o campo overrides
no arquivo package.json
até que o pacote dependente atualize as definições de versão.
Existem múltiplas resoluções para a dependência transitiva
À medida que o projeto cresce, o arquivo pnpm-lock.yaml
pode inflar com várias resoluções para o mesmo pacote. Por exemplo, pode haver 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 na 1.0.1
, e [email protected]
não é afetado. Não podemos simplesmente adicionar um override ao foo
:
- Se adicionarmos um override
"foo": "^1.0.1"
, o[email protected]
será rebaixado para1.0.1
. Isso pode quebrar o projeto, pois 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, pois o[email protected]
pode ter algumas mudanças que causam quebra de compatibilidade.
Assumindo que foo
segue a Versionamento Semântico, podemos adicionar um override assim:
Isso apenas atualizará o [email protected]
para 1.0.1
e manterá o [email protected]
inalterado.
Conclusão
Até que o PNPM suporte a atualização direta de dependências transitivas, o campo overrides
é uma boa solução alternativa para corrigir vulnerabilidades de segurança sem quebrar nada. Espero que este artigo ajude você a lidar com esses casos de maneira mais eficiente. No Logto, usamos esse método para manter nossas dependências atualizadas e seguras.