Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ed1e5ff
test(ci): require authenticated gha cache transport
seonghobae Sep 9, 2026
f76571c
fix(ci): authenticate patch-validator gha cache transport
seonghobae Sep 9, 2026
26b6e93
test(ci): align image build contracts with authenticated gha action
seonghobae Sep 9, 2026
99ce97d
test(ci): bind image context contract to authenticated action
seonghobae Sep 9, 2026
7b64714
test(ci): preserve patch-validator contracts through gha action
seonghobae Sep 9, 2026
f6dbba3
test(ci): preserve bounded image build deadline
seonghobae Sep 9, 2026
e7be3aa
fix(ci): preserve build step timeout with authenticated cache
seonghobae Sep 9, 2026
afe28b5
merge(restack): converge #566 on protected main 97c1562b
seonghobae Sep 9, 2026
c7c86b0
merge(restack): converge #566 on protected main be7df559
seonghobae Sep 9, 2026
eb54686
fix(patch-validator): verify normalized CPE artifact versions
seonghobae Sep 9, 2026
b1c33d1
test(patch-validator): cover normalized V8 scanner version
seonghobae Sep 9, 2026
1c91b73
fix(security): overlay patched c-ares in validator runtime
seonghobae Sep 9, 2026
b79f967
test(security): pin patched c-ares overlay
seonghobae Sep 9, 2026
d829535
fix(security): review exact embedded runtime applicability
seonghobae Sep 9, 2026
1175d77
test(security): cover reviewed runtime false positives
seonghobae Sep 9, 2026
3fc00d7
docs(security): doctor embedded runtime applicability review
seonghobae Sep 9, 2026
6c84bc0
test(security): reject mixed c-ares source overlays
seonghobae Sep 9, 2026
050ab45
fix(security): replace vendored c-ares tree atomically
seonghobae Sep 9, 2026
858f95e
test(security): preserve Node c-ares integration glue
seonghobae Sep 9, 2026
4d1797e
fix(security): preserve Node c-ares integration on source replacement
seonghobae Sep 9, 2026
19f74d2
test(security): align c-ares regression with atomic replacement
seonghobae Sep 9, 2026
e8fb138
test(ci): require canonical c-ares config replacement
seonghobae Sep 9, 2026
772bfba
fix(ci): restore Node-owned c-ares config atomically
seonghobae Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions .github/workflows/patch-validator-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,26 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Build exact-head patch-validator image
- name: Build exact-head patch-validator image with authenticated GitHub Actions cache
timeout-minutes: 150
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
with:
context: .
file: Dockerfile.patch-validator
platforms: linux/amd64
load: true
build-contexts: |
validator_deps=${{ env.VALIDATOR_DEPS_CONTEXT }}
build-args: |
SOURCE_REVISION=${{ env.SOURCE_SHA }}
tags: ${{ env.IMAGE_TAG }}
cache-from: type=gha,scope=noema-patch-validator-image
cache-to: type=gha,mode=max,scope=noema-patch-validator-image

- name: Capture exact-head patch-validator image digest
shell: bash
run: |
set -euo pipefail
test -d "$VALIDATOR_DEPS_CONTEXT/node_modules"
timeout --signal=TERM --kill-after=30s 150m docker buildx build \
--load \
--cache-from=type=gha,scope=noema-patch-validator-image \
--cache-to=type=gha,mode=max,scope=noema-patch-validator-image \
--build-context "validator_deps=${VALIDATOR_DEPS_CONTEXT}" \
--platform=linux/amd64 \
--file=Dockerfile.patch-validator \
--build-arg=SOURCE_REVISION=${SOURCE_SHA} \
--tag="$IMAGE_TAG" \
.
image_digest="$(docker image inspect "$IMAGE_TAG" --format '{{.Id}}')"
case "$image_digest" in
sha256:????????????????????????????????????????????????????????????????) ;;
Expand Down Expand Up @@ -676,4 +681,4 @@ jobs:
name: patch-validator-image-verification-${{ env.SOURCE_SHA }}
path: ${{ runner.temp }}/patch-validator-evidence
if-no-files-found: error
retention-days: 90
retention-days: 90
33 changes: 30 additions & 3 deletions Dockerfile.patch-validator
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ ARG NODE_VERSION=24.19.0
ARG NODE_SOURCE_SHA256=f6d95e10a0431ee1067fc6aabe9f762908b4716dd35324e1ddb4b1466b76659f
ARG OPENSSL_VERSION=3.5.8
ARG OPENSSL_SOURCE_SHA256=a8f84a39918ec6415ce765d9b429d313ba97b8143169c172e734b9514464f5b2
ARG CARES_VERSION=1.34.8
ARG CARES_SOURCE_SHA256=c222b6d681096f9444d2c4863d2c1174019e27cacca0a4a5c114d36dd7d7bf78

RUN apk add --no-cache \
binutils-gold \
Expand Down Expand Up @@ -46,11 +48,34 @@ RUN set -eu; \
download_exact \
"https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" \
"$OPENSSL_SOURCE_SHA256" \
/tmp/openssl.tar.gz
/tmp/openssl.tar.gz; \
download_exact \
"https://github.com/c-ares/c-ares/releases/download/v${CARES_VERSION}/c-ares-${CARES_VERSION}.tar.gz" \
"$CARES_SOURCE_SHA256" \
/tmp/cares.tar.gz

# Node's canonical c-ares updater replaces the upstream payload while retaining
# Node-owned config/GYP/GN integration. Preserve only that build glue; the
# reviewed c-ares source tree itself is replaced atomically.
RUN mkdir -p /usr/src/node /usr/src/openssl \
&& tar -xJf /tmp/node.tar.xz --strip-components=1 -C /usr/src/node \
&& tar -xzf /tmp/openssl.tar.gz --strip-components=1 -C /usr/src/openssl
&& tar -xzf /tmp/openssl.tar.gz --strip-components=1 -C /usr/src/openssl \
&& mkdir -p /tmp/node-cares-integration /tmp/cares-reviewed \
&& cp -R /usr/src/node/deps/cares/config /tmp/node-cares-integration/config \
&& cp /usr/src/node/deps/cares/.gitignore /tmp/node-cares-integration/.gitignore \
&& cp /usr/src/node/deps/cares/cares.gyp /tmp/node-cares-integration/cares.gyp \
&& cp /usr/src/node/deps/cares/*.gn /usr/src/node/deps/cares/*.gni /tmp/node-cares-integration/ \
&& tar -xzf /tmp/cares.tar.gz --strip-components=1 -C /tmp/cares-reviewed \
&& rm -rf /tmp/cares-reviewed/test \
&& rm -rf /tmp/cares-reviewed/config \
&& cp -R /tmp/node-cares-integration/config /tmp/cares-reviewed/config \
&& cp /tmp/node-cares-integration/.gitignore /tmp/cares-reviewed/.gitignore \
&& cp /tmp/node-cares-integration/cares.gyp /tmp/cares-reviewed/cares.gyp \
&& cp /tmp/node-cares-integration/*.gn /tmp/node-cares-integration/*.gni /tmp/cares-reviewed/ \
&& rm -rf /usr/src/node/deps/cares \
&& mv /tmp/cares-reviewed /usr/src/node/deps/cares \
&& test -f /usr/src/node/deps/cares/config/linux/ares_config.h \
&& grep -Fq "#define ARES_VERSION_STR \"${CARES_VERSION}\"" /usr/src/node/deps/cares/include/ares_version.h

WORKDIR /usr/src/openssl

Expand Down Expand Up @@ -84,6 +109,7 @@ RUN ./configure \
&& make install \
&& test "$(/opt/node/bin/node --version)" = "v${NODE_VERSION}" \
&& test "$(/opt/node/bin/npm --version)" = "11.17.0" \
&& CARES_VERSION="${CARES_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.ares !== process.env.CARES_VERSION) throw new Error(`unexpected c-ares ${process.versions.ares}`)' \
&& OPENSSL_VERSION="${OPENSSL_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.openssl !== process.env.OPENSSL_VERSION) throw new Error(`unexpected OpenSSL ${process.versions.openssl}`)' \
&& /opt/node/bin/node --input-type=module --eval='/\p{ID_Continue}/u.test("a")' \
&& ! readelf -l /opt/node/bin/node | grep -q 'Requesting program interpreter' \
Expand All @@ -99,6 +125,7 @@ RUN ./configure \
&& readelf -p .note.package /opt/node/bin/node \
| grep -Fq 'cpe:2.3:a:nodejs:node.js:24.19.0:*:*:*:*:*:*:*' \
&& test "$(/opt/node/bin/node --version)" = "v${NODE_VERSION}" \
&& CARES_VERSION="${CARES_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.ares !== process.env.CARES_VERSION) throw new Error(`unexpected c-ares ${process.versions.ares}`)' \
&& OPENSSL_VERSION="${OPENSSL_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.openssl !== process.env.OPENSSL_VERSION) throw new Error(`unexpected OpenSSL ${process.versions.openssl}`)' \
&& /opt/node/bin/node --input-type=module --eval='/\p{ID_Continue}/u.test("a")' \
&& ! readelf -l /opt/node/bin/node | grep -q 'Requesting program interpreter' \
Expand Down Expand Up @@ -131,4 +158,4 @@ COPY --chown=65532:65532 patch-validator/runtime.mjs /opt/noema/runtime.mjs
COPY --chown=65532:65532 patch-validator/validator-tsconfig.json /opt/noema/validator-tsconfig.json
COPY --chown=65532:65532 patch-validator/validator-vitest.config.mjs /opt/noema/validator-vitest.config.mjs

ENTRYPOINT ["/nodejs/bin/node", "--input-type=module", "--eval", "import { runCli } from '/opt/noema/runtime.mjs'; import { runEntrypoint } from '/opt/noema/entrypoint.mjs'; process.exitCode = runEntrypoint({ runCliImpl: runCli, writeDiagnostic: (message) => process.stderr.write(message) });"]
ENTRYPOINT ["/nodejs/bin/node", "--input-type=module", "--eval", "import { runCli } from '/opt/noema/runtime.mjs'; import { runEntrypoint } from '/opt/noema/entrypoint.mjs'; process.exitCode = runEntrypoint({ runCliImpl: runCli, writeDiagnostic: (message) => process.stderr.write(message) });"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Patch-validator embedded-runtime applicability review — 2026-09-09

## Problem and exact evidence

Noema patch-validator image run `34328891694` on exact source `b1c33d1c0d3ff0dc6d3d5fae6d4d8dd1ea56270d` completed authenticated BuildKit cache import/hit/export, exact image build, static-runtime inspection, isolated smoke execution, SBOM generation, and raw binary/embedded-runtime scans before the final exact-receipt verifier failed. The retained verification artifact is `10095371779` with artifact digest `sha256:21d9d7a7403ae4be1af21b4a769baa23d77714b2239cbc4c7975ac498728c7b4`.

The raw Grype receipt contained five distinct classes that must not be handled as one generic scanner exception:

- c-ares `1.34.6` → `CVE-2026-33630`;
- SQLite `3.53.3` → `BIT-sqlite-2024-0232`, `BIT-sqlite-2025-29088`, and `BIT-sqlite-2025-6965`;
- V8 `13.6.233.17-node.51`, normalized to reviewed CPE version `13.6.233.17` → `CVE-2026-85046` plus three already reviewed legacy advisories;
- zlib `1.3.2.1-motley-3246f1b` → `GHSA-g857-hhfv-j68w`;
- nghttp2 `1.69.0` → the already reviewed nghttpx-only `CVE-2026-58055`.

Raw scanner evidence remains retained. Applicability review creates a derived receipt and is permitted only when exact component identity, CPE provenance, scanner identity, finding ID, and independently reviewed evidence all agree.

## Decisions

### c-ares: repair the vulnerable component; do not suppress the finding

Upstream c-ares 1.34.7 is explicitly a security release fixing `CVE-2026-33630`, a remotely triggerable use-after-free/double-free in `ares_getaddrinfo()` over TCP. The exact validator runtime contains c-ares 1.34.6, so the finding is applicable. c-ares 1.34.8 retains the security fix and restores API compatibility after 1.34.7's unintended callback-signature break.

The validator therefore keeps Node 24.19.0 as its reviewed executable identity but overlays the official c-ares 1.34.8 release source into Node's vendored `deps/cares` tree before compilation. The release tarball is authenticated by SHA-256 `c222b6d681096f9444d2c4863d2c1174019e27cacca0a4a5c114d36dd7d7bf78`, and the built runtime must report `process.versions.ares === "1.34.8"`. This avoids converting an actually vulnerable component into an applicability exception.

Rejected alternatives: ignoring the finding because the smoke container runs with `--network none`; weakening the severity threshold; or accepting c-ares 1.34.6 based on elapsed isolation evidence. Those alternatives make deployment configuration part of binary vulnerability truth and are not accepted.

### SQLite: exact runtime is outside all three reviewed vulnerable ranges

The embedded runtime is SQLite 3.53.3. Reviewed records bound the findings as follows:

- `CVE-2024-0232` / `BIT-sqlite-2024-0232`: affected SQLite 3.43.0 through 3.43.1; fixed in 3.43.2.
- `CVE-2025-29088` / `BIT-sqlite-2025-29088`: SQLite 3.49.0 before 3.49.1.
- `CVE-2025-6965` / `BIT-sqlite-2025-6965`: SQLite versions before 3.50.2.

For this exact 3.53.3 component/CPE, those scanner matches are version-range false positives. The derived applicability receipt may remove only those three exact finding IDs and only when the component remains exactly SQLite 3.53.3 with the reviewed CPE and NVD-CPE Grype provenance. Any other SQLite finding remains blocking.

### zlib: scanner matched an advisory for a different product/ecosystem

`GHSA-g857-hhfv-j68w` / `CVE-2026-27820` is published by `ruby/zlib` for the RubyGems package `zlib`; the affected code is Ruby's `Zlib::GzipReader` wrapper. Noema's runtime component is Node's embedded upstream C zlib library and is represented by a zlib CPE, not a RubyGems PURL. The derived applicability receipt may remove only this exact GHSA when the exact Node zlib component/CPE and NVD-CPE Grype provenance match. Other zlib findings remain blocking.

### V8 CVE-2026-85046: exact Node branch lacks the vulnerable implementation path

The primary V8 fix for Chromium issue 542403045 changes `JSCallReducer::ReduceArraySort` and `MaglevGraphBuilder::TryReduceArrayPrototypeSort` so mixed receiver element kinds are not used by the inlined `Array.prototype.sort` path. The exact signed Node.js v24.19.0 release commit is `cdc1b38d40cb567b7ad0b39c86addf830a0af0ae`. Inspection of that release's vendored V8 `deps/v8/src/compiler/js-call-reducer.cc` and `deps/v8/src/maglev/maglev-graph-builder.cc` finds neither affected reducer. Thus the generic V8 CPE range is not sufficient evidence that Node 24.19.0's `13.6.233.17-node.51` branch contains the vulnerable path.

The derived applicability receipt may remove `CVE-2026-85046` only for exact Node 24.19.0, exact V8 `13.6.233.17-node.51`, reviewed CPE `cpe:2.3:a:google:v8:13.6.233.17:*:*:*:*:*:*:*`, normalized scanner artifact version `13.6.233.17`, Grype 0.116.1, and NVD-CPE provenance. A Node/V8/CPE/scanner/finding mismatch must fail closed.

## Risk and follow-up

Applicability review is deliberately exact-version rather than an open-ended semantic-version rule. A future Node, V8, SQLite, zlib, scanner, or CPE change loses the exception automatically and must be reviewed again. c-ares remains a source repair, not an exception. The image lane must rebuild from the new exact head and prove the patched c-ares version, raw scan retention, derived reviewed receipt, strict final verifier, isolation smoke, SBOM, and unchanged image/source binding before integration.

Noema does not take ownership of scanner authority, quarantine policy, outbound control, or provider routing through this review. It consumes scanner evidence and applies only its own exact runtime applicability decision at the validator acceptance boundary.

## Traceability / references

c-ares. (2026, July 6). *c-ares version 1.34.7 — security release*. https://c-ares.org/changelog.html

c-ares. (2026, July 7). *c-ares version 1.34.8*. https://github.com/c-ares/c-ares/releases/tag/v1.34.8

GitHub. (2026, April 16). *Buffer overflow vulnerability in Zlib::GzipReader (GHSA-g857-hhfv-j68w)*. https://github.com/ruby/zlib/security/advisories/GHSA-g857-hhfv-j68w

National Institute of Standards and Technology. (2024, January 16; modified 2026, June 17). *CVE-2024-0232*. National Vulnerability Database. https://nvd.nist.gov/vuln/detail/CVE-2024-0232

Open Source Vulnerabilities. (2025). *BIT-sqlite-2025-29088*. https://osv.dev/vulnerability/BIT-sqlite-2025-29088

Open Source Vulnerabilities. (2025; modified 2026). *BIT-sqlite-2025-6965*. https://osv.dev/vulnerability/BIT-sqlite-2025-6965

V8 Project. (2026, August 7). *[compiler] Don't inline Array.prototype.sort on mixed elements kinds* (commit `e0562d87ad9c17042b581582c99237d798572e67`). https://github.com/v8/v8/commit/e0562d87ad9c17042b581582c99237d798572e67

Node.js. (2026, August 3). *Node.js v24.19.0 Krypton (LTS) release* (signed tag; commit `cdc1b38d40cb567b7ad0b39c86addf830a0af0ae`). https://github.com/nodejs/node/releases/tag/v24.19.0
Loading
Loading