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.

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.

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