Skip to content

Security: patch the Brotli decoder RCE (CVE-2026-111497) in the backend and image builds - #82

Merged
githubesson merged 5 commits into
mainfrom
security/brotli-cve-2026-111497
Oct 24, 2026
Merged

githubesson merged 5 commits into
mainfrom
security/brotli-cve-2026-111497

Conversation

@githubesson

@githubesson githubesson commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Why

CVE-2026-111497 (GHSA-9v8h-qqgm-4m35, the "node2shell" / "HTTP2Shell" bug) is an out-of-bounds write in the google/brotli decoder, versions 1.0.3 to 1.2.0. Google released 1.2.1, 1.1.1 and 1.0.10 on 21 Oct. The upstream fix is 0ed4268a: an invalid block switch was reported as NEEDS_MORE_INPUT instead of a format error. The streaming decoder therefore kept appending input bytes to its 8-byte internal buffer, guarded only by a debug-only BROTLI_DCHECK, and wrote past it. The fix adds BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH so the invalid switch becomes a permanent error. Any code that decodes a br body from a party it doesn't control is exposed. That includes a client reading a response.

What changed

Where Before After
Go backend andybalholm/brotli v1.2.0 (indirect, via bogdanfinn/fhttp, used by the Genius lyrics client) v1.2.7, which ports the fix. Go bounds checks made this a panic or garbage output rather than RCE.
Backend runtime image alpine:3.20 (end of life): nodejs 20.15.1 linked against brotli-libs 1.1.0 alpine:3.22 (digest-pinned): nodejs 22.23.4 linked against brotli-libs 1.1.1. 'brotli-libs>=1.1.1' fails the build rather than install an unpatched copy.
Frontend image build stage node:20-alpine. Node 20 is end of life and gets no fix. node:24.22.1-alpine (digest-pinned), which includes the brotli cherry-pick. This stage runs on the production host during docker compose build.
CI and the macOS release job Filen helper, core, frontend and mobile jobs and the macOS desktop build on Node 20; frontend-ui and landing on a floating "22" Every setup-node pinned to 22.23.4. A bare "22" takes the runner's cached 22.23.3, which predates the fix, because check-latest is off. The pin also matches the new backend image's Node line for the Filen helper.
Linux/Windows desktop builder (scripts/build-desktop-docker.sh, self-hosted runner with publish credentials) electronuserland/builder:wine from 2026-05-21, Node 24.15.0 Re-pinned to the 2026-10-24 wine build with Node 24.22.1. Its history differs only in a refreshed Ubuntu base layer, a Flatpak PPA key step and the Node version; the Wine layers are unchanged.

Alpine builds nodejs with --shared-brotli, so the backend image's exposure depended on the brotli-libs version, not the Node version. ffmpeg stays on the 6.1 line (6.1.1 → 6.1.2). Two comments that still said "Node 20" are updated.

Not affected (checked)

  • hifi-api: resolving its upstream requirements.txt at the pinned commit gives no Brotli or brotlicffi. urllib3 and httpx only decode br when one of those is installed.
  • Desktop: Electron 43.1.1 links both Chromium's net stack and its embedded Node against Chromium's third_party/brotli. Electron's Node GN patch swaps deps/brotli for //third_party/brotli:dec/:enc. Chromium 150 vendors brotli cfa184a (2026-03-18), which contains 0ed4268a. So neither net.fetch nor Node's zlib in the main process reaches a vulnerable decoder. process.versions.brotli still reads 1.2.0 because cfa184a is a master snapshot taken after the fix but before the 1.2.1 release bumped the version macro.
  • Mobile: Expo uses the Java org.brotli:dec decoder (memory-safe). iOS decoding is in the OS.
  • The golang build stage, the nginx runtime and postgres don't decode Brotli request or response bodies.

Verification

  • go build ./..., go vet ./internal/httpapi/... and go test ./... pass. The DB-gated tests skip locally because no DATABASE_URL is set.
  • Docker isn't usable from the dev shell, so I ran the backend image's apk add line in a bwrap sandbox on the sha256-checked Alpine 3.22.7 minirootfs:
    • It resolves nodejs 22.23.4 with process.versions.brotli === "1.1.1", and ldd shows /usr/lib/libbrotlidec.so.1.
    • npm ci --omit=dev succeeds and the Filen helper's 19 tests pass on that Node.
    • Every ffmpeg encoder the backend uses (aac, flac, libvorbis, libmp3lame, libopus, libx264, libwebp) and drawtext with DejaVu work.
  • npm run build in frontend passes on Node 24. The edited mobile test file passes (29 tests).
  • The backend and frontend image jobs only run on pushes to main, so this PR's CI won't build the images. The desktop builder image only runs in release.yml, so it gets exercised on the next desktop release.

