Brotli CVE-2026-111497: corrija apps Node.js, Bun e Python
CVE-2026-111497 é um bug de corrupção de memória no decodificador Brotli do Google, o código C que descompacta dados com Content-Encoding: br. Dados comprimidos manipulados escrevem além do fim de um buffer, o que pode derrubar o programa, vazar memória ou executar o código do atacante. Node.js, Bun, os pacotes Python Brotli e brotlicffi e o Envoy trazem cada um a sua própria cópia desse decodificador, por isso cada um precisa ser atualizado separadamente. Este guia mostra quando uma aplicação está de fato exposta, como verificar um servidor com um único comando e como aplicar a correção sem esquecer nenhum contêiner.
Qual a gravidade
- O Google dá nota 9.2 (critical) no CVSS 4.0, o Node.js classifica como high e a Red Hat como 8.1.
- Os pesquisadores da Imperva executaram o próprio código num Node.js 24.22.0 sem patch em Linux ARM64, tanto por uma resposta obtida via fetch quanto pelo corpo de uma requisição no Express. O exploit não é público.
- A avaliação da CISA de 23 de outubro não registrou exploração ativa, e não há prova de conceito pública confiável.
- A falha funciona nos dois sentidos: um servidor que descompacta entradas comprimidas pode ser atacado por um cliente, e um cliente que descompacta respostas comprimidas pode ser atacado por um servidor.
A correção entrou no código-fonte do Brotli em 27 de fevereiro, mas nenhuma versão do Brotli a incluiu até 21 de outubro, e é por isso que todos os runtimes corrigidos saíram na mesma semana.
Você está exposto?
Só corre risco o código que decodifica dados Brotli controlados por outra pessoa:
- Node.js e Bun - o
fetch()nativo ao ler uma resposta enviada comContent-Encoding: br, chamadas Brotli denode:zlibsobre dados não confiáveis e parsers de corpo que descompactam uploads em Brotli (Express 5, body-parser 2). Um servidor HTTP Node.js simples não decodifica corpos de requisição por conta própria. - Python - basta ter o pacote Brotli ou brotlicffi instalado: requests, urllib3, httpx e aiohttp passam a decodificar respostas Brotli sozinhos, mesmo que o seu código nunca importe Brotli.
- Envoy e Istio - apenas se houver um filtro de descompressão Brotli configurado.
Qualquer serviço que chame APIs de terceiros, webhooks, provedores de pagamento ou faça scraping de páginas executa fetch() contra servidores que você não controla. Remover br do Accept-Encoding não é uma correção: um servidor pode enviar Brotli mesmo assim.
| Software | Afetado | Corrigido em |
|---|---|---|
| Node.js | 22.x, 24.x, 26.x antes da correção; 20.x e anteriores (fim de vida) | 22.23.4, 24.22.1, 26.12.1 |
| Bun | 1.4.3 e anteriores | 1.4.4 |
| Python Brotli | 1.2.0 e anteriores | 1.2.1 |
| Python brotlicffi | 1.2.0.x e anteriores | 1.2.1.0 |
| Google Brotli (biblioteca C) | 1.0.3 - 1.2.0 | 1.0.10, 1.1.1, 1.2.1 |
| Envoy | 1.39.2, 1.38.5, 1.37.7, 1.36.11 | 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12 |
| Istio | 1.31.0 - 1.31.1, 1.30.0 - 1.30.5, 1.29.0 - 1.29.8 | 1.31.2, 1.30.6, 1.29.9 |
Verifique um servidor com um comando
Este script de shell POSIX verifica Bun, Node.js e Python na máquina (ou dentro de um contêiner) e informa quais são anteriores à correção. Salve-o como brotli-check.sh:
#!/bin/sh
# CVE-2026-111497 (Brotli decoder): which runtimes on this host are older than the fixes?
ver_lt() { [ "$1" = "$2" ] && return 1; [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]; }
if command -v bun >/dev/null 2>&1; then
v=$(bun --version)
if ver_lt "$v" 1.4.4; then echo "bun $v: VULNERABLE, upgrade to 1.4.4+"; else echo "bun $v: fixed"; fi
fi
if command -v node >/dev/null 2>&1; then
v=$(node -p "process.versions.bun ? '' : process.versions.node")
if [ -z "$v" ]; then echo "node: this is Bun's node shim, see the bun line"; else major=${v%%.*}
case $major in
22) fix=22.23.4 ;; 24) fix=24.22.1 ;; 26) fix=26.12.1 ;;
*) fix= ;;
esac
if [ -z "$fix" ]; then echo "node $v: no fixed release for this line, move to 22, 24 or 26"
elif ver_lt "$v" "$fix"; then echo "node $v: VULNERABLE, upgrade to $fix+"
else echo "node $v: fixed"; fi
fi
fi
for py in python3 python; do
command -v $py >/dev/null 2>&1 || continue
$py - <<'PY'
from importlib import metadata
for pkg, fix in (("Brotli", "1.2.1"), ("brotlicffi", "1.2.1.0")):
try:
v = metadata.version(pkg)
except metadata.PackageNotFoundError:
print(f"python {pkg}: not installed")
continue
a = [int(x) for x in v.split(".") if x.isdigit()]
b = [int(x) for x in fix.split(".")]
print(f"python {pkg} {v}: {'VULNERABLE, upgrade to ' + fix + '+' if a < b else 'fixed'}")
PY
break
done
sh brotli-check.sh
docker exec my-app sh -s < brotli-check.sh # the same check inside a running container
Saída verificada em 2026-10-24 (um notebook de desenvolvimento e depois imagens oficiais do Docker):
bun 1.4.1: VULNERABLE, upgrade to 1.4.4+
node 26.6.0: VULNERABLE, upgrade to 26.12.1+
python Brotli: not installed
python brotlicffi: not installed
bun 1.4.4: fixed
node: this is Bun's node shim, see the bun line
node 22.23.4: fixed
node 16.20.2: no fixed release for this line, move to 22, 24 or 26
python Brotli 1.1.0: VULNERABLE, upgrade to 1.2.1+
python brotlicffi: not installed
Rode-o em cada contêiner, não só no host: o Node.js do host não diz nada sobre o que vem embutido numa imagem.
Como aplicar a correção
- Encontre cada cópia. As imagens são o esquecimento mais comum. Liste o que está rodando:
docker ps --format '{{.Image}}'e depois as linhasFROMdos seus Dockerfiles. Uma tag comonode:22ouoven/bun:alpinesó avança quando você faz pull; uma tag fixada comooven/bun:1.4.1-slimnunca avança. - Atualize o runtime. Suba as tags fixadas (
oven/bun:1.4.4-slim,node:22.23.4-alpine), rodebun upgradee atualize os pacotes Node.js nos hosts, epip install -U "Brotli>=1.2.1"(oubrotlicffi>=1.2.1.0) nos ambientes Python. Se uma aplicação Python não precisa de Brotli,pip uninstall Brotli brotlicffitambém fecha o caminho: os clientes HTTP voltam a usar gzip e deflate. - Reconstrua e substitua.
docker compose build --pull && docker compose up -d. Um processo em execução mantém o decodificador antigo até ser reiniciado, então reinicie também os serviços nos hosts. - Verifique. Rode de novo o script de verificação dentro de cada contêiner (
docker exec <name> bun --versioné a versão rápida). Lockfiles com--frozen-lockfilepodem quebrar a reconstrução depois de atualizar dependências: faça commit do lockfile atualizado junto com opackage.json. - Ainda não pode atualizar? Rejeite no proxy as requisições com
Content-Encoding: brantes que qualquer coisa decodifique o corpo, e evite buscar dados de hosts não confiáveis. A Vercel observa que regras de firewall não cobrem as respostas que o seu próprio código busca em outros servidores - só a atualização resolve.
Para uma stack Node.js numa VM veja Servidor cloud para Node.js, para Python Servidor cloud para Python e Flask e, para imagens que se reconstroem sem problemas, Servidor cloud para Docker Swarm.
O que nós fizemos
Poucos dias após as correções, passamos para 1.4.4 todos os serviços da DCXV que rodam em Bun: as imagens base fixadas foram para tags corrigidas, e todas as imagens foram reconstruídas com um pull novo. Nossos componentes Node.js não decodificam corpos Brotli, e nossos proxies não têm descompressão Brotli. Os servidores dos clientes são geridos pelos seus donos: se você roda Node.js, Bun ou Python num servidor cloud da DCXV, execute hoje a verificação acima ou pergunte-nos sobre a administração gerenciada de servidores.
Fontes (verificadas em 2026-10-24): as versões de segurança do Node.js de outubro de 2026 (22.23.4, 24.22.1, 26.12.1), a versão Bun 1.4.4, a avaliação da CISA de 23 de outubro de 2026 e a pesquisa da Imperva.
