Skip to content

fix(docker): require the patched Debian Brotli decoder - #6956

Merged
o3LL merged 3 commits into
odysseus-dev:devfrom
RaresKeY:fix/brotli-decoder-security-floor
Oct 29, 2026
Merged

o3LL merged 3 commits into
odysseus-dev:devfrom
RaresKeY:fix/brotli-decoder-security-floor

Conversation

@RaresKeY

@RaresKeY RaresKeY commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

Docker builds can retain an affected native Brotli decoder even when distro Node's own version is unchanged. Check inherited system CA trust before the first HTTPS package request, then refresh libbrotli1 from Debian's signed archives in a shared layer inherited by the wheel builder and runtime, then require Debian's fixed revision, the selected library's package ownership, either exact Debian 12/13 native fix marker spelling and a valid decoding control. Check that distro Node resolves to that same decoder after system dependencies and again after Python dependencies. This accepts Debian's security backports without treating their unchanged upstream version as an affected wheel. The Debian tracker documents the fixed floors and system-library linkage.

Target branch

  • This PR targets the correct integration branch: lab in the private maintainer-preview repository, or dev in the public repository. main remains release-curated.

Linked Issue

Part of #6942

This covers CVE-2026-111497 in the native decoder selected by Debian Node. The Node release line is unchanged.

Type of Change

  • Bug fix (non-breaking — fixes a confirmed issue)
  • New feature (non-breaking — adds new behaviour)
  • Breaking change (changes or removes existing behaviour)
  • Refactor / cleanup (behaviour unchanged)
  • Documentation only
  • CI / tooling / configuration

Checklist

  • I searched open issues and open PRs — this is not a duplicate.
  • This PR targets the correct integration branch (lab in maintainer-preview; dev in the public repository)
  • My changes are limited to the scope described above — no unrelated refactors or whitespace changes mixed in.
  • I actually ran the app (docker compose up or uvicorn app:app) and verified the change works end-to-end. Type-checks and unit tests are not enough.
  • I did not run the app/runtime validation and stated that gap in How to Test. Leave this unchecked when the app-run box above is checked.

How to Test

  1. python3 -m unittest tests.test_brotli_image_gate tests.test_ca_trust -v: 37 tests passed in an isolated container. These cover Debian 12/13 package-version boundaries, both fixed native marker spellings, unpatched and near-match markers, decoding controls, decoder selection and the first Docker RUN stopping before APT when its CA guard fails. Five added regressions cover failed package-query/ldd stderr, warnings from a successful query, non-installed package statuses, and malformed records. A broader local pytest invocation could not collect because that runner has no pytest installation.
  2. Built the actual updated brotli-base target. Its log shows the system CA guard succeeding before the first HTTPS APT fetch, then acceptance of libbrotli1 1.1.0-2+deb13u1. Reproduce with podman build --target brotli-base ..
  3. In a disposable image, injected the public blocked AAA Certificate Services root into the inherited system bundle, then ran the same updated base recipe with networking disabled. It rejected that CA at the guard before source conversion or APT.
  4. Downloaded the exact fixed Bookworm package libbrotli1=1.0.9-2+deb12u1 over HTTPS using signed APT metadata. Extracted its native libraries and loaded them together in a fresh process: error -17 returned exactly b"BLOCK_SWITCH", and the checker accepted that decoder and its valid-stream control. The rebuilt exact runtime system-package recipe also passed with Debian 13's fixed decoder and Node linkage. Run python /usr/local/bin/check-brotli.py --node /usr/bin/node in a built application image to check its effective selection.
  5. Repeated the signed affected Debian 13 package probe using exact libbrotli1=1.1.0-2+b7. Its decoder failed the native fix-marker check, loading it through LD_LIBRARY_PATH failed the package-ownership check, and installing it failed the version-floor check. Updating through signed APT metadata to 1.1.0-2+deb13u1 restored acceptance.
  6. Full application image build and application startup/runtime checks were not run. Package-layer builds and native/library-link checks were run without invoking Node/npm or a paid provider. ARM64 image execution remains untested. GPU Compose variants inherit this Dockerfile; the separate SearXNG recipe only validates an external upstream image and is outside this Debian package change.

Visual / UI changes — REQUIRED if you touched anything that renders

Anything that changes what the UI looks like — buttons, icons, padding, colors, fonts, spacing, layout, CSS, HTML, SVG, or any static/js/ module that draws to the DOM — needs all of the following. PRs that change rendering without these WILL be closed.

  • Screenshot or short clip of the change in the running app, attached below. Mobile screenshot too if the change affects mobile.
  • Style match: the change uses Odysseus's existing visual language. Specifically:
    • Reuse existing CSS variables (--red, --fg, --bg, --card, --border, etc.) — do not introduce new color values, font sizes, or spacing units.
    • Reuse existing button/input/card/border classes. Don't invent parallel styling.
    • No Unicode emoji in UI or code. Use inline SVG (matching the monochrome icon style already in static/index.html) or plain text.
    • Monospaced font (Fira Code) for primary UI text. Don't override.
    • Dark theme is the default; any light-mode work must be wired through the existing theme system, not hard-coded.
  • No new component patterns. If a similar widget already exists in the app, extend it instead of writing a parallel one.
  • I am not an LLM agent submitting a bulk PR. If you are, please open an issue describing the problem first — bulk auto-generated PRs that don't match the project's visual style are closed on sight, even when the underlying fix is correct.

Screenshots / clips

N/A: no UI or rendering changes.

@github-actions

Copy link
Copy Markdown

⚠️ PR description is complete; validation evidence is still outstanding

Changed-file classification: backend/runtime.

Author-reported runtime / visual state

  • The author explicitly reports that app/runtime validation was not performed.

Checkboxes are author attestations. GitHub Actions results remain the execution evidence for CI; this check does not prove that a local command ran.


This comment updates automatically when the description or changed files change.

@github-actions github-actions Bot added the needs runtime validation Runtime validation not attested — tick the app-run box after running it, or state the gap label Oct 28, 2026
Refresh the signed Debian native Brotli package in a shared build-stage base and fail builds below the security backport floor. Verify the actual selected library, native fix marker, valid decoding, and distro Node linkage before accepting the runtime.
Check the inherited system CA store before the shared base makes its first HTTPS package request. Accept the exact fixed Debian 12 marker alongside Debian 13 while retaining package floors, decoder ownership, and valid decoding controls.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The Brotli checker drops subprocess error output and mishandles unexpected package statuses or incomplete records, producing missing or misleading build diagnostics.

2 open issues
What changed in this PR

Hardens Docker images against CVE-2026-111497 by requiring Debian’s patched native Brotli decoder while leaving the Node release line unchanged.

Changes:

  • Adds a shared Brotli package layer for builder and runtime stages, checking inherited CA trust before HTTPS package requests.
  • Validates Debian package revisions, decoder ownership, native fix markers, successful decoding, and Node’s library selection.
  • Adds offline regression coverage for decoder acceptance and Docker guard ordering.
File Description
tests/​test_brotli_image_gate.py Tests package floors, decoder checks, Node linkage, and Docker guard ordering.
scripts/​check_brotli.py Implements Debian package and native decoder acceptance checks.
Dockerfile Shares the guarded Brotli layer and rechecks Node’s decoder after system and Python dependencies.

Review effort: Max

Comment thread scripts/check_brotli.py Resolved
Comment thread scripts/check_brotli.py Resolved

@o3LL o3LL left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed e2b0750 (merge-base 57a1859). Merge probe against current dev (0f996f5) is clean.

The gate looks right to me. Debian's tracker lists 1.0.9-2+deb12u1 / 1.1.0-2+deb13u1 as the fixed floors, and the Node-side upstream change (nodejs/node@a18ec92) only exposes the new error constant, so patching the system libbrotli1 is what actually closes this for Debian Node. I ran Node's regression vector from that commit through Debian Node 20.19.2 on 1.1.0-2+deb13u1 and got ERR__ERROR_FORMAT_BLOCK_SWITCH / -17.

Findings

  • P3 suggestion non-blocking: the -17 error-string check proves the enum entry exists, not that the bounds check is there. By the time it runs the package floor has already passed, so it would be cheap to also stream the public vector from a18ec92 (200010414141fcffff07002228f00b60f7f41f000000) and require BrotliDecoderGetErrorCode() == -17. That turns the marker into a behavioural check without changing what gets accepted today. scripts/check_brotli.py:69-77
  • P3 nit: brotli-base already copies check_ca_trust.py to the same path, so the COPY lines in both child stages are now redundant. Dockerfile:22, Dockerfile:73

Validation

  • Ran: brotli-base built on arm64 and gated 1.1.0-2+deb13u1. check-brotli.py --node /usr/bin/node accepted Debian Node in that image. The new tests passed inside the Debian image (37 OK), and the full suite on macOS adds no failures beyond the merge-base. The Trivy job's amd64 build of this head logs all three gates as accepted.
  • Not run: a full arm64 image build (my local Docker VM ran out of disk), a container boot, Bookworm.
  • Residual risk: zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH stays undefined in Debian Node 20 until Debian rebuilds it. Nothing in the repo reads it, and err.code is still set.

PR Hygiene: targets dev, template complete, both Copilot threads look addressed in e2b0750. Part of #6942 is right, since the issue asks for a Node bump this doesn't do. No overlapping PRs found.

@o3LL
o3LL merged commit 8b8fc62 into odysseus-dev:dev Oct 29, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs runtime validation Runtime validation not attested — tick the app-run box after running it, or state the gap

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants