Programação Por Equipe DevBras

Capa do artigo: Node.js corrige falha HIGH em 11/11: como se preparar

Node.js corrige falha HIGH em 11/11: como se preparar

Node.js 26, 24 e 22 recebem correção de falha HIGH em 11/11 e o Node 20 fica de fora. Faça o inventário de versões com um script e ensaie a atualização.

O projeto Node.js avisou, em 3 de novembro, que vai publicar versões novas das linhas 26.x, 24.x e 22.x “em ou pouco depois de” quarta-feira, 11 de novembro de 2026, e que a falha mais grave corrigida em cada uma delas tem severidade HIGH. Os detalhes técnicos só saem junto com as versões, como sempre; até lá ninguém sabe qual módulo é afetado. O que já dá para saber é se você está pronto para atualizar no mesmo dia.

Este guia usa os dias antes do lançamento para o que costuma atrasar a correção: descobrir em quantos lugares a versão do Node está fixada (servidor, Dockerfile, CI, .nvmrc), separar o que está em linha sem suporte — que não recebe correção nenhuma — e deixar um teste pronto para rodar com a versão nova. O inventário foi testado no nosso ambiente, com a saída real mostrada abaixo.

O que o aviso oficial diz (e o que ainda não diz)

O pré-anúncio está no blog de vulnerabilidades do Node.js. Em resumo:

  • Linhas corrigidas: 26.x, 24.x e 22.x, todas com pelo menos uma falha HIGH.
  • Data: em ou logo depois de 11/11/2026. Pode ser à noite no horário de Brasília, ou no dia seguinte.
  • O texto repete a regra da casa: versões em fim de vida “são sempre afetadas” quando sai uma correção de segurança. Ou seja, quem está no Node 20 ou anterior deve assumir que é vulnerável e não vai receber versão corrigida.

O que o aviso não diz: CVE, módulo afetado, se a exploração é remota. Não vale a pena especular. O lançamento anterior, de 21 de outubro, mostra o tamanho que esses pacotes costumam ter: quatro falhas HIGH (DNS truncando nome com byte nulo, duas formas de burlar a verificação de nome no certificado TLS e uma escrita fora do buffer no decodificador Brotli), além de atualizações de c-ares, OpenSSL, SQLite e Undici. A do Brotli explicamos em falha no Brotli: como checar e corrigir. Quem atualizou em outubro está em 22.23.4, 24.22.1 ou 26.12.1, e vai precisar subir de novo.

Qual linha você está usando importa mais que o número exato

A agenda oficial de versões (o arquivo schedule.json do repositório nodejs/Release) dá a situação de cada linha em 10/11/2026:

  • Node 26 (Lithium): virou LTS em 28/10/2026; suporte até 30/04/2029. É a linha recomendada para projeto novo.
  • Node 24 (Krypton): entrou em manutenção em 20/10/2026; recebe correções até 30/04/2028.
  • Node 22 (Jod): em manutenção; termina em 30/04/2027, daqui a menos de seis meses.
  • Node 20 (Iron): fim de vida em 30/04/2026. A última versão é a 20.20.2, de março. Não haverá 20.x corrigida em novembro.
  • Node 25: linha ímpar, sem LTS, encerrada em 01/06/2026.

Se algum sistema seu ainda roda 20, 18 ou 25, a quarta-feira não muda nada para ele: o trabalho é migrar de linha, e isso leva mais que uma tarde. Comece por esses.

Inventário: onde a versão do Node está escrita

O Node raramente está fixado num lugar só. No mesmo repositório costuma haver um .nvmrc para quem desenvolve, um FROM node:... em cada Dockerfile, o node-version do GitHub Actions e o campo engines do package.json. Atualizar um e esquecer outro é como uma correção vai para o CI e não chega em produção.

O script abaixo varre uma pasta, acha essas quatro fontes e compara cada uma com a lista oficial de versões (nodejs.org/dist/index.json) e com a agenda de suporte. Não tem dependência: roda em Node 18 ou mais novo, que já trazem fetch.

// inventario-node.mjs — lista onde o projeto fixa a versão do Node e compara com nodejs.org
// uso: node inventario-node.mjs <pasta>
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';

const raiz = process.argv[2] ?? '.';
const PULAR = new Set(['node_modules', '.git', 'dist', 'build']);
const achados = [];

function anota(arquivo, origem, valor) {
  const m = String(valor).match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
  if (m) achados.push({ arquivo: relative(raiz, arquivo), origem, valor: String(valor).trim(), major: Number(m[1]) });
}

function varre(pasta) {
  for (const nome of readdirSync(pasta)) {
    if (PULAR.has(nome)) continue;
    const caminho = join(pasta, nome);
    if (statSync(caminho).isDirectory()) { varre(caminho); continue; }
    const texto = () => readFileSync(caminho, 'utf8');
    if (nome === '.nvmrc' || nome === '.node-version') anota(caminho, nome, texto());
    else if (nome === 'package.json') {
      const eng = JSON.parse(texto()).engines?.node;
      if (eng) anota(caminho, 'engines.node', eng);
    } else if (/^Dockerfile/.test(nome) || /\.dockerfile$/.test(nome)) {
      for (const m of texto().matchAll(/^FROM\s+(?:\S+\/)?node:(\S+)/gim)) anota(caminho, 'FROM node', m[1]);
    } else if (/\.ya?ml$/.test(nome) && caminho.includes('.github')) {
      for (const m of texto().matchAll(/node-version:\s*['"]?([\w.]+)/g)) anota(caminho, 'setup-node', m[1]);
    }
  }
}

varre(raiz);
const [versoes, agenda] = await Promise.all([
  fetch('https://nodejs.org/dist/index.json').then(r => r.json()),
  fetch('https://raw.githubusercontent.com/nodejs/Release/main/schedule.json').then(r => r.json()),
]);
const hoje = new Date().toISOString().slice(0, 10);
console.log(`Node deste computador: ${process.version}  |  consulta em ${hoje}\n`);
for (const a of achados) {
  const linha = agenda[`v${a.major}`];
  const ultima = versoes.find(v => v.version.startsWith(`v${a.major}.`));
  let situacao;
  if (!linha || !ultima) situacao = 'linha desconhecida';
  else if (linha.end < hoje) situacao = `FORA DE SUPORTE desde ${linha.end}: não recebe a correção`;
  else situacao = `suportada até ${linha.end}; última publicada ${ultima.version} (${ultima.date})`;
  console.log(`${a.arquivo.padEnd(34)} ${a.origem.padEnd(13)} ${a.valor.padEnd(14)} -> ${situacao}`);
}

Para testar, montamos um repositório fictício parecido com o de uma loja pequena: uma API com Dockerfile em Node 20, um painel em Node 24, CI no 22 e um .nvmrc esquecido no 20. A saída real foi esta:

$ node inventario-node.mjs loja-teste
Node deste computador: v24.22.1  |  consulta em 2026-11-10

.github/workflows/ci.yml           setup-node    22.x           -> suportada até 2027-04-30; última publicada v22.23.4 (2026-10-20)
.nvmrc                             .nvmrc        20             -> FORA DE SUPORTE desde 2026-04-30: não recebe a correção
api/Dockerfile                     FROM node     20.19-alpine   -> FORA DE SUPORTE desde 2026-04-30: não recebe a correção
api/package.json                   engines.node  >=18           -> FORA DE SUPORTE desde 2025-04-30: não recebe a correção
painel/Dockerfile                  FROM node     24-slim        -> suportada até 2028-04-30; última publicada v24.22.2 (2026-10-27)
painel/Dockerfile                  FROM node     24-slim        -> suportada até 2028-04-30; última publicada v24.22.2 (2026-10-27)
painel/package.json                engines.node  ^22.12 || >=24 -> suportada até 2027-04-30; última publicada v22.23.4 (2026-10-20)

Três achados que aparecem em quase todo projeto:

  • A API em node:20.19-alpine não vai receber a correção. Trocar a tag para node:26-alpine exige rodar os testes, porque mudam V8, OpenSSL e alguns comportamentos padrão. É o item que vai para o topo.
  • engines: ">=18" não protege nada. Aceita qualquer versão a partir da 18, inclusive as sem suporte. O script lê só o limite inferior da faixa; vale subir para algo como "^22.22 || ^24.22 || >=26.12" depois da atualização, para o npm avisar quando alguém instalar uma versão antiga.
  • node:24-slim e 22.x são tags flutuantes. Elas vão apontar para a versão corrigida quando a imagem oficial e o setup-node forem atualizados, mas a imagem que já está no seu servidor continua a antiga até você reconstruir e subir de novo. Se você fixa por digest, como recomendamos em tag latest no Docker não é versão, o digest precisa ser trocado de propósito.

Antes de quarta: deixe a atualização ensaiada

A versão corrigida de cada linha sai com número de patch ou minor novo. O que dá para fazer agora, sem saber o número:

  1. Atualize para a última versão de hoje da sua linha. Em 10/11 são 26.13.0, 24.22.2 e 22.23.4. Se o seu sistema quebra ao sair da 24.22.1 para a 24.22.2, é melhor descobrir antes, sem pressa, do que na quarta à noite junto com a correção.
  2. Garanta que o lockfile manda na instalação. Com npm ci no CI e na imagem, a única coisa que muda na atualização é o Node, não meia dúzia de dependências junto. O motivo está em npm ci e lockfile.
  3. Tenha o caminho de volta. Guarde a tag ou o digest da imagem que está no ar hoje. Se a versão nova der problema, voltar é trocar uma linha e reiniciar.
  4. Combine quem atualiza. Para quem tem servidor exposto à internet, o razoável é aplicar no mesmo dia em que os detalhes saírem, se a falha atingir algo que você usa (HTTP, TLS, zlib, DNS são os candidatos habituais).

Na quarta: como saber se a correção já saiu e se você está nela

A lista oficial marca com "security": true as versões que corrigem falhas de segurança. Este script curto compara o Node em uso com a última versão de segurança da mesma linha:

// ultima-seguranca.mjs — compara o Node em uso com a última versão marcada "security" na mesma linha
const v = await fetch('https://nodejs.org/dist/index.json').then(r => r.json());
const major = process.version.split('.')[0];
const seg = v.find(x => x.version.startsWith(major + '.') && x.security);
const ultima = v.find(x => x.version.startsWith(major + '.'));
console.log(`em uso:              ${process.version}`);
console.log(`última de segurança: ${seg.version} (${seg.date})`);
console.log(`última da linha:     ${ultima.version} (${ultima.date})`);

Rodado hoje, ainda antes do lançamento, o resultado foi:

$ node ultima-seguranca.mjs
em uso:              v24.22.1
última de segurança: v24.22.1 (2026-10-20)
última da linha:     v24.22.2 (2026-10-27)

Repare que a última versão marcada como segurança na linha 24 é a de outubro. Quando a correção de novembro sair, a linha “última de segurança” passa a mostrar a versão nova, e o “em uso” precisa ser igual a ela ou mais recente. Para conferir o que está realmente em produção, e não só no seu computador, rode docker exec <container> node -v em cada container e compare com a linha “última de segurança”.

Para receber o aviso sem depender de rede social, a lista nodejs-sec é só de anúncios e tem pouco volume. A política de segurança do projeto explica como os pré-anúncios funcionam.

E se você usa Docker em vários servidores

O trabalho pesado é reconstruir e reiniciar tudo o que usa node:. Um painel que mostra a imagem e a data de criação de cada container ajuda a ver o que ficou para trás. Comparamos opções em painéis para Docker: Portainer, Dockge, Komodo e Dockyard; o Dockyard, que desenvolvemos, foi feito para esse tipo de conferência em servidor pequeno. Sem painel, docker ps --format '{{.Names}} {{.Image}}' em cada máquina resolve.

Resumo

  • Node.js 26.x, 24.x e 22.x recebem correção de falha HIGH em ou logo depois de 11/11/2026; detalhes só no lançamento.
  • Node 20 e anteriores estão fora de suporte e não terão versão corrigida: migre de linha.
  • Faça o inventário agora: .nvmrc, Dockerfile, CI e engines.
  • Ensaie a atualização com a última versão de hoje e deixe a volta preparada.
  • Depois do lançamento, confira com a lista oficial que o Node em produção é o corrigido.

Quando as versões saírem, os CVEs e os números de cada linha aparecem no mesmo endereço do pré-anúncio, linkado no início.

— Equipe DevBras. Siga a DevBras no Instagram e no YouTube.

Avalie este artigo