Skip to content

🤖 tests: move the bug-bash sandbox image to Node 22.23.4 (CVE-2026-111497) - #7362

Merged
ibetitsmike merged 1 commit into
mainfrom
mike/bugbash-sandbox-node-22.23.4
Oct 23, 2026
Merged

ibetitsmike merged 1 commit into
mainfrom
mike/bugbash-sandbox-node-22.23.4

Conversation

@ibetitsmike

Copy link
Copy Markdown
Contributor

Summary

The bug-bash sandbox image pins node:22-slim by digest, and that digest is Node 22.23.3, which is affected by CVE-2026-111497 (crafted Brotli data runs code). This moves the base to the node:22.23.4-slim index digest (sha256:de170b39…, Node 22.23.4 on amd64 and arm64). The sandbox runs the seeded xum server on this Node.

Background

#7359 already makes the server refuse compressed request bodies on any Node, and the sandbox has no network, so the remaining risk in the sandbox is small. The bump keeps test infrastructure off an affected Node, like the release image.

Landing

A change to the Dockerfile changes the sandbox inputs key, so it lands in three steps: this PR, a run of the "Bug-bash sandbox image" workflow on main, then a PR that pins the published digest in image.json.

make test-stories refuses a stale key for model-driven jobs (tests/bugbash/sandbox/launch.ts), and both Test / Stories (merge queue) and Test / Stories (Haiku) (PR pushes) now count in Required (STORIES_MODE and STORIES_PR_MODE are block). The earlier bumps (#7030 with #7034, #6547 with #6623) landed before those gates blocked. Until the image.json PR merges, story jobs on this PR and on every merge-queue entry refuse the stale key. This PR stays a draft until that is settled.

Validation

  • tests/bugbash/sandbox/build.sh built the image locally; node -v in it prints v22.23.4, and its org.xum.bugbash.inputs label is the new key 2bf9e12d3ac7dcb10397698df2792bf1b244b3fcbe68225378302ef695ba302a.
  • make static-check (including hadolint) passes.

@ibetitsmike
ibetitsmike marked this pull request as ready for review October 23, 2026 18:35
@ibetitsmike
ibetitsmike merged commit 2e2a2b7 into main Oct 23, 2026
41 of 42 checks passed
@ibetitsmike
ibetitsmike deleted the mike/bugbash-sandbox-node-22.23.4 branch October 23, 2026 18:35
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

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 ✅ Completed 2026-10-23T18:36:41.249551Z 3dd7914 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-23T18:38:19.843902Z 3dd7914 Draft marked ready
ℹ️ 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.

@github-actions

Copy link
Copy Markdown
Contributor

No job left output: make test-stories stopped before its first job. See the run log.

@ThomasK33

Copy link
Copy Markdown
Member

The merge queue has been blocked for every PR since this PR merged at 18:35Z. Each merge group fails Test / Stories with:

sandbox refused: stale image: image.json has inputs key 08a81da3ddeb1ec1, this checkout 2bf9e12d3ac7dcb1

(for example run 40012690165, #7364). The queue is empty now: #7360 and #7364 were dequeued for this.

The two follow-up steps from this PR's body have not run yet:

  1. Run the "Bug-bash sandbox image" workflow (workflow_dispatch) on main. The only run so far is the pull request run 39993637913 on the branch, which does not publish.
  2. Open a PR that pins the new digest from that run's job summary in tests/bugbash/sandbox/image.json.

@ibetitsmike, are you doing these? If not, the issue coordinator desk will do them once the maintainer approves the publish.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

azaj01 pushed a commit to azaj01/mux that referenced this pull request Oct 25, 2026
…2.23.4 sandbox image (coder#7369)

## Summary

This PR fixes the two problems that keep main red, in one PR, because
each fix fails CI without the other. The first commit is Thomas's fix
from coder#7364 (cherry-picked, his authorship kept). It moves the
ask_user_question argument schemas out of `toolDefinitions.ts`, so the
renderer entry chunk no longer pulls in every tool schema and
`rendererFirstLoad.test.ts` passes again. The second commit pins the
bug-bash sandbox image that main published from coder#7362 (base
`node:22.23.4-slim`, the CVE-2026-111497 fix), so the model-driven story
jobs run again on main and in the merge queue. Fixes coder#7363.

## Background

- coder#6887 made `openAskUserQuestions.ts`, which the eager
`StreamingMessageAggregator` loads, import `TOOL_DEFINITIONS`. That
moved `toolDefinitions.ts` and its imports into `main-*.js` (+92 KB raw,
+23.7 KB br) and fails `Test / Unit (5/6)`: "renderer first load > the
entry's static imports do not reach the tool definitions module".
- coder#7362 changed the sandbox Dockerfile, so the inputs key on main is now
`2bf9e12d…`, but `image.json` still pins the old image. `make
test-stories` refuses the old record ("sandbox refused: stale image:
image.json has inputs key 08a81da3ddeb1ec1, this checkout
2bf9e12d3ac7dcb1"). `Test / Stories` and `Test / Stories (Haiku)` count
in `Required` (`STORIES_MODE` and `STORIES_PR_MODE` are `block`), so the
queue removed coder#7360 and coder#7364 on that failure.

So coder#7364 alone fails the story jobs, and the pin alone fails the unit
test. The queue groups entries with `ALLGREEN`, so neither can merge
first. This PR carries both.

## Implementation

- `perf: keep toolDefinitions out of the main chunk (coder#7363)` is
fd6b719 from coder#7364 with no change (the same `git patch-id`). The new
`src/common/utils/tools/askUserQuestionSchemas.ts` imports only zod and
holds the three ask_user_question schemas. `toolDefinitions.ts` imports
and re-exports them, and `openAskUserQuestions.ts` parses with
`AskUserQuestionToolArgsSchema` directly. coder#7364 has the bundle
measurements: the main chunk is back within 750 B br of the build before
coder#6887. coder#7364 can close after this merges.
- `tests/bugbash/sandbox/image.json` gets this record, written with the
same `jq -n` command that `build.sh --push` uses:

| Field | Value |
| ---------------- |
----------------------------------------------------------------------------------------------------------------------------
|
| `digest` |
`sha256:e3804b5418a8e6783698d807c75cc256efae45b81b03400644ae7f13364c9a03`
|
| `inputsKey` |
`2bf9e12d3ac7dcb10397698df2792bf1b244b3fcbe68225378302ef695ba302a`, what
`make -s bugbash-sandbox-key` prints on this commit |
| `playwrightCore` | `1.63.0` (unchanged) |
| `publishedFrom` | `2e2a2b769f417aedcecc09f33abb379d6c8be7b1` (main,
the coder#7362 merge) |

## Validation

- `rendererFirstLoad.test.ts` fails on the pin commit alone (21f9c3f)
and passes with the cherry-pick.
- Publish run on main:
[40016150145](https://github.com/coder/xum/actions/runs/40016150145),
from `2e2a2b769f`. The digest is the subject of its build provenance
attestation.
- `gh attestation verify
oci://ghcr.io/coder/xum-bugbash-sandbox@sha256:e3804b54… --repo
coder/xum` passes. The signer is
`.github/workflows/bugbash-sandbox-image.yml@refs/heads/main`
(workflow_dispatch, run 40016150145), and the source commit is
`2e2a2b769f`.
- I pulled the image by digest. It is linux/amd64, its
`org.xum.bugbash.inputs` label is `2bf9e12d…a302a`, its
`org.xum.bugbash.playwright-core` label is `1.63.0`, and `node -v` in it
prints `v22.23.4`.

Co-authored-by: Thomas Kosiewski <tk@coder.com>

---------

Co-authored-by: Thomas Kosiewski <tk@coder.com>
@linear-code

linear-code Bot commented Oct 26, 2026

Copy link
Copy Markdown

MUX-410

yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 27, 2026
…oder#7671)

## Summary

The "Bug-bash sandbox image" workflow now publishes the image by itself.
A push to main that changes an image input runs the publish job. The job
creates the image only if the key has none yet: each inputs key gets one
stable tag, `ghcr.io/coder/xum-bugbash-sandbox:inputs-<key>`. A key that
has its tag pushes nothing. This is PR 1 of 2. Runners still trust only
the digest in `image.json` until PR 2.

## Background

An image change takes four steps today, and two of them need a person.
coder#7362 changed the Dockerfile. Its story lanes refused the stale image,
so it needed an admin merge, and the merge queue then stayed red for 3.5
hours. After that, a maintainer ran the manual publish on main, and
coder#7369 pinned the new digest. Four images went through that process
between Oct 8 and Oct 23.

The plan, which the user approved: main publishes once per key with no
person (this PR). Then every sandbox job pulls `inputs-<key>`, checks
its build attestation (`gh attestation verify --source-ref
refs/heads/main`), and builds locally when there is no verified image
(PR 2). `image.json` and the `BUGBASH_SANDBOX_BUILD_STALE` opt-in go
away in PR 2.

## Implementation

1. **New trigger:** `push` to main, filtered to the image inputs
(`build.sh --key` hashes the Dockerfile, `build.sh` and the
playwright-core version in `bun.lock`) and to this workflow file. A
change to this file therefore publishes the current key once. `Makefile`
is not in the push filter. A Makefile change does not change the key,
and an existing key needs no publish.
2. **Create if missing:** the publish step looks up the stable tag with
`docker buildx imagetools inspect`. If the tag exists, the step writes
"exists" to the summary and stops. Only a `: not found` error counts as
missing. Any other lookup error fails the job. If the tag is missing,
the step runs the publish as before.
3. **Tag last:** Attest signs the new digest. Only then does a new step,
"Tag inputs-<key>", point the stable tag at it with `imagetools create
--prefer-index=false`. That flag copies the manifest as it is. The
default wraps a single manifest in a new index with a new, unsigned
digest. The step reads the tag's digest back and fails unless it equals
the signed digest. The stable tag marks the key as done, so a failed
Attest leaves no tag, and a re-run publishes again.
4. **No races:** the existing concurrency group runs one publish at a
time and never cancels it. A second push of the same key waits, then
finds the tag and stops.
5. **Docs:** AGENTS.md and the bug-bash skill now say that the merge
push publishes the image. A manual run on main retries a failed publish.

This PR does not change any image input, so the inputs key stays
`2bf9e12d…`, the key in `image.json`. No old-style publish is needed for
this PR.

## Validation

- I ran the step's lookup branch against GHCR with an empty Docker
config, with these results:
  - The current key's stable tag: `PUBLISH: … missing`.
  - An existing published tag: `SKIP: … exists`.
- An unreachable registry: `FAIL` with exit 1, and the DNS error
printed.
- I ran the "Tag inputs-<key>" step's script against a local registry,
with a single-platform Docker v2 manifest like the published image. It
kept the source digest. Without `--prefer-index=false`, the tag got a
different digest.
- `actionlint` 1.7.13: clean. `make lint-zizmor` and `make
static-check`: pass.
- After the merge, I will check two runs. The merge push must publish
`inputs-2bf9e12d3ac7dcb10397698df2792bf1b244b3fcbe68225378302ef695ba302a`.
Then a manual run on main must report "exists" and push nothing. I will
link both runs here.

## Risks

Low. Publishing stays main-only, from the run's own commit, with a fresh
build and an attestation. A new path is the one trigger: every
`bun.lock` change on main starts a run that ends after the lookup, at
about one minute of runner time each. A failed lookup fails the run and
pushes nothing.

<details>
<summary>Decisions in the approved plan</summary>

1. A job can build the image itself in CI and on a developer machine.
2. Only main pushes images, after a merge. The merge queue does not
push.
3. A job uses a registry image only if its attestation shows that main's
publish workflow built it. Otherwise it builds locally.
4. Two PRs: this publish change first, then the runner, `build.sh` and
docs.

</details>

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`xhigh` • Cost: `$263.47`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=xhigh
costs=263.47 -->
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 27, 2026
…e build locally (coder#7672)

## Summary

Every sandbox job now gets its image without a pin. In CI and on a
developer machine, the runner looks up the stable tag
`ghcr.io/coder/xum-bugbash-sandbox:inputs-<key>` for its checkout's
inputs key. It checks the tag's build attestation with `gh`, and it uses
the image only if main's publish workflow built it. In every other case
it builds the image from its own checkout, about 40 s in CI, and warns.
`tests/bugbash/sandbox/image.json`, the `BUGBASH_SANDBOX_BUILD_STALE`
opt-in and the job-kind gate go away. With this PR, an image change
takes one normal PR: no manual publish, no pin PR and no stale-key
refusal. This is PR 2 of 2. It is stacked on coder#7671, which makes main
publish the stable tag.

## Background

The user approved the plan "Sandbox Image Upsert in CI". Today the
runner trusts only the digest in `image.json` and refuses a checkout
whose key differs from it. Only the self-check and the exact-step repros
can build locally, and only in CI. So a Dockerfile change broke the
story lanes until a person published the image and pinned it. After
coder#7362, that kept the merge queue red for 3.5 hours.

## Implementation

`Session.ensureImage()` (`tests/bugbash/sandbox/runner.ts`) resolves the
image in this order:

1. It looks up the stable tag anonymously, through the runner's private
Docker client config, with `docker buildx imagetools inspect`.
2. If the tag exists, it runs `gh attestation verify oci://IMAGE@digest
--repo coder/xum --bundle-from-oci --source-ref refs/heads/main
--signer-workflow coder/xum/.github/workflows/bugbash-sandbox-image.yml`
on the host. `gh` gets only `PATH`, `HOME`, `GH_TOKEN` and
`GH_CONFIG_DIR`. None of them reaches Docker or the job container.
3. If the check passes, the runner uses `IMAGE@digest` and pulls it when
Docker does not have it.
4. In every other case, the runner logs the reason and reuses or builds
the local image with `build.sh`: a missing tag, a lookup error, a bad
digest, no `gh`, no gh token, a failed check or a timeout.
5. The label check (`org.xum.bugbash.inputs` must equal the key) still
runs for both kinds of image. It still refuses on a mismatch.

The launcher prints the LOCAL IMAGE warning with the reason, as a
`::warning::` in Actions, for every job kind.

A local build gives a job no new power. Each job already stages its
checkout's git-listed files and mounts its `dist/` and `node_modules/`
in the container. So a job runs either an image that main's workflow
built and attested, or one built from its own checkout. A registry tag
that someone else pushed is never used.

CI gives the launcher steps `GH_TOKEN: ${{ github.token }}`: the story
lanes in `stories.yml` and both steps of `Test / Bug-bash sandbox` in
`pr.yml`. This PR changes `build.sh` and the Dockerfile, which are
comment-only changes, so its key changes. Its own CI therefore runs the
local-build path in every sandbox job.

## Validation

- `bun test ./tests/bugbash ./tests/bugbash/sandbox`: 356 pass.
- Each path in `ensureImage` has a test in `runner.test.ts`. The
implementer broke each path in turn and saw its test fail. That was 10
breaks, plus 3 for the env allowlist.
- `GH_TOKEN` and `GH_CONFIG_DIR` reach the fake `gh`. They never reach
the fake `docker` or the `docker run` line.
- `make static-check`, `make lint-eslint`, `make lint-zizmor`,
actionlint and `make crap-check`: pass.
- Real Docker, $0, no model: `make bug-bash-sandbox-check` passed, 54 of
54. It built the image once for the new key and reused it for all 3
jobs, with one warning per job ("… is not published"). It passed again
with no gh token.
- `gh` with the runner's exact arguments and env:
  - The current published digest: exit 0.
  - `--source-ref refs/heads/nope`: exit 1.
  - No login: exit 4, which the runner reports as "no gh token".

Still to do after coder#7671 lands and publishes `inputs-2bf9…`: run the
self-check from a checkout with this runner and main's key, and show
that it pulls and verifies the image with no build. After this PR
merges, its new key gets published, and the next PR's sandbox jobs must
pull that image without a build. I will link both runs here.

## Risks

- I did not verify that the job token is enough for `gh attestation
verify --bundle-from-oci` in CI. If it is not, CI builds locally with a
warning, about 40 s per job, and does not fail. I will check the first
CI logs.
- The preflight of `make bug-bash`, `make mcp-apps-e2e` and `make
test-stories` now builds the image when no verified one exists. That can
take up to 25 minutes. Before, it refused.
- If a stable tag ever named a digest without main's attestation, every
job would build locally and warn until a person deleted that tag.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`xhigh` • Cost: `$266.91`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=xhigh
costs=266.91 -->
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.

2 participants