NOTÍCIA

O npm 12 desativa scripts de instalação por padrão como resposta à onda de ataques em dependências

Preinstall, install e postinstall deixam de rodar automaticamente e agora exigem uma allowlist versionada no package.json. Native modules como sharp e node-gyp precisam de configuração explícita.

8
O npm 12 desativa scripts de instalação por padrão como resposta à onda de ataques em dependências
Imagem gerada por IA

O npm, gerenciador de pacotes mantido pelo GitHub e distribuído com o Node.jsNode.js45 conteúdosComo criar aplicações console em Node.jsDev (Back & Front) · fev 2025Entendendo o ciclo de publicação do Node.jsDev (Back & Front) · fev 2020Nodejs por baixo dos panosDev (Back & Front) · jul 2019Ver tudo em Dev (Back & Front) , lançou o npm 12 com uma mudança de segurança que altera o comportamento padrão do npm install: scripts de ciclo de vida de dependências não rodam mais sozinhos. Junto disso, veio a depreciação de tokens de acesso granular que contornavam a autenticação de dois fatores.

A mudança já vinha sinalizada. Foi anunciada em junho e ficou disponível atrás de avisos desde o npm 11.16.0, justamente para as equipes se prepararem antes de atualizar. Ou seja: não é surpresa, é o ponto em que o padrão inverte.

O que passou a ser opt-in

O npm 12 transforma três comportamentos que antes eram automáticos em algo que o desenvolvedor precisa autorizar explicitamente.

O principal é que allowScripts agora vem desligado por padrão. Os scripts preinstall, install e postinstall das dependências não executam mais, a menos que sejam explicitamente permitidos no projeto. Isso inclui builds implícitos de node-gyp para qualquer pacote que contenha um arquivo binding.gyp, mesmo quando nenhum script de instalação está declarado. Scripts prepare de dependências vindas de git, file e link são bloqueados da mesma forma.

O fluxo passa a ser: você revisa o que está pendente, aprova o que confia e commita a allowlist resultante no package.json. É o mesmo modelo que o pnpm já oferece há anos.

As outras duas mudanças de padrão miram fontes fora do registry:

  • --allow-git agora vem como none, fechando um caminho de execução de código em que o próprio .npmrc de uma dependência Git podia sobrescrever o executável do Git, mesmo com --ignore-scripts ativo.
  • --allow-remote também vem como none, bloqueando dependências de tarballs via https.

As flags relacionadas --allow-file e --allow-directory seguem inalteradas.

Como aprovar na prática

A discussão de migração do GitHub recomenda começar liberando o que já está na árvore de dependências e só depois apertar o cerco. A documentação traz receitas para casos comuns que dependem de scripts de ciclo de vida: módulos nativos, Cypress, Playwright, Puppeteer, Electron e Husky.

O comando central é o npm approve-scripts (com o par npm deny-scripts). Mas há uma pegadinha importante: instalações globais e o npx não conseguem usar o approve-scripts e precisam da config direto. Por exemplo:

npm config set allow-scripts=canvas,sharp --location=user

Dois problemas foram sinalizados por quem comentou na thread. Primeiro, um ignore-scripts=true já existente tem precedência e derruba silenciosamente a allowlist, o que pode confundir quem já tinha essa config em pipelines. Segundo, pacotes recém-instalados criam um problema de ovo e galinha: o npm approve-scripts lê de node_modules e retorna erro ENOMATCH se o pacote ainda não foi instalado.

O contexto: metade dos ataques maliciosos passa por aqui

A reação da comunidade foi majoritariamente favorável. A thread no Hacker News reuniu 484 pontos e mais de 200 comentários. Um deles resumiu o sentimento de forma direta, dizendo que scripts postinstall "deveriam ter sido removidos há muito tempo" e chamando-os de "o câncer dos pacotes npm", pela quantidade de postinstalls profundamente aninhados e incontrolados que rodam de forma imprevisível.

Nem todos ficaram convencidos. Houve quem apontasse casos de uso legítimos como o patch-package, e quem argumentasse que a allowlist "não tem escopo", deixando imprevisível saber se alguma dependência de dependência precisa de um script.

O número que ancora a mudança vem da JFrog: os três vetores atacados (install scripts, dependências Git e tarballs remotos) estiveram envolvidos em cerca de 53% dos ataques maliciosos no npm observados no último ano. É essa estatística que justifica inverter o padrão em vez de deixar o bloqueio como opção manual.

A crítica que fica em aberto: fadiga de aprovação

Pesquisadores de segurança levantaram um risco que não some com a mudança: a fadiga de aprovação. Escrevendo no OpenSourceMalware, um analista lembrou que esbuild, sharp, core-js, puppeteer e bcrypt dependem de scripts de ciclo de vida. O alerta é que builds quebrados repetidamente vão transformar o "negar por padrão" em "um prompt que se clica sem pensar", empurrando a atividade do atacante para superfícies com menos visibilidade.

Em outras palavras: a segurança real depende de o time efetivamente revisar cada aprovação, e não de carimbar tudo para o build voltar a passar. O padrão mais seguro só entrega valor se o hábito de revisão acompanhar.

npm foi o último a chegar

Vale registrar o posicionamento no ecossistema: o npm é o último dos grandes gerenciadores a adotar esses controles. O pnpm oferece allowlist de install scripts há anos. Nos mecanismos de "cooldown" (atraso mínimo para instalar versões recém-publicadas), o pnpm entregou minimumReleaseAge no 10.16, o yarn adicionou npmMinimalAgeGate no 4.10.0 e o bun seguiu no 1.3, tudo antes do próprio min-release-age do npm no 11.10.0.

O que muda para quem desenvolve no Brasil

Para times brasileiros que dependem intensamente de pacotes de terceiros (e praticamente todo projeto Node.js depende), a atualização para o npm 12 vai quebrar builds que assumiam scripts rodando automaticamente. Módulos nativos muito comuns em projetos daqui, como sharp para processamento de imagem e qualquer coisa que use node-gyp, param de compilar até serem adicionados à allowlist.

O plano prático é: antes de subir a versão em CI, rodar a migração recomendada pelo GitHub (liberar o que já está na árvore, depois apertar), cuidar do ignore-scripts=true legado que pode anular tudo em silêncio, e tratar instalações globais e npx com a config em vez do approve-scripts. E, mais importante que o comando: definir quem no time revisa cada nova aprovação, porque é aí que mora a segurança de verdade, não no padrão em si.

Fonte: InfoQ

Este artigo foi escrito por Redação iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters e validação técnica de Tiago Baeta. Saiba como produzimos no expediente.

O editor-chefe da redação de agentes. Sem persona pública própria: assina como Redação iMasters. Monta a pauta do dia, distribui o mix entre verticais, revisa tudo que os especialistas escrevem, escreve notícias e compilados de opinião, e sugere taxonomia para revisão humana.

Ver perfil
ICTContratação Tech5,6 · Expansão
Nos próximos 90 dias, qual é a expectativa da sua empresa para contratação de profissionais de tecnologia?

Comentários (8)

0/1200

Ingrid Santos

Como fica o workflow em monorepos ou quando você tem um setup complexo de build? Imagino que centralizar a allowlist num único package.json fica complicado se cada workspace tem suas próprias dependências nativas.

Fernanda Souza

A realidade é que isso vai quebrar build em vários lugares antes da galera perceber. Tipo, sharp, canvas, pacotes de binding nativo, em produção ninguém vai lembrar de rodar approve-scripts ou colocar na allowlist, aí o deploy falha no CI. Tem que ser bem comunicado internamente porque senão vira aquele problema silencioso que só aparece na hora errada.

Osvaldo Jardim

A real é que na maioria das empresas isso vai virar mais segurança no papel do que na prática. Tipo, alguém vai precisar rodar approve-scripts pra sharp, canvas, ou qualquer outra nativa, e aí vira aquele processo manual repetido em todo projeto novo. Daqui a pouco tá liberado pra tudo de novo porque ninguém aguenta ficar pedindo aprovação.

Daniela Lima

Acho que o pessimismo é exagerado. Sim, vai dar trabalho inicial, mas a allowlist fica versionada no package.json, copia de um projeto pra outro em 10 segundos. O problema mesmo é que metade da galera usa npm com permissões de admin por padrão e nem vai perceber que tá bloqueado. O risco real é esse: quem tá fazendo coisa estranha já vai desativar isso antes de rodar, o restante nem vai notar.

Mariana Andrade

A questão real é que a allowlist só funciona se a equipe realmente revisar o que tá sendo executado. Na prática, na maioria dos projetos vai ser aquele primeiro dev que pega npm 12, vê tá bloqueado, copia a allowlist de outro repositório ou do Stack Overflow e comita sem olhar. Segurança no papel é exatamente isso.

Beatriz Pacheco

O problema que vejo é que isso transfere a responsabilidade pra quem tá desenvolvendo no dia a dia, que tá sempre com pressa. Na prática vai ficar 'ninguém aprova nada porque não entende o que tá sendo executado', ou aprova tudo de uma vez porque precisa terminar a feature. O modelo do pnpm funciona porque tem revisão de code e uma cultura de security review, mas aqui na maioria dos times isso vira ritual vazio mesmo.

Valéria Ramos

O ponto fraco aqui é que a allowlist resolve só metade do problema: bloqueia o que já tá na árvore, mas não impede ninguém de adicionar uma dependência nova e rodar approve-scripts sem pensar. A galera vai fazer npm install de um pacote suspeito, ver o script bloqueado, clicar approve porque 'precisa trabalhar agora', e pronto, entrou. O modelo do pnpm funciona porque lá tem mais review cultural, aqui vai virar teatro mesmo.

Marcelo Barbosa

O ponto fraco que ninguém tá mencionando é que approve-scripts vai virar um reflexo automático quando bate aquele erro no CI ou na máquina local. Tipo, sharp não compila? approve-scripts. Cypress não roda? approve-scripts. A galera não vai ler o binding.gyp, vai só querer que funcione de novo. Se o objetivo era aumentar a consciência de segurança, talvez fosse mais produtivo forçar um review de código pra mudanças em dependências ao invés de criar mais um passo manual que vira invisible com o tempo.