From c66501c173400a73a1d738f181a0d3b2687380fc Mon Sep 17 00:00:00 2001 From: esson <55195336+githubesson@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:31:54 +0000 Subject: [PATCH 1/5] Backend: andybalholm/brotli v1.2.7 for CVE-2026-111497 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 --- backend/go.mod | 2 +- backend/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 0d8b34b9..08f122b4 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -21,7 +21,7 @@ require ( ) require ( - github.com/andybalholm/brotli v1.2.0 // indirect + github.com/andybalholm/brotli v1.2.7 // indirect github.com/bdandy/go-errors v1.2.2 // indirect github.com/bdandy/go-socks4 v1.2.3 // indirect github.com/bogdanfinn/quic-go-utls v1.0.9-utls // indirect diff --git a/backend/go.sum b/backend/go.sum index f1aeab7c..2b1742ac 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -2,8 +2,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/andybalholm/brotli v1.2.7 h1:UgafRPGMam5ySHfRkhthMqOFA6IFoeF3D7mWIDLjG5A= +github.com/andybalholm/brotli v1.2.7/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q= github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic= From 59d744fbeae8594c8bf493a8fc39f9304adff9d4 Mon Sep 17 00:00:00 2001 From: esson <55195336+githubesson@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:31:54 +0000 Subject: [PATCH 2/5] Backend image: Alpine 3.22 so Node decodes Brotli with a patched brotli-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 --- .github/workflows/ci.yml | 4 ++-- backend/Dockerfile | 7 +++++-- backend/README.md | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9b604e4..91b7c58a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,10 +74,10 @@ jobs: - run: go build ./... - run: go vet ./... - run: go test ./... - # The Filen helper ships in the backend image (Alpine's Node 20). + # The Filen helper ships in the backend image (Alpine's Node 22). - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "20" + node-version: "22" cache: npm cache-dependency-path: backend/filen-downloader/package-lock.json - name: Filen helper tests diff --git a/backend/Dockerfile b/backend/Dockerfile index da3ae115..4d4422bd 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -5,8 +5,11 @@ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/server ./cmd/server -FROM alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc -RUN apk add --no-cache ca-certificates ffmpeg font-dejavu nodejs npm tzdata \ +FROM alpine:3.22@sha256:2e0b81d60597f449baa56dc25d355367869d2dca4cd57e866f1e2b52af3eb55f +# Alpine's nodejs decodes Brotli through the shared brotli-libs, so the floor +# below is what keeps the Filen helper off decoders hit by CVE-2026-111497 +# (fixed upstream in 1.2.1, and in the 1.1.1 backport Alpine 3.22 ships). +RUN apk add --no-cache ca-certificates ffmpeg font-dejavu nodejs npm tzdata 'brotli-libs>=1.1.1' \ && addgroup -S app \ && adduser -S -G app -h /app app WORKDIR /app diff --git a/backend/README.md b/backend/README.md index 9c994fda..35e820dd 100644 --- a/backend/README.md +++ b/backend/README.md @@ -48,7 +48,7 @@ internal/ lastshare/ apitracker/ pinscan/ musicroots/ admin-managed scan roots fonts/ fonts bundled for preview image rendering -filen-downloader/ Node 20 helper the Docker image bundles for Filen links +filen-downloader/ Node 22 helper the Docker image bundles for Filen links ``` ## Run (local) From 73243b38c1c81c510e44fb79f4b47b6f506129b1 Mon Sep 17 00:00:00 2001 From: esson <55195336+githubesson@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:31:54 +0000 Subject: [PATCH 3/5] Frontend image: build on Node 24.22.1 for CVE-2026-111497 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 --- frontend/Dockerfile | 2 +- mobile/tests/download-store.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 54fe7b18..9aa7299f 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS build +FROM node:24.22.1-alpine@sha256:06d7adf3e0ef61a13f1d28682726366f97a15aa1888c934d3ecec86c33918b53 AS build WORKDIR /app # Core must land at /core: the app's package.json, tsconfig paths, and Vite # aliases all reference it as ../core relative to /app. diff --git a/mobile/tests/download-store.test.ts b/mobile/tests/download-store.test.ts index 5e0c9a30..9d7333e1 100644 --- a/mobile/tests/download-store.test.ts +++ b/mobile/tests/download-store.test.ts @@ -232,7 +232,7 @@ async function finishTask( h.files.set(partUri(id), bytes); task.cbs.begin?.({ headers: { "content-type": contentType } }); // Hand-rolled deferred rather than Promise.withResolvers, which needs - // Node 22 — CI and both Docker images run Node 20. + // Node 22 — the mobile CI job runs Node 20. let resolve!: () => void; const promise = new Promise((res) => { resolve = res; From 06cc5e59094426942bbdfbb7c654d30e88596e64 Mon Sep 17 00:00:00 2001 From: esson <55195336+githubesson@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:43:51 +0000 Subject: [PATCH 4/5] CI and desktop builds: off Node lines without the Brotli fix 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 --- .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 2 +- mobile/tests/download-store.test.ts | 4 ++-- scripts/build-desktop-docker.sh | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91b7c58a..d2d59fa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "20" + node-version: "22" cache: npm cache-dependency-path: core/package-lock.json - run: npm ci @@ -112,7 +112,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "20" + node-version: "22" cache: npm cache-dependency-path: | frontend/package-lock.json @@ -178,7 +178,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "20" + node-version: "22" cache: npm cache-dependency-path: mobile/package-lock.json # `npm ci`'s postinstall runs sync:core, which regenerates the vendored diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc1b26c3..7e743612 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -143,7 +143,7 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "20" + node-version: "22" cache: npm cache-dependency-path: | frontend/package-lock.json diff --git a/mobile/tests/download-store.test.ts b/mobile/tests/download-store.test.ts index 9d7333e1..1e63c8cd 100644 --- a/mobile/tests/download-store.test.ts +++ b/mobile/tests/download-store.test.ts @@ -231,8 +231,8 @@ async function finishTask( h.dirs.add(`${h.DOC}/offline-audio-v2/https_3A_2F_2Fapi.test/alice`); h.files.set(partUri(id), bytes); task.cbs.begin?.({ headers: { "content-type": contentType } }); - // Hand-rolled deferred rather than Promise.withResolvers, which needs - // Node 22 — the mobile CI job runs Node 20. + // Hand-rolled deferred rather than Promise.withResolvers, which Node 20 + // (still allowed for local runs) lacks. let resolve!: () => void; const promise = new Promise((res) => { resolve = res; diff --git a/scripts/build-desktop-docker.sh b/scripts/build-desktop-docker.sh index f3194812..d8ed0f7f 100755 --- a/scripts/build-desktop-docker.sh +++ b/scripts/build-desktop-docker.sh @@ -19,8 +19,9 @@ docker info >/dev/null repo_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) builder_home="${XDG_CACHE_HOME:-$HOME/.cache}/lumen-desktop-builder" mkdir -p "$builder_home" -# electronuserland/builder:wine, pinned so toolchain updates are intentional. -builder_image='electronuserland/builder@sha256:41ae540902461b6cbc988987db79547fcc10cda04d2a6c6367504f59d4b37c64' +# electronuserland/builder:wine (Node 24.22.1), pinned so toolchain updates +# are intentional. +builder_image='electronuserland/builder@sha256:f3a8d451994c881d0972e7b1846ee4d4fdc15eb41fc6b1f350b38cbf99ca90fe' publish_env=() if [[ $publish_mode == always ]]; then publish_env+=(--env GH_TOKEN) From 5c2784477ed75d94c4f1272d6a0b837ab897745a Mon Sep 17 00:00:00 2001 From: esson <55195336+githubesson@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:48:48 +0000 Subject: [PATCH 5/5] CI: pin Node 22.23.4, since "22" resolved to the runner's cached 22.23.3 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 --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2d59fa6..ad8a6ca9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: # The Filen helper ships in the backend image (Alpine's Node 22). - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: backend/filen-downloader/package-lock.json - name: Filen helper tests @@ -95,7 +95,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: core/package-lock.json - run: npm ci @@ -112,7 +112,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: | frontend/package-lock.json @@ -138,7 +138,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: | frontend/package-lock.json @@ -158,7 +158,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" # Wrangler and ESLint 10 need Node ^22.13 || >=24 + node-version: "22.23.4" # Wrangler and ESLint 10 need Node ^22.13 || >=24 cache: npm cache-dependency-path: landing/package-lock.json - run: npm ci @@ -178,7 +178,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: mobile/package-lock.json # `npm ci`'s postinstall runs sync:core, which regenerates the vendored diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e743612..b9f943b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -143,7 +143,7 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: "22" + node-version: "22.23.4" cache: npm cache-dependency-path: | frontend/package-lock.json