Skip to main content

Como lidar com a nova árvore de dependências do npm@3

Escrito por
Headshot of Remy Sharp

Remy Sharp

25 de fevereiro de 2016

0 minutos de leitura

Até pouco tempo atrás, a ferramenta de linha de comando da Snyk só oferecia suporte ao npm@2. Isso mudou quando lançamos o snyk@1.9.0, com suporte completo às novas estruturas de diretórios do npm@3.

Queremos compartilhar alguns dos desafios técnicos envolvidos e as novas ferramentas que surgiram desse processo.

O que mudou no npm@3

No npm@2, as dependências do Node eram instaladas no diretório node_modules de cada pacote Node. Por exemplo, os diretórios do módulo request ficam assim:

request/node_modules
  ├── aws-sign2
  ├── aws4
  │   └── node_modules
  │       └── lru-cache
  │           └── test
  ├── bl
  │   ├── node_modules
  │   │   └── readable-stream
  │   │       ├── doc
  │   │       │   └── wg-meetings
  │   │       └── node_modules
  │   │           ├── core-util-is
  ...snipped

Como você pode ver no trecho acima, node_modules já aparece várias vezes. Isso não é tão ruim, mas pode gerar muita duplicação. Bibliotecas populares, como lodash, podem aparecer muitas e muitas vezes em um mesmo projeto!

Originalmente, o npm@2 fazia um trabalho para eliminar duplicações, mas o npm@3 achata completamente essa estrutura de diretórios com o objetivo de remover toda a duplicação. Com o npm@3, o mesmo pacote request fica assim:

request/node_modules
  ├── ansi-regex
  ├── ansi-styles
  ├── asn1
  │   └── lib
  ├── assert-plus
  ├── async
  │   └── lib
  ├── aws-sign2
  ├── aws4
  ├── bl
  │   └── test
  ...snipped

Como você pode ver, a estrutura é completamente diferente, mas, o mais importante, isso não afeta em nada a forma como o Node carrega os módulos.

Como isso afeta a Snyk?

Para começar, foi preciso reescrever completamente a lógica da CLI para percorrer os pacotes. Antes, a Snyk percorria o diretório node_modules, iterava por cada subdiretório e montava uma representação em árvore dos pacotes. Relativamente simples.

Só que agora a estrutura de diretórios plana do npm@3 não representa as relações entre os pacotes. Por exemplo, o pacote async na listagem do npm@3 acima é, na verdade, uma dependência do pacote form-data, que, por sua vez, é uma dependência de request. Mas não dá para ver isso na árvore de arquivos.

Por isso, reescrevemos completamente a resolução de pacotes da Snyk e a extraímos para um módulo independente chamado snyk-resolve-deps (código aberto sob a licença Apache 2).

Esse módulo é usado na CLI do snyk, mas também pode ser instalado como uma ferramenta de CLI independente (a instalação com npm install -g snyk-resolve-deps disponibiliza um utilitário chamado snyk-resolve).

O snyk-resolve-deps percorre primeiro toda a estrutura de diretórios para montar a árvore física. Em seguida, essa árvore é passada para a próxima etapa, que cria uma árvore lógica: a estrutura que representa de onde os pacotes podem ser carregados.

Isso significa que há suporte às estruturas de diretórios do npm@2 e do npm@3, que geram uma árvore virtual como esta:

❯ snyk-resolve
  request@2.69.1
  ├── aws-sign2@0.6.0
  ├─┬ aws4@1.2.1
  │ └── lru-cache@2.7.3
  ├─┬ bl@1.0.2
  │ └─┬ readable-stream@2.0.5
  │   ├── core-util-is@1.0.2
  ...snipped

Em resumo, agora a Snyk oferece suporte aos seus projetos instalados com npm@3 tão bem quanto aos instalados com npm@2. Conseguimos encontrar os caminhos corretos para aplicar patches e identificar com precisão todos os caminhos vulneráveis.

Qual é a diferença em relação ao ‘npm ls’?

Se você conhece as ferramentas do npm, provavelmente já ouviu falar de npm ls, útil para visualizar essas árvores.

A grande diferença entre snyk-resolve-deps e npm ls é que nossa ferramenta mostra a árvore lógica completa e todos os caminhos pelos quais um pacote entrou no seu projeto.

Se analisarmos como request é incluído no código do npm (na branch 2.x), veremos que o npm ls conta uma história (do que está disponível no disco):

Saída do terminal de `npm ls request`, mostrando várias versões instaladas do pacote request em uma árvore de dependências.

Já nosso próprio método de resolução conta outra história. Isso não significa que o npm ls esteja errado; queremos saber exatamente o que está carregando request, e nosso snyk-resolve-deps pode nos dar essa resposta:

Saída do terminal do comando `snyk-resolve -f request --dev`, mostrando uma árvore de dependências do npm com várias versões do pacote request incluídas.

Como você pode ver, muito mais pacotes dependem do módulo request do que você imaginava. Se esse pacote tivesse uma vulnerabilidade, agora a Snyk saberia claramente quais são os caminhos vulneráveis.

snyk-resolve-deps

O pacote snyk-resolve-deps já está disponível sob uma licença de código aberto Apache 2. Depois de instalado globalmente, ele fica disponível com o alias snyk-resolve.

O utilitário de CLI oferece diversos filtros e opções, incluindo a visualização --disk (que gera resultados bem parecidos com os de npm ls) e --filter X e --count X, para filtrar e encontrar ocorrências de uma dependência específica. Saiba mais com snyk-resolve --help.


Você pode executar npm install -g snyk hoje mesmo e testar anonimamente qualquer pacote ou repositório do GitHub. Com uma conta gratuita, você também pode começar a monitorar seus projetos em busca de vulnerabilidades.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.