Skip to content

fix(security): Node 22.23.4 base pinned by digest; protobufjs 6.11.6 - #5

Merged
gascity-infra-agent[bot] merged 1 commit into
gascity-ssofrom
gascity/node-22.23.4-protobufjs
Nov 5, 2026
Merged

gascity-infra-agent[bot] merged 1 commit into
gascity-ssofrom
gascity/node-22.23.4-protobufjs

Conversation

@gascity-infra-agent

@gascity-infra-agent gascity-infra-agent Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

CVEs fixed

  • Node 22.22.3 → 22.23.4:
    • CVE-2026-111497 (Brotli decoder OOB write, CVSS 9.2, CISA KEV since 2026-10-27, due 2026-11-10, actively exploited)
    • CVE-2026-105482 and -106069 (7.4, DNS NUL truncation and TLS hostname verification), CVE-2026-105481, -106072, -106073 and -106074
  • Alpine 3.24.1 → 3.24.3: OpenSSL 3.5.7 → 3.5.9 (CVE-2026-14456 HIGH plus 9 more), musl CVE-2026-107891, zlib CVE-2026-85091
  • protobufjs 6.11.4 → 6.11.6 (via @hyperdx/otel-web-session-recorder): CVE-2026-41242 / GHSA-xq3m-2v4x-88gg. The recorder uses only protobufjs/minimal, so the browser bundle is byte-identical. This clears the lockfile finding.

Change. Both Node stages now use node:22.23.4-alpine3.24@sha256:fe4f6d74…, the multi-arch index digest. A yarn resolution scoped to the recorder; the lockfile diff is 4 lines.

Verified on this tree:

  • yarn install --immutable; api lint 0 errors; tsc clean; jest fork suites 82/82
  • prod image build
  • node --version = v22.23.4; zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH present (the CVE-2026-111497 fix marker)
  • CI symbol guard 10/10
  • smoke harness 76/76: both auth postures 31/31 including the T13–T15 leak matrix, telemetry 4/4, failclosed 3/3, rotation 7/7, seed read back
  • Trivy: 0 OS-package findings (was 23); osv: GHSA-xq3m gone

Rollout.

  1. Merge; gascity-image publishes 2.42.0-gascity-<sha12>.
  2. Repin in internal-services flux/apps/hyperdx/hyperdx.yaml.
  3. Flux rolls it out (Recreate, about 20 s gap).
  4. Check /api/health version and run the §6.3 count-only checks.

Rollback. Repin sha256:b2080937… (2.42.0-gascity-753b16de700f, same app; no probe change).

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

Move both Node stages of docker/hyperdx/Dockerfile (node_base, prod) from
the floating node:22.22-alpine tag (resolved to 22.22.3) to
node:22.23.4-alpine3.24, pinned to the multi-arch index digest
sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914.

Node 22.23.4 fixes CVE-2026-111497 (Brotli decoder out-of-bounds write,
CVSS 9.2, CISA KEV 2026-10-27, actively exploited) and CVE-2026-105481,
-105482, -106069, -106072, -106073, -106074 (HTTP agent reuse, DNS NUL
truncation, TLS hostname verification, crypto caches). The new base also
moves Alpine 3.24.1 -> 3.24.3 (libssl3/libcrypto3 3.5.7 -> 3.5.9, musl,
zlib), clearing 23 OS-package findings including HIGH CVE-2026-14456.

Resolve @hyperdx/otel-web-session-recorder's protobufjs ~6.11.2 to
~6.11.6 (6.11.4 -> 6.11.6) for CVE-2026-41242 / GHSA-xq3m-2v4x-88gg.
The recorder only imports protobufjs/minimal, so the browser bundle is
byte-identical; this clears the lockfile finding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@gascity-infra-agent gascity-infra-agent Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Review (max effort): approve, merging. No HIGH or MED findings. I re-verified everything independently on b9e70a09, from a pristine git archive export, and did not reuse the earlier local evidence.

1. Base image digest. sha256:fe4f6d74…9914 is the official node:22.23.4-alpine3.24 OCI index. I checked it four ways:

  • crane digest resolves 22.23.4-alpine3.24 to it. It is also the current 22.23.4-alpine, 22.23-alpine3.24 and 22-alpine3.24.
  • Registry API: the Docker-Content-Digest header matches, and so does the SHA-256 recomputed over the raw index bytes.
  • Docker Hub API: the digest field matches (updated 2026-10-22).
  • docker-library repo-info and official-images library/node: the tag maps to GitCommit 80f02a54, 22/alpine3.24, for the same 5 platforms.

The amd64 SLSA provenance names nodejs/docker-node@80f02a54:22/alpine3.24, built by the docker-library builder. That Dockerfile installs Node from the GPG-verified nodejs.org/dist linux-x64-musl tarball.

Both Node stages (node_base, prod) use the pin. Nothing else in the Dockerfile changed, and no script passes NODE_VERSION as a build arg.

2. Node 22.22.3 → 22.23.4. This crosses five releases, not one: 22.23.0 (security, semver-minor), 22.23.1, 22.23.2 (security), 22.23.3, and 22.23.4 (security). The floating 22.22 tag stopped at 22.22.3, so the deployed image is also missing the 22.23.0 (11 CVEs) and 22.23.2 (10 CVEs) fixes, and this PR closes those too.

The 22.23.4 CVE list in the description matches the October advisory for 22.x exactly (7 CVEs, including CVE-2026-106072).

Reviewed for impact on HyperDX:

  • TLS client changes. These are hostname normalization, non-canonical-IP rejection, session binding to the authenticated host, X.509/SAN hardening, the SPKI blocklist (8 eMudhra intermediates), and Node's bundled roots going from 145 to 116.
    • The roots dropped by NSS 3.123.1 / 3.125 / 3.131.1 include DigiCert Global Root CA, Assured ID, High Assurance EV, COMODO, QuoVadis 2/3, GTS R2, Entrust and emSign.
    • Against our deployment manifests (private), none of these are on the deployed request paths.
    • If an HTTPS egress (alert webhooks, a TLS ClickHouse) is opened later, check that endpoint's chain against Node's bundled roots, or set NODE_EXTRA_CA_CERTS.
  • http.Agent. 22.23.0 added an idle-socket data guard, 22.23.1 fixed its regression, and 22.23.4 adds a pending-read check before reuse. These only protect, and the smoke run exercises them on the Next → API proxy hop.
  • llhttp 9.3.1 → 9.4.3. Stricter only: CRLF is required in the status line, and an empty Transfer-Encoding is rejected.
  • Smaller tightenings. More than 2000 request headers now returns 431. zlib throws on out-of-bounds buffers instead of aborting. DNS rejects NUL bytes.
  • undici 6.24.1 → 6.29.1. These are security fixes. The API's fetch() connection test was exercised end to end.
  • http2 priority-signaling removal (SEMVER-MAJOR, landed in 22.23.0). It now warns and is a no-op, and HyperDX's production paths don't use http2.
  • V8 12.4.254.21, libuv 1.51.0 and ABI 127 are unchanged.

3. protobufjs. The resolution is scoped correctly.

  • @hyperdx/otel-web-session-recorder@0.16.2 is the only dependent on ~6.11.2. The three 7.x descriptors are untouched.
  • The 6.11.4 → 6.11.6 tarball diff changes only src/type.js (one line), plus the dist banners.
  • The recorder's dist/ imports only protobufjs/minimal, whose source closure is unchanged.
  • In the built image, all 76 .next/static chunk files are byte-identical to the deployed image, and so is all of node_modules. 6.11.x is not shipped.

4. The #4 patch is intact. All patched API files, both workflows and gascity/smoke are blob-identical to 753b16de.

Re-run here:

  • Pristine tree in node:22.23.4-alpine3.24@sha256:fe4f6d74… (the gascity-pr steps):
    • yarn install --immutable.
    • API lint: 0 errors (301 warnings, within the budget of 302).
    • tsc --noEmit: clean.
    • jest: 4 suites, 82/82.
    • API build plus the artifact symbol guard: 10/10.
  • Fresh prod image (--no-cache --pull --target prod, CI build contexts):
    • node --version v22.23.4, bundled OpenSSL 3.5.9, Alpine 3.24.3 (libssl3 3.5.9).
    • zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH = -17 (undefined on the deployed image), and a Brotli round-trip works.
    • The gascity-image symbol guard passes 10/10.
  • Smoke harness on the fresh image: 107/107, 0 failures.
    • auth in three postures, 31/31 each: default, production (SMOKE_TELEMETRY=off), and trace with OTEL_LOG_LEVEL=debug.
    • telemetry 4/4, failclosed 3/3, rotation 7/7, and the seeded state read back across containers.
    • In every container: /ready 200, 0 restarts, 0 deprecation warnings, 0 TLS errors, and 0 secret hits in the full logs.
  • A/B: the same driver against the currently deployed image produces identical outcome lines (107/107).

Non-blocking (LOW):

  • (a) The description doesn't mention the 22.23.0 and 22.23.2 CVEs it also fixes.
  • (b) An ARG-based FROM ...@${NODE_DIGEST} isn't visible to Dependabot version updates, so the next Node security release needs a manual bump. The old floating 22.22 tag went stale the same way.
  • (c) A parent/dep resolution applies to every recorder version. Remove it once the recorder moves off protobufjs 6.x; a resolutionsNotes entry would record that.
  • (d) Pre-existing upstream behavior, identical on the deployed image: pages/api/[...all].ts builds an http-proxy-middleware instance per request, and each one adds a close listener to the Next server (MaxListenersExceededWarning). This is tracked separately.

Merging with a merge commit (the gascity-sso convention). The required auth quality gate is green on b9e70a09.

@gascity-infra-agent
gascity-infra-agent Bot merged commit ab064cc into gascity-sso Nov 5, 2026
5 checks passed
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