# CVE-2026-111497 — agente de remediación de Brotli

Detector, remediador y verificador deterministas para **CVE-2026-111497**: una escritura fuera de
límites en el decodificador de Google Brotli (`c/dec/decode.c`, CWE-787). Tres scripts de shell
realizan la auditoría completa y devuelven un veredicto compacto; el modelo de lenguaje orquesta y
lee una línea en lugar de explorar el host a mano.

<!-- md-i18n:switcher -->
**En otros idiomas:** [English](README.md) · [Deutsch](README.de.md) · [中文](README.zh.md) · [हिन्दी](README.hi.md) · Español · [Русский](README.ru.md)

[![CI](https://github.com/paulmann/CVE-2026-111497/actions/workflows/ci.yml/badge.svg)](https://github.com/paulmann/CVE-2026-111497/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/paulmann/CVE-2026-111497)](https://github.com/paulmann/CVE-2026-111497/releases)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Shell: bash](https://img.shields.io/badge/shell-bash-4EAA25.svg)](#requisitos)
[![Platform: Debian/Ubuntu](https://img.shields.io/badge/platform-Debian%20%7C%20Ubuntu-A80030.svg)](#requisitos)

![CVE-2026-111497 — agente de remediación de Brotli](assets/social-preview.svg)

## Contenido

- [Por qué existe esto](#por-qué-existe-esto)
- [Modelo de exposición](#modelo-de-exposición)
- [Inicio rápido](#inicio-rápido)
- [Contrato de veredicto y códigos de salida](#contrato-de-veredicto-y-códigos-de-salida)
- [Qué cubre la auditoría](#qué-cubre-la-auditoría)
- [Estructura del repositorio](#estructura-del-repositorio)
- [Requisitos](#requisitos)
- [Limitaciones](#limitaciones)
- [Documentación](#documentación)
- [Idiomas](#idiomas)
- [Contribución](#contribución)
- [Licencia](#licencia)

[Wiki](https://github.com/paulmann/CVE-2026-111497/wiki) · [Changelog](CHANGELOG.md) · [Security](SECURITY.md) · [English](README.md)

## Por qué existe esto

Una auditoría completa de Brotli no es una comprobación de versión. Exige responder cuatro
preguntas por host: qué paquete está instalado, si los procesos en ejecución siguen mapeando el
decodificador vulnerable, qué rutas de la aplicación decodifican realmente Brotli no confiable y si
el backport de la distribución es la corrección real y no una recompilación ajena al problema.

Respondidas a mano, esas preguntas cuestan decenas de llamadas a herramientas por host y producen
texto que no se puede volver a comprobar. Aquí las responden scripts con un contrato fijo de códigos
de salida:

| Comando | Efecto en el host | Salida para el agente |
|---|---|---|
| `detect.sh` | solo lectura | `VERDICT: SAFE\|VULNERABLE\|NOT_INSTALLED` |
| `remediate.sh` | único punto de mutación | `REMEDIATED: <old> -> <new>` |
| `verify.sh` | solo lectura | `VERIFY: PASS\|FAIL` |

## Modelo de exposición

El código vulnerable es el **decodificador**, no el codificador. Los servicios que solo comprimen
respuestas con Brotli no son la superficie de ataque; sí lo son los servicios que descomprimen
Brotli no confiable.

| Ruta de decodificación alcanzable | Portador típico |
|---|---|
| Cuerpos de petición HTTP | `Content-Encoding: br` enviado a la aplicación |
| Clientes HTTP salientes | `fetch()`, libcurl con `CURLOPT_ENCODING` vacío (aceptar todo) |
| Respuestas entrantes de proxies | descompresores HTTP de Envoy/Istio |
| Archivos cargados o descargados | flujo Brotli a nivel de aplicación |
| Runtimes empaquetados | Node.js, Bun, `Brotli`/`brotlicffi` de Python, descompresión de peticiones de .NET |

Las versiones upstream vulnerables son de la **1.0.3 a la 1.2.0**. Las versiones corregidas son la
**1.0.10, la 1.1.1 y la 1.2.1** (publicadas el 21 de octubre de 2026), además de todo backport de
distribución que mencione el CVE en su changelog empaquetado.

## Inicio rápido

```bash
# 1. Deliver the scripts once (host is an SSH alias from ~/.ssh/config)
scp src/common.sh src/detect.sh src/remediate.sh src/verify.sh <host>:/root/cve-2026-111497/

# 2. Audit — read-only
ssh <host> 'bash /root/cve-2026-111497/detect.sh'          # exit 0/1/3/10

# 3. Remediate — only when the verdict is VULNERABLE
ssh <host> 'bash /root/cve-2026-111497/remediate.sh'

# 4. Verify the actual effect
ssh <host> 'bash /root/cve-2026-111497/verify.sh'          # VERIFY: PASS|FAIL
```

## Contrato de veredicto y códigos de salida

| Código | Veredicto | Significado | Siguiente paso |
|---:|---|---|---|
| 0 | `SAFE` | la versión instalada queda fuera del rango vulnerable, o ya hay un backport de la distribución aplicado | documentar y cerrar |
| 1 | `VULNERABLE` | la versión upstream instalada está en el rango vulnerable y no hay backport | ejecutar `remediate.sh` |
| 3 | `NOT_INSTALLED` | `libbrotli1` no está presente en el host | documentar; volver a comprobar si llega Brotli |
| 10 | `ERROR` | se ha violado una precondición o un invariante | leer el mensaje, escalar, no forzar |
| 1 (`verify.sh`) | `FAIL` | la remediación no surtió efecto | investigar antes de declarar el cierre |

`remediate.sh` se detiene con el código 10 antes de tocar el sistema cuando el candidato de apt es
igual a la versión instalada, o cuando el changelog del candidato no menciona `CVE-2026-111497`.

## Qué cubre la auditoría

| Comprobación | Mecanismo | Por qué importa |
|---|---|---|
| Versión instalada y versión upstream | `dpkg-query`, con epoch y revisión eliminados | las revisiones de la distribución ocultan la versión upstream |
| Backport aplicado | `changelog.Debian.gz` empaquetado que menciona el CVE | la señal autoritativa, no la cadena de versión |
| Candidato de apt | `apt-get update` y después `apt-cache policy` | decide si existe alguna corrección soportada |
| Consumidores activos | escaneo de `/proc/<pid>/maps` en busca de `libbrotlidec` | un archivo parcheado no parchea un proceso en ejecución |
| Superficie de decodificación | característica Brotli de libcurl, módulos de Node/Bun/Python/PHP, `ngx_brotli`, `mod_brotli` | separa las rutas de decodificación de las de solo compresión |
| Primitivas de decodificación en la aplicación | recuento de archivos PHP con `CURLOPT_ENCODING` / `CURLOPT_ACCEPT_ENCODING` | WordPress, SimplePie y Guzzle aceptan todas las codificaciones, incluida `br` |
| Instantánea de inodos previa al parche | `find -type f -printf '%i'` sobre el `.so` real | el inodo del enlace simbólico es un ancla falsa |

## Estructura del repositorio

```text
src/
  common.sh      logging, version parsing, classification matrix, state_get
  detect.sh      read-only audit: verdict, JSON report, before-state
  remediate.sh   backup, authenticity gate, dry-run, install, restart, inode check
  verify.sh      version, CVE reference, stale mappings, failed units, round-trip
docs/
  THREAT-MODEL.md  decode-path exposure model and attack surface
  PLAYBOOK.md      phase-by-phase procedure for a human auditor
  FIELD-REPORT.md  anonymised results from three production hosts
wiki/             operator documentation, field cases, advisory digest
SKILL.md          agent skill: the minimal orchestration playbook
AGENTS.md         repository rules and invariants
```

## Requisitos

- Host de destino: Debian 12, Ubuntu 24.04 o Ubuntu 26.04 con `apt`, root mediante una clave SSH.
- `bash` 5.x, `dpkg`, `apt-get`, `systemd`; `curl` y `brotli` son opcionales y solo amplían el
  round-trip funcional.
- Ningún acceso a la red desde las herramientas más allá de los espejos de paquetes de la
  distribución.
- Estado de salida: `/var/lib/cve-2026-111497/` (`report.json`, `state.before`, `state.after`).
- Copias de seguridad: `/root/cve-2026-111497-fix-<timestamp>/` con el `.deb` antiguo, el `.so`
  actual y una instantánea de `dpkg`.

## Limitaciones

- **Solo apt.** Los hosts basados en rpm, Alpine y las imágenes base de contenedor quedan fuera de
  alcance; la matriz de clasificación es portable, pero la ruta de remediación no lo es.
- **Sin detección de enlace estático.** Una copia de Brotli compilada dentro de un binario o de una
  imagen de contenedor no es visible para `dpkg`. El detector informa de las versiones en tiempo de
  ejecución que puede leer y nada más.
- **Sin capa de orquestación.** Envoy, Istio, .NET, Bun y Deno se describen en el modelo de amenazas,
  pero los scripts no los remedian.
- **Los consumidores de escritorio e infraestructura se difieren, no se reinician.** Las pilas de
  fuentes y de interfaz gráfica mapean `libbrotlidec` a través de freetype; reiniciarlas cortaría la
  sesión. Se informan como `deferred` y cargan la biblioteca parcheada en el siguiente inicio de
  sesión o reinicio.
- **`needrestart` puede actuar primero.** En Ubuntu 24.04 el hook del paquete reinicia servicios de
  servidor durante la instalación, por lo que la sección de reinicio puede legítimamente no
  encontrar nada pendiente.
- **Los datos de campo son tres hosts.** El informe es una muestra, no un censo de la flota.

## Documentación

| Documento | Contenido |
|---|---|
| [`docs/THREAT-MODEL.es.md`](docs/THREAT-MODEL.es.md) | modelo de exposición, inventario de rutas de decodificación, razonamiento de gravedad |
| [`docs/PLAYBOOK.es.md`](docs/PLAYBOOK.es.md) | el procedimiento que implementan los scripts, para un auditor humano |
| [`docs/FIELD-REPORT.es.md`](docs/FIELD-REPORT.es.md) | tres hosts, nueve defectos encontrados en campo, evidencia de verificación |
| [Wiki](https://github.com/paulmann/CVE-2026-111497/wiki) | casos de campo con causa raíz, resumen de avisos, manual operativo |

## Idiomas

El inglés es canónico: un documento sin sufijo de idioma es la fuente de verdad. Toda traducción
lleva el sufijo de su idioma y enlaza de vuelta al archivo canónico.

| Idioma | Archivos |
|---|---|
| Inglés (canónico) | `README.md`, `AGENTS.md`, `SKILL.md`, `docs/*.md` |
| Ruso | `*.ru.md` |
| Alemán | `*.de.md` |
| Español | `*.es.md` |
| Chino | `*.zh.md` |
| Hindi | `*.hi.md` |

## Contribución

Las correcciones y los nuevos backports de distribución son bienvenidos. Lea primero
[`CONTRIBUTING.md`](CONTRIBUTING.md): el contrato de códigos de salida, la regla de copia de
seguridad antes del cambio y la puerta de autenticidad no son negociables. Los informes de
seguridad siguen [`SECURITY.md`](SECURITY.md).

## Licencia

MIT. La herramienta es defensiva; no contiene credenciales, ni identificadores de infraestructura,
ni direcciones de destino.
