# Remediation playbooks

How to close CVE-2026-111497 on each component class, in the order that reduces risk fastest. The
[ecosystem matrix](06-Ecosystem-Matrix) says what is affected; this page says what to type.

## 1. Five principles

1. **Patch the runtime or the library, not the manifest.** Updating `package.json` does not fix a
   vulnerable Node.js runtime; updating `requirements.txt` does not fix a vulnerable base image.
2. **Rebuild the final artifact.** Images, Lambda layers, standalone executables, compiled binaries
   and cached CI artifacts keep the old code until they are rebuilt.
3. **Restart every affected process.** A running process continues to use the library it mapped at
   start-up, no matter what the package database says.
4. **Verify the deployed artifact, not the repository.** Check versions inside the running container,
   VM or function.
5. **Order by exposure.** Internet-facing decode paths first; offline utilities last.

## 2. Priority

| Priority | Asset class | Action |
|---|---|---|
| P0 | public Node.js or Bun APIs that accept or fetch untrusted Brotli | upgrade runtime, rebuild, restart, verify |
| P0 | Envoy or Istio with Brotli decompression enabled | upgrade, or disable the Brotli decompressor |
| P0 | public Python or .NET services decoding untrusted Brotli | upgrade package or runtime, rebuild, restart |
| P1 | internal services decoding Brotli from semi-trusted sources | patch on the normal emergency cycle |
| P1 | container images with a vulnerable bundled copy | rebuild from patched base images |
| P2 | internal tools and batch workers | patch and restart |
| P3 | offline utilities processing only trusted local files | routine maintenance |

## 3. Node.js

Upgrade to a patched supported line — there is no complete workaround:

```bash
# with n
npm install -g n
n 24.22.1        # or 22.23.4 / 26.12.1
node --version

# with nvm
nvm install 24.22.1
nvm alias default 24.22.1
nvm use 24.22.1
```

Rebuild and restart the application, because native modules and bundles were built against the old
runtime:

```bash
rm -rf node_modules
npm ci
npm run build
pm2 reload all
# or
systemctl restart your-node-service
```

For containers, change the base image and rebuild without cache — a cached layer keeps the old
runtime even when the tag moved:

```dockerfile
FROM node:24.22.1-bookworm-slim
```

```bash
docker build --no-cache -t your-app:patched .
docker compose up -d --force-recreate
```

Node.js 20.x, 23.x and 25.x have no fixed release: migration is the remediation.

## 4. Bun

```bash
bun upgrade
bun --version     # 1.4.4 or later
```

`bun build --compile` output embeds the runtime. After the upgrade every standalone executable,
bundle and deployment artifact must be rebuilt and redistributed.

## 5. Python

```bash
python3 -m pip install --upgrade 'Brotli>=1.2.1' 'brotlicffi>=1.2.1.0'
```

Update the lockfile, because a pinned dependency will restore the vulnerable build on the next
install:

```bash
# pip-tools
pip-compile --upgrade-package Brotli --upgrade-package brotlicffi requirements.in

# Poetry
poetry update Brotli brotlicffi

# uv
uv lock --upgrade-package Brotli --upgrade-package brotlicffi
```

Then rebuild the image and restart web processes, Celery/RQ workers and scheduled jobs — a worker
keeps the old wheel in memory long after the install.

## 6. .NET

Install the out-of-band update for the release line in use (8.0.33, 9.0.22, 10.0.14, 11 RC3),
rebuild and redeploy. If Brotli request or response decompression is not required, disable it as an
interim measure:

```csharp
// Potentially exposed: decompresses untrusted inbound request bodies
builder.Services.AddRequestDecompression();
app.UseRequestDecompression();

// Potentially exposed: automatically decompresses external Brotli responses
var handler = new HttpClientHandler
{
    AutomaticDecompression = DecompressionMethods.Brotli |
                             DecompressionMethods.GZip |
                             DecompressionMethods.Deflate
};
```

## 7. Envoy

Upgrade to a fixed release: 1.40.0, 1.39.3, 1.38.6, 1.37.8 or 1.36.12.

If an immediate upgrade is impossible, disable the Brotli decompressor for routes that carry
untrusted traffic. Do not remove the decompression filter wholesale: gzip and zstd handling for
legitimate traffic goes with it.

## 8. Istio

Upgrade to 1.31.2, 1.30.6 or 1.29.9, then check the data plane:

- exposure is conditional on Brotli decompression being enabled, typically through an `EnvoyFilter`;
- a control-plane upgrade does not replace the proxy image on running pods;
- audit the filters, confirm the proxy version, and only then close the finding.

## 9. Debian and Ubuntu

```bash
sudo apt update
sudo apt install --only-upgrade libbrotli1 brotli
dpkg -l libbrotli1 brotli
apt-cache policy libbrotli1 brotli
```

After patching a system library, restart every service linked against it — web servers, proxies,
application workers, and containers that mount host libraries.

This is the one row the tool automates. `remediate.sh` performs the backup, the authenticity gate,
the dry run, the install and the restart of server consumers, and `verify.sh` confirms the effect in
`/proc`. The procedure, its stop conditions and its rollback are in the
[operating runbook](10-Operations); the rules it obeys are in the
[repository contract](03-Repository-Contract).

## 10. Closure

A row of the inventory is closed when all five statements are true:

1. The running artifact reports a fixed version, and the command that showed it is recorded.
2. The process start time is later than the patch time.
3. The reject rule for `Content-Encoding: br` answers where decompression was disabled.
4. Legitimate `gzip`, `deflate` and `zstd` traffic still works.
5. The artifact was rescanned (Trivy, Grype or equivalent) and came back clean.

Evidence per row: CVE, asset, old version, new version, build ID, deployment time, owner,
verification result.

Next: [Interim mitigations](09-Interim-Mitigations) — what to do while a patch is impossible.
