Skip to main content

O pacote npm faker e o fim inesperado de bibliotecas de código aberto

Escrito por
blog feature snyk open source blue

2 de setembro de 2022

0 minutos de leitura

Para onde vão as dependências de código aberto para morrer, e por que chegam ao fim? O que aconteceu com o módulo npm faker? Isso pode acontecer de novo? Venha descobrir como as bibliotecas de software de código aberto chegam ao auge e como atingem o fim da vida útil. Também vou compartilhar algumas lições para desenvolvedores e engenheiros de operações.

Quero compartilhar algumas histórias dos ecossistemas JavaScript e PHP sobre interrupções repentinas na cadeia de suprimentos de software motivadas por preocupações climáticas e ambientais, além de disputas jurídicas relacionadas à política e a ações humanitárias.

O tema central dessas histórias são os movimentos de protestware no código aberto, que surgiram em resposta a várias questões relacionadas à sustentabilidade do código aberto, às mudanças climáticas e à liberdade do software de código aberto.

Outra história interessante de protestware no npm envolve um mantenedor do pacote npm node-ipc que tomou uma atitude diante de uma crise política.

A biblioteca JavaScript left-pad e o mantenedor do npm Azer Koçulu

Esta é a história de como ações jurídicas de uma empresa contra um desenvolvedor de código aberto acabaram interrompendo milhares de projetos do ecossistema JavaScript, incluindo Node.js e Babel. €‹ €‹

Azer Koçulu, mantenedor do pacote npm left-pad, também mantinha outro módulo npm chamado Kik. Pelo visto, o módulo Kik chamou a atenção de advogados de uma empresa por causa de um aplicativo de mensagens instantâneas com o mesmo nome. Apesar do contato dos advogados e dos pedidos do npm, Azer se recusou a mudar o nome do pacote.

Diante da ameaça de um processo (e como a organização npm não conseguiu chegar a um acordo entre as partes), Azer removeu mais de 250 de seus pacotes npm do registro do npm em protesto.

Infelizmente, um desses pacotes npm era o left-pad. O que é o left-pad? É uma biblioteca minúscula, com 17 linhas de código para preencher strings com espaços, se tomarmos como referência o left-pad@1.0.0. Mas até preencher uma string à esquerda exige lidar com alguns casos extremos. Sua versão mais recente, 1.3.0, foi publicada em 9 de abril de 2018 da seguinte forma:

/* This program is free software. It comes without any warranty, to
     * the extent permitted by applicable law. You can redistribute it     * and/or modify it under the terms of the Do What The Fuck You Want
     * To Public License, Version 2, as published by Sam Hocevar. See
     * http://www.wtfpl.net/ for more details. */'use strict';
module.exports = leftPad;

var cache = [
  '',
  ' ',
  '  ',
  '   ',
  '    ',
  '     ',
  '      ',
  '       ',
  '        ',
  '         '
];

function leftPad (str, len, ch) {
  // convert `str` to a `string`
  str = str + '';
  // `len` is the `pad`'s length now
  len = len - str.length;
  // doesn't need to pad
  if (len <= 0) return str;
  // `ch` defaults to `' '`
  if (!ch && ch !== 0) ch = ' ';
  // convert `ch` to a `string` cuz it could be a number
  ch = ch + '';
  // cache common use cases
  if (ch === ' ' && len < 10) return cache[len] + str;
  // `pad` starts with an empty string
  var pad = '';
  // loop
  while (true) {
    // add `ch` to `pad` if `len` is odd
    if (len & 1) pad += ch;
    // divide `len` by 2, ditch the remainder
    len >>= 1;
    // "double" the `ch` so this operation count grows logarithmically on `len`
    // each time `ch` is "doubled", the `len` would need to be "doubled" too
    // similar to finding a value in binary search tree, hence O(log(n))
    if (len) ch += ch;
    // `len` is 0, exit the loop
    else break;
  }
  // pad `str`!
  return pad + str;
}

O left-pad recebia mais de 2,5 milhões de downloads por mês e era muito popular — sua remoção fez com que outros projetos que dependiam dele parassem de funcionar: Node.js, Babel e outros (saiba mais em: http://left-pad.io/).

O impacto da remoção de dependências na comunidade npm foi algo inédito no registro do npm. Ninguém imaginava que alguém faria isso.

Essa história ficou conhecida como o caso do desenvolvedor que derrubou todo o ecossistema JavaScript com 17 linhas de código.

Como preencher uma string à esquerda em JavaScript?

O protótipo String inclui os métodos padStart() e padEnd(), que recebem dois argumentos: o comprimento em caracteres a ser preenchido e a string usada para preencher. O valor retornado é a nova string preenchida. Essa API está disponível no Node.js 8 e, atualmente, é compatível com todos os navegadores modernos.

Veja a seguir um exemplo de código para preencher uma string à esquerda em JavaScript:

const someString = ‘h’;
console.log(someString.padStart(5, 'h'));

// output will be:
####

Muitos atribuem a inclusão dessa nova especificação ECMAScript ao pacote npm left-pad, por ter iniciado a discussão sobre uma API de linguagem mais completa.

A biblioteca Faker para PHP, Laravel e outros projetos que gera dados fictícios

Você conseguiria abandonar um projeto bem-sucedido se isso significasse reduzir seu impacto ambiental? Esta próxima história é sobre um mantenedor que teve coragem de deixar o próprio sucesso de lado em nome de um planeta mais verde.

Conheça François. Em 2011, François Zaninotto lançou o Faker, uma biblioteca PHP para criar dados fictícios, útil para popular bancos de dados e fazer testes.

Com o tempo, o Faker cresceu e se tornou muito popular, com SDKs em outras linguagens (como Python) e suporte a mais de 70 localidades. Mas o Faker é uma biblioteca monolítica: mesmo que você precise apenas de um conjunto parcial de dados para criar nomes de usuário fictícios nos testes, ainda precisa baixar o conjunto inteiro, que ocupa 3 MB.

No entanto, a maioria das pessoas usa apenas uma localidade. Qual era o tamanho da biblioteca? O Faker ocupava cerca de 3 MB. Para uma biblioteca de ferramentas de desenvolvimento, talvez não seja pouco, mas também não é muito (pense no Chrome para testes de ponta a ponta, por exemplo). Só que, em 2019, o Faker já era tão popular que havia sido baixado mais de 121 milhões de vezes ao longo de sua existência.

Deixe-me perguntar: com que frequência você já se preocupou com o tamanho de uma biblioteca (além das óbvias otimizações de desempenho no front-end)? Ao longo dos anos, o Faker provavelmente emitiu mais de 11 toneladas métricas de CO2 equivalente.

François não queria carregar esse peso na consciência, então o projeto foi descontinuado. Mas François poderia ter passado a responsabilidade para outra pessoa, certo? Ele teve uma experiência ruim com uma biblioteca que havia criado antes: Properl, uma biblioteca ORM para PHP. A experiência não foi boa.

É isso que aconteceu com a biblioteca Faker para PHP. O repositório do projeto no GitHub foi arquivado. Sem novos lançamentos. Sem novas solicitações de pull. Ainda assim, como acontece com projetos de código aberto, é possível criar um fork do código-fonte. Desenvolvedores se ofereceram para transferir o Faker para uma nova organização, com novos mantenedores, mas isso significaria perder a reputação conquistada pelo projeto de código aberto (por exemplo, apagando a contagem de estrelas no GitHub). Por isso, a proposta não deu certo.

François deixou uma mensagem de encerramento para novos mantenedores e desenvolvedores: "Se tiver interesse, você pode começar criando um fork do Faker. Ele será muito melhor que o Faker. Só saiba que provavelmente vai se arrepender daqui a alguns anos ;)".

A biblioteca npm Faker

Esta biblioteca Faker.js também ajuda a gerar enormes quantidades de dados aleatórios que você pode usar nos testes, mas sua história é bem diferente da história da biblioteca PHP acima.

"Chega de trabalho gratuito", escreveu Marak Squires em uma issue do GitHub de um de seus repositórios, em novembro de 2020.

Manter software de código aberto é um enorme fardo. O trabalho vai da programação à manutenção regular do projeto (incluindo a documentação), passando pela publicação de versões, atualização dos arquivos de changelog, criação de um ambiente acolhedor para novos desenvolvedores e colaboradores que queiram participar, resposta a perguntas, investigação de problemas relatados e revisão de solicitações de pull. Essas são apenas algumas das tarefas necessárias para manter um projeto de código aberto saudável.

Muitos de nós, desenvolvedores, assim como empresas com fins lucrativos, usamos bastante software de código aberto. Mas será que contribuímos de alguma forma?

Marak é um desenvolvedor de código aberto e mantenedor de alguns projetos desse tipo. Ele mantém um popular pacote npm de código aberto chamado faker.js, que chegou a receber mais de 12 milhões de downloads por mês. Mas não confunda essa história com a da biblioteca Faker para PHP, mencionada anteriormente. São projetos, mantenedores e histórias diferentes.

O trabalho de Marak ajudava desenvolvedores de empresas da Fortune 500 e, em última análise, contribuía para o crescimento dessas empresas. Mas, como muitos mantenedores, ele não recebia muito em troca. O trabalho de manutenção costuma não ter financiamento nem reconhecimento.

Em novembro de 2020, Marak decidiu fazer uma declaração sobre a sustentabilidade do código aberto — ou a falta dela. O mantenedor abriu uma issue em seu repositório no GitHub dizendo que não faria mais trabalho gratuito.

Issue do GitHub intitulado “Chega de trabalho gratuito para Marak — me pague ou faça um fork deste projeto #1046”, mostrando nos comentários uma cena de protesto de Futurama.

Pelas reações com emojis à declaração de Marak nesta issue do GitHub, a comunidade a aceitou e reconheceu amplamente. Marak foi ouvido, mas suas ações não deixaram um impacto duradouro significativo.

Em 5 de janeiro de 2022, o mantenedor publicou uma nova versão do pacote npm Faker.js com o número de versão semântica 6.6.6, de modo que faker@6.6.6 passou a ser uma versão válida para download no registro do npm.

A nova versão 6.6.6 removeu completamente todos os vestígios do código-fonte original do pacote npm Faker.js. Qualquer nova instalação dessa versão principal deixaria de funcionar corretamente.

Junto com a publicação da nova versão faker@6.6.6, que acompanhava essa declaração, o mantenedor também forçou o envio de um commit ao repositório Faker.js no GitHub, removendo todo o código-fonte:

Página de repositório do GitHub mostrando arquivos do projeto e o título de um README: “O que realmente aconteceu com Aaron Swartz?”
Histórico de commits do GitHub em tema escuro, mostrando a branch master e um commit chamado “endgame”, feito por Marak, com o hash 2c4f82f.

Qual seria o impacto de um pacote npm quebrado para quem o utiliza? Para avaliar isso, é preciso entender como funciona o versionamento dos pacotes npm e como o gerenciador de pacotes npm costuma operar.

Em resumo: se você já usa faker em seus projetos, com ou sem um arquivo de lock para fixar suas dependências, o gerenciador de pacotes npm buscará a versão mais recente de acordo com as resoluções de versão minor ou patch. Ainda assim, você permanecerá na “zona segura” da versão principal atual do Faker.js. Isso porque faker@5.5.3 é a versão principal segura mais recente, enquanto a versão principal quebrada faker@6.6.6 exige uma atualização explícita, que muitas vezes não é disponibilizada automaticamente para o usuário final.

Então, será que esse mantenedor estava fazendo uma declaração, mas ninguém sentiu os efeitos na prática? A história continua: Marak adicionou uma vulnerabilidade de negação de serviço ao pacote npm colors, afetando cerca de 100 milhões de downloads mensais.

Lições para usar bibliotecas de código aberto com responsabilidade

A seguir, veja quatro recomendações práticas para gerenciar software de código aberto e evitar os impactos de segurança causados por pacotes npm de protestware e software sem manutenção.

1. Gerencie com eficiência sua infraestrutura de bibliotecas de código aberto

O que podemos fazer como desenvolvedores que dependem da disponibilidade de bibliotecas de código aberto? Reinstalar pacotes de registros é lento e ineficiente, além de poder gerar resultados não determinísticos.

Para lidar melhor com situações em que pacotes e bibliotecas de código aberto ficam obsoletos ou são removidos de registros públicos como npm, PyPI e outros, recomendamos usar proxies privados locais e de cache, como o projeto de código aberto Verdaccio. Nós entrevistamos Juan Picado, mantenedor do Verdaccio. Leia a entrevista para saber mais sobre como gerenciar suas bibliotecas de código aberto.

2. Avalie bibliotecas de código aberto

Ao adotar novos pacotes de código aberto ou considerar alternativas aos que você já usa, reserve um tempo para avaliar a comunidade do projeto, seu histórico e suas atividades atuais de manutenção, além de sua postura de segurança. Outros aspectos também são importantes, como a qualidade do projeto e a disponibilidade de testes e documentação.

Uma ferramenta que ajuda desenvolvedores a avaliar a qualidade de pacotes npm é o Snyk Advisor, um recurso gratuito que ajuda a quantificar aspectos de um pacote de código aberto. Por exemplo, o Snyk Advisor fornece uma pontuação de integridade para o pacote npm nodemon.

Página do npm para o nodemon v2.0.7, com uma pontuação de integridade do pacote de 90/100, pacotes semelhantes, downloads, frequência de commits e métricas do projeto

3. Use um arquivo de lock para fixar dependências

Usar um arquivo de lock para fixar dependências em versões conhecidas e testadas é uma ótima forma de garantir instalações determinísticas. Isso vale para qualquer ecossistema JavaScript e é uma técnica conhecida para evitar surpresas na CI e em outros contextos.

Se você está começando a usar arquivos de lock e não sabe bem como eles funcionam, recomendo ler O que é um package-lock.json para entender a semântica e os mecanismos do gerenciamento de dependências.

Diagrama que mostra o fluxo de trabalho do npmFaker: o package-lock.json de um projeto npm é usado para instalar dependências, enquanto o yarn.lock é mantido para os colaboradores.

4. Fixe dependências transitivas

Em certas situações, talvez você não consiga controlar a versão das dependências transitivas adicionadas à sua árvore node_modules. Isso acontece porque essas dependências fazem parte de outras dependências declaradas por outros mantenedores.

Dito isso, os gerenciadores de pacotes npm e outros, como yarn ou pnpm, permitem substituir a versão de uma dependência aninhada declarando o intervalo de versões aceitas no arquivo package.json.

Veja um exemplo que foi de importância crucial devido às consequências da sabotagem do módulo peacenotwar e do protestware do pacote npm node-ipc:

"overrides": {
    "node-ipc@>9.2.1 <10": "9.2.1",
    "node-ipc@>10.1.0": "10.1.0
  }

Considerações finais

A segurança de software de código aberto e o ativismo são temas muito importantes para mim, e este artigo se baseia em uma palestra que apresentei na Cloud Native SecurityCon, em Valência, em maio de 2022. Se tiver interesse, você pode assistir à gravação aqui:

The Unexpected Demise of Open Source Libraries - Liran Tal, Synk

Também recomendo muito adotar uma abordagem proativa para proteger as dependências de código aberto: conecte seus repositórios Git ao Snyk para receber correções de segurança automatizadas e atualizações de versão das dependências.

Comece a resolver desafios de capture the flag

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

Leia mais

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

feature insights context
Blog

A prevenção é essencialmente um problema resolvido?

A prevenção em código gerado por agentes está resolvida do ponto de vista arquitetural — mas escolher controles que protejam a segurança sem desacelerar o desenvolvimento continua sendo um desafio.

Live Stream

Agentes de Remediação, Desmistificados: Por Que Corrigir é Melhor do que Encontrar

Veja como o Remediation Agent da Snyk usa inteligência de segurança, análise de explorabilidade e validação para transformar vulnerabilidades em pull requests prontos para merge.