A misteriosa ameaça à cadeia de suprimentos do pacote npm string-width-cjs
3 de outubro de 2024
0 minutos de leituraEsta história começa quando Sébastien Lorber, mantenedor do Docusaurus, projeto de documentação de código aberto baseado em React, nota uma alteração no manifesto do pacote em um pull request. Esta é a alteração proposta para o popular pacote npm cliui:

Mais especificamente, chamou a atenção a alteração nas dependências do npm, que usa uma sintaxe desconhecida:
A maioria dos desenvolvedores esperaria encontrar um intervalo de versões semver no valor de um pacote ou talvez uma URL baseada em Git ou em arquivo. Neste caso, porém, há uma sintaxe especial com o prefixo npm:. O que isso significa?
O que é aliasing de pacotes npm?
O gerenciador de pacotes npm oferece um recurso de alias que permite definir regras personalizadas para a resolução de pacotes. Assim, sempre que o pacote for referenciado — no código ou no arquivo de lock —, ele será resolvido para o nome e a versão especificados pelo alias.
Nesse caso, portanto, com a alteração proposta neste pull request, o pacote string-width-cjs será resolvido para o pacote string-width na versão ^4.2.0. Isso significa que haverá uma entrada para string-width-cjs no diretório node_modules, mas com o conteúdo de string-width@^4.2.0, e um comportamento semelhante no arquivo de lock (package-lock.json).
O aliasing de pacotes é um recurso do gerenciador de pacotes npm que pode ser usado, por exemplo, para oferecer suporte a ESM e CJS.
Dito isso, o aliasing de pacotes pode ser usado de forma mal-intencionada. Em um artigo e alerta de segurança publicado em 2021, Nishant Jain, Snyk Ambassador, demonstrou como o registro oficial npmjs podia ser enganado e fornecer informações incorretas sobre dependências por meio do aliasing de pacotes, como parte de um problema de confusão de dependências e segurança da cadeia de suprimentos.
O pull request era legítimo e não havia risco de ataque à cadeia de suprimentos. No entanto, a preocupação de Sébastien com o nome do pacote levou à descoberta de um possível risco de segurança.
Como encontrar comportamentos suspeitos em arquivos de lock do npm relacionados a módulos maliciosos
Para analisar o pull request, Sébastien usou o lockfile-lint. Essa ferramenta verifica arquivos de lock, como package-lock.json e yarn.lock, em busca de sinais de adulteração, para garantir que pacotes maliciosos não tenham sido inseridos no lugar do pacote npm original.
A execução da ferramenta exibiu os seguintes avisos:
Observação: desenvolvi o lockfile-lint em 2019, depois de publicar um artigo que revelou o risco de segurança relacionado a arquivos de lock: por que arquivos de lock do npm podem ser um ponto cego de segurança para a inserção de módulos maliciosos.
Alerta máximo: pacotes populares com nomes parecidos no npm
Diante dos resultados do lockfile-lint, Sébastien pesquisou esses nomes de pacotes no npm e, para sua surpresa, descobriu que eles realmente existem no registro público do npm:
https://www.npmjs.com/package/string-width-cjs
https://www.npmjs.com/package/strip-ansi-cjs
https://www.npmjs.com/package/wrap-ansi-cjs
Sébastien percebeu que esses nomes de pacotes não só existem no npm, como também apresentam características suspeitas. Os pacotes não estavam associados a um repositório público de código-fonte, não continham código algum quando foram inspecionados e haviam sido publicados anonimamente, sem nenhuma informação pessoal associada.
Ao analisar o pacote npm strip-ansi-cjs, não encontramos um README nem um repositório de código-fonte. Ainda assim, muitos pacotes legítimos e populares apresentam o mesmo comportamento.
Na verdade, esse pacote específico é popular, como mostram seus 529 dependentes (outros pacotes que dependem dele) e 7.274 downloads semanais.

Ao analisar o código de strip-ansi-cjs, vemos que esse pacote contém apenas um arquivo: o manifesto do pacote, package.json.
Então, por que um pacote que não faz nada tem tantos downloads? E por que tantos outros pacotes dependem dele?

Vamos analisar quem publicou esses pacotes npm.
Os três pacotes pertencem a himanshutester002 e foram publicados no ano passado, com números de versão gerados automaticamente. Veja alguns pontos interessantes:
O pacote npm
isaacs-cliuipode ser uma tentativa de typosquatting contra o fork do projetocliuicriado pelo próprio Isaac e o pacote npm legítimo publicado no namespace dele: @isaacs/cliui.O pacote npm
azure-sdk-for-netpode ser uma tentativa de confusão de dependências para atacar pacotes privados com o mesmo nome.O pacote npm
link-deepestá tentando se apropriar de uma funcionalidade popular relacionada a pacotes utilitários como lodash e outros.

Também vale notar que não há informações identificáveis no perfil do usuário himanshutester002 no npmjs.
Como mencionamos, mais de 500 outros pacotes usam o pacote npm strip-ansi-cjs, o que poderia indicar sua popularidade. Vamos analisá-los:

A inclusão na lista pode parecer convincente, mas será que é mesmo?
Por exemplo, nomes como clazz-transformer, react-native-multiply ou talvez gh-monoproject-cli parecem legítimos. Mas será que são?
Veja a página do pacote npm react-native-multiply:

Esse pacote praticamente não tem downloads, e seu autor é um usuário anônimo do npm, sem informações que permitam identificá-lo. A URL do repositório para o qual o pacote redireciona não existe: https://github[.]com/hasandader/react-native-multiply. O perfil do usuário no GitHub também parece muito suspeito e não apresenta atividade relevante.
Embora o pacote npm pareça conter código-fonte, uma análise mais atenta revela que se trata de um exemplo de código gerado para o protótipo de um aplicativo “hello world”.

Também vale questionar: se esse pacote é apenas uma biblioteca de multiplicação, por que ele precisa de 776 dependências para fazer o seguinte?
Alguns brincam que o JavaScript contribui para criar árvores astronômicas de pacotes aninhados por causa do uso excessivo de dependências. Mas um projeto com 776 dependências diretas é grande demais, sem justificativa.
Entre todas essas dependências estão os três pacotes npm suspeitos que deram início a esta história: string-width-cjs, strip-ansi-cjs e wrap-ansi-cjs:

Mencionamos que uma das dependências de strip-ansi-cjs se chama clazz-transformer. Vamos analisá-la:

Vamos explicar o que está acontecendo aqui. O pacote npm clazz-transformer usa intencionalmente um nome diferente do título class-transformer em sua página do README. Além disso, o repositório de código-fonte, https://github[.]com/typestack/class-transformer, não corresponde ao nome do pacote, o que levanta dúvidas sobre sua legitimidade.
O arquivo package.json do repositório associado typstack/class-transformer no GitHub é assim:

O arquivo package.json no GitHub não declara nenhuma dependência. No entanto, ao inspecionar o código-fonte do pacote publicado no npmjs, encontramos as 437 dependências incluídas no pacote clazz-transformer. Mais uma vez, os três pacotes suspeitos *-cjs estão convenientemente entre elas:

Outras considerações sobre os pacotes npm suspeitos
Antes de tirar outras conclusões, é importante mencionar algumas características dos pacotes npm que analisamos:
Os pacotes do React Native parecem ter sido derivados da ferramenta de scaffolding
create-react-native-library. Essa ferramenta também inclui a função de exemplo padrãomultiplyno código-fonte gerado para um novo projeto.Os pacotes têm estruturas de diretórios e arquivos e dependências que podem ter sido derivadas do boilerplate inicial do Next.js 14, como o criado com
npx create-next-app@14.
Nossos colegas da Sonatype já identificaram casos semelhantes de inundações de registros de código aberto com pacotes. Nesses casos, o objetivo final era que os desenvolvedores se recompensassem com tokens Tea, de uma plataforma Web3 que monetiza software de código aberto.
A presença de alguns arquivos tea.yaml nos pacotes mencionados reforça a hipótese de que um dos objetivos dessa campanha seja minerar tokens Tea por meio do uso indevido do Tea.
No início deste ano, em 14 de abril de 2024, um usuário do fórum Tea publicou um comentário que reforça ainda mais a preocupação com o uso indevido do Tea:

Antes de concluir, gostaria de agradecer sinceramente a Sébastien Lorber por sua postura cautelosa como mantenedor e por ajudar a revelar indícios de um possível ataque à cadeia de suprimentos do npm.
O que está acontecendo com string-width-cjs?
Neste momento, estou bastante confiante de que posso continuar investigando os outros pacotes que supostamente dependem de string-width-cjs e encontrar indícios muito duvidosos de que sejam legítimos.
Acredito que todos esses pacotes dependentes e o aumento artificial de downloads tenham um único objetivo: criar uma falsa aparência de legitimidade para os três pacotes *-cjs, para que, no momento certo e diante da vítima adequada, esses pacotes falsos sejam instalados e, em seguida, recebam uma nova versão maliciosa.
Para ajudar você a trabalhar com software de código aberto com mais segurança, recomendo muito adotar práticas de segurança e consultar estes materiais educativos:
Será que descobrimos uma campanha de ataque à cadeia de suprimentos em meio a essas ações maliciosas? Ou tudo se resume a uma busca por dinheiro e, portanto, pode ser atribuído a spam e ao abuso de registros públicos como npm e GitHub para minerar tokens Tea?
Seja qual for o desfecho, fique atento.
Proteja seu código enquanto desenvolve
O Snyk analisa seu código em busca de problemas de qualidade e segurança e recomenda correções direto no seu IDE.