After merging

Production needs a manual rebuild on the host, docker compose build backend frontend && docker compose up -d, because up alone reuses the old :local images.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated the Node.js versions used in backend testing and frontend builds.
    • Updated the backend container’s Alpine Linux version and Brotli library.
    • Updated the bundled Filen helper’s documented Node.js requirement.

githubesson and others added 3 commits October 24, 2026 12:31
v1.2.7 ports google/brotli 0ed4268a. The block-switch fast path dropped
decodeBlockTypeAndLength's result, so a crafted stream kept decoding with
stale block state. The module reaches the binary through bogdanfinn/fhttp,
which decodes `br` responses for the Genius lyrics client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…li-libs

Alpine builds nodejs with --shared-brotli, so the Filen helper's axios
downloads decode `br` through the system brotli-libs. Alpine 3.20 is end of
life and stuck on brotli-libs 1.1.0, which CVE-2026-111497 affects. Alpine
3.22 ships the 1.1.1 backport (with nodejs 22.23.4). The apk floor keeps an
older brotli-libs from installing silently, and ffmpeg stays on the same
6.1 line. CI's Filen helper job moves to Node 22 to match the image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node 20 is end of life and gets no Brotli fix. The build stage runs on
the production host during `docker compose build`, so it moves to the
patched 24.22.1, which matches the Node 24 line used in local dev.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-24T12:32:19.421341Z
We couldn’t complete this review. Comment @codex review to try again.
73243b3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 43 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 64e2d82d-c637-47de-befd-b3785886f69b
Commits

Reviewing files that changed from the base of the PR and between 73243b3 and 5c27844.

Files selected for processing: 4
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • mobile/tests/download-store.test.ts
  • scripts/build-desktop-docker.sh

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 479e7ad2-c104-446c-a1e4-a6108012307f
Commits

Reviewing files that changed from the base of the PR and between a4320bf and 73243b3.

Files ignored due to path filters: 1
  • backend/go.sum is excluded by !**/*.sum
Files selected for processing: 6
  • .github/workflows/ci.yml
  • backend/Dockerfile
  • backend/README.md
  • backend/go.mod
  • frontend/Dockerfile
  • mobile/tests/download-store.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The changes update Node.js versions used by backend CI and the frontend build image, revise related version references, and update the backend Alpine base image and Brotli package and Go dependency versions.

Changes: Runtime and Brotli updates

Layer / File(s) Summary
Node.js versions
.github/workflows/ci.yml, backend/README.md, frontend/Dockerfile, mobile/tests/download-store.test.ts
Backend Filen helper CI and its README now specify Node.js 22. The frontend build image uses Node 24.22.1. A mobile test comment states that mobile CI uses Node 20.
Backend Alpine and Brotli versions
backend/Dockerfile, backend/go.mod
The backend runtime image changes to Alpine 3.22 and requires brotli-libs>=1.1.1. The indirect Go Brotli dependency changes to v1.2.7.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: Minimal · up to 73243

The reviewed version updates do not show an actionable merge-blocking risk.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the Brotli security fix and the affected backend and image builds. It matches the primary objective of the changes.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (5 skipped: 5 …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (5 skipped: 5 unsupported.)

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

I’m a rabbit; Node versions hop,
Backend CI moves to twenty-two.
Frontend builds on twenty-four,
Alpine and Brotli versions change.
I twitch my nose and cheer the update.

Comment @coderabbitai help to get the list of available commands.

githubesson and others added 2 commits October 24, 2026 12:43
The core, frontend and mobile CI jobs and the macOS release job still ran
Node 20, which is end of life and gets no CVE-2026-111497 fix. The
self-hosted Linux/Windows desktop build used an electron-builder image
with Node 24.15.0. Every one of them runs `npm ci` or electron-builder
downloads through Node's Brotli decoder, and the self-hosted build holds
publish credentials. The jobs move to Node 22, and the builder image is
re-pinned to today's `wine` build (Node 24.22.1; same Wine layers, with a
refreshed Ubuntu base).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setup-node uses whatever 22.x is in the runner's toolcache unless
check-latest is set. Both the ubuntu-24.04 and macOS 26 images still carry
22.23.3, which predates the CVE-2026-111497 fix, so the major-only bump
did not reach a patched Node. An exact pin matches how the workflows
already pin go-version, and setup-node downloads it from
actions/node-versions on a cache miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@githubesson
githubesson merged commit e00c2fe into main Oct 24, 2026
10 checks passed
@githubesson
githubesson deleted the security/brotli-cve-2026-111497 branch October 24, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant