Skip to content

RFC: rebuild agent images for Node.js security releases (Node 22.23.4 / 24.22.1) #1567

Open

Description

@antigenius0910

Summary

The Node.js security release of 2026-10-21 fixes CVE-2026-111497, a Brotli decoder out-of-bounds write that permits remote code execution, along with other issues. The fixed versions are 22.23.4 and 24.22.1. No released openab agent image includes these versions yet: stable-<agent> has Node 22.23.1, and beta-<agent> has 22.23.3. The stable-* images also missed the earlier security release (22.23.2, 2026-07-28).

The problem is in how images are published. The Node base image is a floating tag, but stable releases are re-tagged instead of rebuilt, and no workflow rebuilds release images when the base image changes.

This RFC proposes:

  1. Ship patched images now.
  2. Pin the Node version with a build ARG.
  3. Add a daily check that opens an issue when a newer Node security release exists.
  4. Add a release-note template for these releases.

The exposure in openab is unclear and possibly low (see Exposure). The fix costs only a rebuild.

Motivation

Users who pull the newest openab release or beta tag today get a Node.js runtime that has known High-severity CVEs, and nothing in the current release flow will change that until the next minor release. The sections below show the advisory, which images are affected, what is published, and why.

Advisory

From the Node.js October 2026 security releases, these High-severity issues affect 22.x and 24.x:

CVE Severity Title
CVE-2026-111497 High Node.js Brotli decoder out-of-bounds write permits remote code execution
CVE-2026-106069 High TLS certificate hostname bypass when the host loosely resolves to an IP address
CVE-2026-105482 High dns.resolve truncates names containing NUL char

Fixed versions: 22.23.4, 24.22.1, 26.12.1.

Which images contain Node

  • node:22-trixie-slim (base-node): claude, codex, copilot, gemini, mimocode, opencode, pi
  • node:24-trixie-slim: kimi
  • No Node: kiro, cursor, grok, devin, antigravity, native, agentcore, gateway

What is published

Measured on 2026-10-22 by running node --version in each image (linux/amd64, fresh anonymous pull). The results match the NODE_VERSION value in each image config. All agents in a row show the same version.

Tag (ghcr.io/openabdev/openab) Source Built Node Fixed
stable-<agent>, 0.9-<agent>, 0.9.0-<agent> v0.9.0 (promoted from beta.12) 2026-07-19 22.23.1 no
beta-<agent>, 0.10.0-beta.5-<agent> v0.10.0-beta.5 2026-09-26 22.23.3 no
pre-beta-<agent> main 2026-10-03 22.23.3 no
nightly-<agent> main + vendor-latest CLIs 2026-10-22 22.23.4 yes
beta-kimi, pre-beta-kimi (no stable-kimi) 24.21.0 no
nightly-kimi 2026-10-22 24.22.1 yes

The deprecated per-agent repositories (openab-claude, openab-codex, openab-gemini, openab-copilot, openab-opencode, openab-pi, openab-mimocode) were last built on 2026-06-21 on Debian bookworm with Node 22.23.0. No release workflow builds them anymore, and their stable and latest tags point at 0.8.5.

Why released images go stale

  1. FROM node:22-trixie-slim floats, so the Node version changes only on rebuild.
  2. promote-stable in build-operator.yml re-tags the last beta and does not rebuild. This is by design ("what you tested is what you ship"). As a result, stable-<agent> keeps that beta's Node version until the next stable release.
  3. No workflow rebuilds release images when the base image changes. nightly-build.yml rebuilds daily with no-cache and picked up 22.23.4 within a day, but nightly is not a release channel.
  4. There is no Dependabot or Renovate configuration.

Exposure

Unclear, and possibly low. This RFC does not demonstrate an exploit path.

  • No Node server on a network port by default. openab and the gateway are Rust. Neither the Dockerfiles nor the chart start a Node server on a network port, so the scenario "server accepts Brotli request bodies" does not apply to a default deployment. A user-configured MCP server that uses the HTTP transport could change this.

  • Node processes are mostly outbound clients. The npm metadata of the pinned versions shows which CLIs run on the image's Node:

    Agent Runs on the image's Node
    claude claude-agent-acp (JS). Claude Code itself ships as a native binary.
    gemini, pi (+ pi-acp), kimi Yes (JS bundles)
    codex, copilot, opencode, mimocode Only a JS launcher. The CLI is a per-platform native binary.
    MCP servers started with npx Yes
  • Possible client-side path (not verified). Node's fetch decompresses Content-Encoding: br responses automatically. A JS agent that fetches an attacker-chosen URL may therefore decode attacker-controlled Brotli data. Whether CVE-2026-111497 is reachable this way has not been verified.

  • TLS hostname bypass. CVE-2026-106069 affects outbound HTTPS from the same processes. An attacker needs a position on the network path to use it.

Proposal

1. Immediate: ship patched images

  • Beta: Cut 0.10.0-beta.6 from main through the normal Release PR flow. The floating base already resolves to 22.23.4 / 24.22.1. Verify node --version on beta-<agent>. If the Actions cache returns old layers, re-run with gh workflow run build-operator.yml -f tag=v0.10.0-beta.6 -f no_cache=true.
  • Stable: Choose one option:
    • (a) 0.9.1, with no code changes. Branch from v0.9.0, bump Cargo.toml and Chart.yaml, then push the tags v0.9.1-beta.1 and v0.9.1 by hand. release-pr.yml and tag-on-merge.yml support main only.
    • (b) Promote 0.10.0 after beta.6 is tested. Users on stable-<agent> move to a new minor version.
  • Not rebuilt: 0.8.x, 0.9.0-beta.x, and the deprecated per-agent repositories. The release note tells users of those tags to migrate.
  • Version tags stay immutable. Do not overwrite existing version tags such as 0.9.0-claude.

2. Pin the Node version with a build ARG

ARG NODE22_VERSION=22.23.4
ARG NODE24_VERSION=24.22.1
FROM node:${NODE22_VERSION}-trixie-slim AS base-node
FROM node:${NODE24_VERSION}-trixie-slim AS kimi

Apply this to Dockerfile.unified, Dockerfile.package, and the per-agent Dockerfiles. Extend the smoke tests to:

  • assert that node --version matches the ARG, the same way the tests already read the CLI version ARGs;
  • assert that the ARG has the same value in every file, the same way they already do for KIRO_CLI_VERSION.
Float (today) Pin version tag (proposed) Pin digest
Node version visible in the repo no yes yes
Debian fixes picked up on rebuild yes yes no
Reproducible no Node only yes
Upkeep none one-line PR per Node release PR per upstream image rebuild

The pin alone does not fix the stale-stable problem. It makes the Node version explicit and checkable, and the bump PR records the change.

3. Prevention: daily node-security-check.yml

A daily workflow (also runnable with workflow_dispatch), about 60 lines of bash and jq. It needs only GITHUB_TOKEN with issues: write.

  1. Read NODE22_VERSION and NODE24_VERSION from Dockerfile.unified.
  2. Fail if https://nodejs.org/dist/index.json lists a newer release with security: true in the same major version.
  3. Read NODE_VERSION from the configs of the published stable-<agent> and beta-<agent> images, using an anonymous registry request without pulling the images. Report every image that is behind. This catches the current case, where main is fixed but the released tags are not.
  4. Open an issue, or update the existing one, with the advisory link and the list of stale tags.

Non-security Node patch releases do not trigger it.

Dependabot is not part of this proposal. It cannot parse an ARG-templated FROM line, and it does not cover the release step, which is where the gap is.

4. Runbook and release-note template

Add a "Base image security release" section to RELEASING.md. The steps are: the check opens an issue, a PR bumps the ARG, the next beta is cut, Node is verified in beta-<agent>, the stable path from step 1 is followed, and the note is posted. In the same PR, fix the outdated references in RELEASING.md: build.yml is now build-operator.yml, and the image list still names the per-agent repositories.

Release-note template:

### Security: Node.js base image update (<22.x.y> / <24.x.y>)

This release rebuilds the Node-based agent images to include the Node.js <Month YYYY> security release: <advisory URL>. <There are no openab code changes. | Other changes: ...>

**Rebuilt:** `openab:<version>-<agent>` for claude, codex, copilot, gemini, kimi, mimocode, opencode, pi. The kiro, cursor, grok, devin, antigravity, native, and agentcore images do not contain Node.

**Floating tags now point at the fixed build:** `stable-<agent>`, `<major.minor>-<agent>`, `beta-<agent>`.

**Not rebuilt (still vulnerable):** `openab:<previous versions>-<agent>` and every tag in the deprecated per-agent repositories (`openab-claude`, ...). To migrate, see docs/image-tags.md.

**Exposure in openab:** <one or two sentences; say what is verified and what is not>

**Verify:** `docker run --rm --entrypoint node ghcr.io/openabdev/openab:<tag> --version`

Alternatives considered

  • Rebuild and overwrite existing version tags. Rejected. It breaks pinned deployments and the rule "what you tested is what you ship".
  • Point deprecated repository tags (openab-claude:stable) at the new unified images. Rejected. Users would move from 0.8.5 to 0.9.x or 0.10 without notice.
  • Scheduled automatic rebuild and republish of stable. Rejected. It publishes untested builds under stable tags, and nightly already serves as the rebuild lane.
  • Dependabot or Renovate. Not proposed now, for the reasons in Proposal §3. Renovate with a regex manager could be added later next to the check.

Rollout

Steps 1–3 do not depend on steps 4–6.

  1. Cut 0.10.0-beta.6 and verify Node in beta-<agent>.
  2. Decide the stable path (option a or b) and ship it.
  3. Post the security note.
  4. Open a PR for the Node ARG pin and the smoke-test assertions.
  5. Open a PR for node-security-check.yml.
  6. Open a PR for the RELEASING.md runbook and the outdated-reference fixes. If maintainers want an ADR in docs/adr/, a draft is ready.

Open questions

  1. Support policy. There is no SECURITY.md and no list of supported lines. Is 0.9 still supported now that 0.10 has been in beta since July? The answer decides between option (a) and option (b).
  2. Patch releases outside main. Is it acceptable to push tags by hand from a v0.9.0 branch? The OCI chart is published by build-operator.yml, but the GitHub Pages index (release.yml) is updated only on pushes to main.
  3. beta.6 contents. beta.6 includes everything merged since beta.5, including the breaking cron change (fix(cron)!: treat blank thread_id/id as unset; document thread_id/id behavior #1564). Is that acceptable for a security-motivated release?
  4. Deprecated repositories. Add a deprecation notice, or remove them?
  5. Nightly. Should nightly follow the pinned Node version or keep floating?
  6. Reachability. Can someone confirm or rule out the fetch Brotli path for the JS agents?

摘要(繁體中文)

Node.js 在 2026-10-21 發布 security release,修補了 Brotli decoder 的 RCE 漏洞(CVE-2026-111497)等問題,修正版本是 22.23.4 和 24.22.1。

實測結果:stable-*(Node 22.23.1)和 beta-*(22.23.3)都還沒修補,只有 nightly-* 已經更新。原因是 stable 只重新貼 tag、不會 rebuild,而且 Node base image 有新版時沒有任何機制觸發重建。舊的 openab-claude 等 per-agent repo 已經淘汰,之後也不會再更新。

影響評估:預設部署沒有 Node server 開放對外 port,實際風險不明,可能偏低。

提案:

  1. 立即發布 0.10.0-beta.6;stable 走 0.9.1 或 0.10.0。
  2. 用 build ARG 釘住 Node 版本。
  3. 每天檢查 Node security release,版本落後時自動開 issue。
  4. 提供 release note 範本。

歡迎討論。

Activity

  1. chaodu-obk commented on Oct 22, 2026

    @chaodu-obk
    Contributor

    Thanks for the thorough RFC. The measurements and root-cause analysis are clear. Maintainer decisions below (confirmed by @pahud):

    Decisions

    Question Decision
    Support policy (Q1) 0.9 is no longer supported. Only the latest minor line is supported; no backports.
    Stable path Option (b). Cut 0.10.0-beta.6, verify node --version on beta-<agent> (22.23.4 / kimi 24.22.1), then promote to 0.10.0. Target: promote within about one week after beta.6 is verified, so stable-* does not sit on 22.23.1 longer than necessary.
    Patch outside main (Q2) Moot. No v0.9.x branch.
    beta.6 contents (Q3) Accepted. beta.6 / 0.10.0 ship with the #1564 cron breaking change. The release note must have a separate migration section: check cronjob.toml for usercron ids that are equal after trimming and make them unique before upgrading. Stable users jump from 0.9 to 0.10 in one step, so this needs to be prominent.
    Deprecated per-agent repos (Q4) Delete them, after a notice period. Announce in the release note and repo/package READMEs first, then delete after a 2–4 week grace period. Deleting a GHCR package is irreversible, and deployments still pulling openab-claude:stable etc. will hit ImagePullBackOff on the next pull. Deletion is done by an org admin.
    Nightly (Q5) Keep floating. Nightly acts as the canary (it picked up 22.23.4 first). The smoke-test assertion "node --version == ARG" must be skipped or warn-only on nightly builds.
    SECURITY.md Yes. Add a SECURITY.md stating the policy above, in the same PR as the RELEASING.md runbook.

    Left to PR review (no decision needed)

    • node-security-check.yml: we suggest opening/updating a single labeled issue rather than failing the workflow daily (a red check every day gets ignored). Cover both the 22 and 24 (kimi) lines.
    • Q6 (fetch Brotli reachability): would be good to confirm, but it does not block the release.

    Next steps

    1. Cut 0.10.0-beta.6 and verify Node in beta-<agent>.
    2. Promote 0.10.0; post the security + breaking-change release note (include the per-agent repo deprecation notice and deletion date).
    3. PRs: Node ARG pin + smoke tests, node-security-check.yml, RELEASING.md runbook + SECURITY.md + outdated-reference fixes.

    @antigenius0910 the PRs for step 3 are yours if you'd like to take them. Thanks!

  2. chaodu-obk commented on Oct 23, 2026

    @chaodu-obk
    Contributor

    Status update and dates (confirmed by @pahud):

    Step 1 — 0.10.0-beta.6: released via #1568 (tag v0.10.0-beta.6). Image builds are in progress. Node versions in beta-<agent> (22.23.4, kimi 24.22.1) will be verified from the published image configs once the build completes, and reported here.

    Step 2 — dates:

    Milestone Date
    Promote 0.10.0 (stable) from beta.6, if no blocker is found 2026-10-30
    Delete the deprecated per-agent repositories (openab-claude, openab-codex, openab-gemini, openab-copilot, openab-opencode, openab-pi, openab-mimocode) 2026-11-30

    The 0.10.0 release note will carry the security section, the #1564 migration step, and the deletion notice with this date. Users still on ghcr.io/openabdev/openab-<agent> should migrate to ghcr.io/openabdev/openab:<tag>-<agent> before then (see docs/image-tags.md).

    Step 3 — PRs: @antigenius0910, the three PRs (Node ARG pin + smoke tests, node-security-check.yml, RELEASING.md runbook + SECURITY.md) are still yours if you want them. Please reply here by 2026-10-24 end of day (UTC). If we don't hear back by then, maintainers will pick them up. No problem either way.

  3. chaodu-obk commented on Oct 23, 2026

    @chaodu-obk
    Contributor

    0.10.0-beta.6 Node verification: passed. The Build Operator run completed successfully: https://github.com/openabdev/openab/actions/runs/39883871537

    NODE_VERSION was read from the published image configs through an anonymous GHCR request, for both linux/amd64 and linux/arm64:

    Tag amd64 arm64
    beta-claude, 0.10.0-beta.6-claude 22.23.4 22.23.4
    beta-codex 22.23.4 22.23.4
    beta-copilot 22.23.4 22.23.4
    beta-gemini 22.23.4 22.23.4
    beta-mimocode 22.23.4 22.23.4
    beta-opencode 22.23.4 22.23.4
    beta-pi 22.23.4 22.23.4
    beta-kimi, 0.10.0-beta.6-kimi 24.22.1 24.22.1
    beta-kiro (control, no Node) — —

    All images were built on 2026-10-23, between 01:38 and 01:40 UTC. No no_cache re-run was needed.

    stable-* stays on Node 22.23.1 until 0.10.0 is promoted, planned for 2026-10-30.

  4. chaodu-obk commented on Oct 23, 2026

    @chaodu-obk
    Contributor

    pre-beta-* and downstream openab-pty images are now on the patched Node. Each tag was checked for both linux/amd64 and linux/arm64.

    Image Node 22 agents (claude, codex, copilot, gemini, mimocode, opencode, pi) kimi Run
    openab:pre-beta-<agent> 22.23.4 24.22.1 Pre-Beta Build (manual, all agents)
    openab-pty:pre-beta-<variant> 22.23.4 — (no variant) image.yml
    openab-pty:nightly-<variant> 22.23.4 — (no variant) nightly-images.yml

    Before this, openab:pre-beta-* had not been rebuilt since 2026-10-03 (99c11ec). openab-pty builds every image FROM openab:pre-beta-<variant>, including nightly, so all of its images were on 22.23.3 as well. The root cause is the pre-beta schedule gate, which compared against a 70-minute window while GitHub delivers the hourly schedule only every ~4–5 hours. The fix is in #1573. The daily check in #1571 should cover these channels too (see #1571 (comment)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions