fix(docker): move the app and bot images to Node 22.23.4 (Node2Shell) #226

Merged
whitlocktech merged 2 commits from fix/node22-node2shell into main 2026-10-27 12:40:04 -05:00
Member

What & why

CVE-2026-111497 ("Node2Shell") is an out-of-bounds write in the Brotli decoder that ships inside Node. It was fixed on 2026-10-21 in 22.23.4 / 24.22.1 / 26.12.1. Both of our images were on node:20-alpine. Node 20 is end-of-life and will never get the fix.

Exposure audit (approved by the org lead before this change):

  • Incoming request bodies: not exposed. We run Express 4.22.2 with body-parser 1.20.x, which answers Content-Encoding: br with 415. The CVE's main attack path is Express 5 / body-parser 2.
  • Direct zlib Brotli calls: none. The only zlib call anywhere is a gunzipSync in Module-uo.
  • node-tar (module install): not exposed. It tries Brotli only for .tar.br/.tbr, and we always write bundle.tar.gz.
  • Outbound fetch(): low risk. Node decodes br responses automatically, but we only fetch from fixed or admin-configured hosts (Google, Discord, Steam, custom OIDC, the sidecar, the bot↔site link, module hosts on the allowlist). There are no user-supplied URLs.

The runtime itself was still unpatched, so this:

  • pins node:22.23.4-alpine in Dockerfile and bot/Dockerfile. The exact version is pinned on purpose: a floating tag can keep an old build cached;
  • runs PR checks on Node 22.23.4, the same version the image ships, instead of 20;
  • updates the README prerequisites. The docs/website/website-README.md mirror follows in a docs PR.

Once this merges, build-images.yml publishes new images; every deployment then needs docker compose pull && docker compose up -d.

How it was tested

  • Both images built locally; node --version inside each one prints v22.23.4.
  • Ran the server suite on node:22.23.4-alpine (npm ci && npm test, MODULES_DIR pointed at an empty dir): 2180 pass, 99 skipped. All 3 failures came from my harness: I mounted only server/, so two tests couldn't find ../../scripts/… and one couldn't find client/src/styles/theme.css. None of them involve Node 22. CI runs the full tree.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Claude Opus 5.5). I have reviewed and understand
    every change, and take responsibility for it. AI-authored commits are
    marked with a Co-Authored-By / Assisted-By trailer.

License

  • I agree that my contribution is licensed under this project's license
    (GNU GPL v3.0 or later), and I have the right to contribute it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

## What & why CVE-2026-111497 ("Node2Shell") is an out-of-bounds write in the Brotli decoder that ships inside Node. It was fixed on 2026-10-21 in **22.23.4 / 24.22.1 / 26.12.1**. Both of our images were on `node:20-alpine`. Node 20 is end-of-life and will never get the fix. **Exposure audit (approved by the org lead before this change):** - **Incoming request bodies:** not exposed. We run Express 4.22.2 with body-parser 1.20.x, which answers `Content-Encoding: br` with 415. The CVE's main attack path is Express 5 / body-parser 2. - **Direct zlib Brotli calls:** none. The only zlib call anywhere is a `gunzipSync` in Module-uo. - **node-tar (module install):** not exposed. It tries Brotli only for `.tar.br`/`.tbr`, and we always write `bundle.tar.gz`. - **Outbound `fetch()`:** low risk. Node decodes `br` responses automatically, but we only fetch from fixed or admin-configured hosts (Google, Discord, Steam, custom OIDC, the sidecar, the bot↔site link, module hosts on the allowlist). There are no user-supplied URLs. The runtime itself was still unpatched, so this: - pins `node:22.23.4-alpine` in `Dockerfile` and `bot/Dockerfile`. The exact version is pinned on purpose: a floating tag can keep an old build cached; - runs PR checks on Node 22.23.4, the same version the image ships, instead of 20; - updates the README prerequisites. The `docs/website/website-README.md` mirror follows in a docs PR. Once this merges, `build-images.yml` publishes new images; every deployment then needs `docker compose pull && docker compose up -d`. ## How it was tested - Both images built locally; `node --version` inside each one prints `v22.23.4`. - Ran the server suite on `node:22.23.4-alpine` (`npm ci && npm test`, `MODULES_DIR` pointed at an empty dir): 2180 pass, 99 skipped. All 3 failures came from my harness: I mounted only `server/`, so two tests couldn't find `../../scripts/…` and one couldn't find `client/src/styles/theme.css`. None of them involve Node 22. CI runs the full tree. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Claude Opus 5.5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` / `Assisted-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-26 10:00:28 -05:00
fix(docker): move the app and bot images to Node 22.23.4 (Node2Shell)
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 1m1s
PR Checks / client-build (pull_request) Successful in 2m13s
PR Checks / server-tests (pull_request) Successful in 7m19s
2a6e307f16
CVE-2026-111497 ("Node2Shell") is an out-of-bounds write in the Brotli
decoder Node bundles; it was fixed on 2026-10-21 in 22.23.4, 24.22.1 and
26.12.1. Both images were on node:20-alpine, and Node 20 is past end of
life, so it gets no fix.

The site is not reachable through the main path (Express 4 / body-parser
1.20 refuse a `br` request body with 415, and nothing calls the zlib
Brotli API), but fetch() decodes Brotli responses and the runtime itself
was unpatched. Pin the exact patched version so a floating tag cannot
hold an old build, and run CI on the same version the image ships.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech approved these changes 2026-10-27 12:33:05 -05:00
whitlocktech added 1 commit 2026-10-27 12:33:19 -05:00
Merge branch 'main' into fix/node22-node2shell
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 45s
PR Checks / client-build (pull_request) Successful in 1m5s
PR Checks / server-tests (pull_request) Successful in 6m35s
3c1c174337
whitlocktech scheduled this pull request to auto merge when all checks succeed 2026-10-27 12:33:30 -05:00
whitlocktech merged commit 3b9998539f into main 2026-10-27 12:40:04 -05:00
whitlocktech deleted branch fix/node22-node2shell 2026-10-27 12:40:04 -05:00
Sign in to join this conversation.
No description provided.