Skip to content

Scan published native images and refresh the runtime base (#802) - #1158

Merged
rmcdaniel merged 3 commits into
mainfrom
feat/server-802-image-scan
Nov 12, 2026
Merged

rmcdaniel merged 3 commits into
mainfrom
feat/server-802-image-scan

Conversation

@rmcdaniel

Copy link
Copy Markdown
Member

Part of #802. Native releases recorded provenance but never inventoried vulnerabilities in the published image.

Finding: scanning published native 3.5.0 (linux/amd64 sha256:0f394e96…) with the pinned Trivy found CVE-2026-109518 (HIGH) in apt and libapt-pkg6.0 2.6.1, fixed in 2.6.2. The package comes from the runtime base and the server never invokes it. The other rows are unfixed Debian inventory: 1 critical (CVE-2023-45853 zlib, will_not_fix) and 34 high (affected/fix_deferred). Disposition is for Security.

Change

  • scripts/ci/native-image-scan.sh IMAGE@sha256:… PLATFORM OUT reads one platform digest from the registry with Trivy 0.76.1, pinned by its index sha256:790fd66d…. Its linux/amd64 child is the sha256:2e6c00d7… Security used on Scan published Server images for actionable vulnerabilities #802. Nothing runs the image.
  • scripts/ci/native_scan_receipt.py keeps a compact receipt (about 8 KB): digest, OS, scanner and DB identity and times, every HIGH/CRITICAL row (id, package, installed, fixed, status, os-pkgs/lang-pkgs class), counts and a row fingerprint. The full report is discarded. It exits 1 only for a row with a fixed version, per Scan published Server images for actionable vulnerabilities #802's policy. Unfixed rows are inventory.
  • native-release.yml gains scan-amd64 and scan-arm64 after publish, keeping native-scan-* receipts for 90 days. native_alias_evidence.RELEASE_JOBS now requires both scan jobs, so a fixable finding holds alias promotion. The tag contract check expects the two jobs.
  • native-image-rescan.yml rescans latest weekly (Monday 06:17 UTC) and on dispatch.
  • Dockerfile.native runtime base moves to the current Debian 12 bookworm-slim index sha256:734a72b3… (12.15, apt 2.6.2). Scanned with the same Trivy, it has zero fixable HIGH/CRITICAL rows on amd64 and arm64. The unfixed inventory is unchanged. Without this, 3.6.0 would fail its own scan.
  • docs/native-release-promotion.md documents the scan and the hold.

Tests

  • New tests/Unit/Support/native_scan_receipt_test.py (4 tests) covers unfixed inventory not holding, a fixed row holding with its class, a fingerprint that is stable across scan time but follows row changes, and refusal of an unpinned or mismatched image. It is wired into the native promotion contracts workflow.
  • Native promotion Python tests (115) and check-native-release-tags.sh pass.
  • The receipt builder ran on the real 3.5.0 scan above. PR CI's native image build and smoke exercise the new base.

Not in this PR: Security's reachability disposition of the unfixed rows, and the formal SBOM (#1012).

🤖 Generated with Claude Code

The native release recorded provenance but never inventoried
vulnerabilities in the published image. Add a repository-owned scan:
scripts/ci/native-image-scan.sh runs the pinned Trivy 0.76.1 index on one
platform digest from the registry, and native_scan_receipt.py keeps a
compact metadata receipt (digest, scanner and database identity, every
HIGH/CRITICAL row with fix status and class, fingerprint).

native-release.yml scans both published children after publish. A HIGH
or CRITICAL row with a fixed version fails the job; alias evidence now
requires both scan jobs, so it holds promotion. Unfixed rows stay
inventory. native-image-rescan.yml rescans latest weekly.

Scanning published 3.5.0 found CVE-2026-109518 (apt, libapt-pkg6.0
2.6.1, fixed in 2.6.2) in the runtime base, so the base moves to the
current Debian 12 bookworm-slim index, which scans clean of fixable rows
on both architectures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel

Copy link
Copy Markdown
Member Author

Security review: PASS (head 5d0ca9bb). Merge after green CI.

  • Scanner pin verified independently: index docker.io/aquasec/trivy@sha256:790fd66d… contains the linux/amd64 child 2e6c00d7…, the same scanner Security used for Cloud Publish Server images with the patched CommonMark dependency #288/Record rejected updates like PHP #656. It reads the published image remotely per platform digest and never runs it. Workflows are contents: read only, with SHA-pinned actions and no secrets.
  • Gate: a row is actionable only when Trivy reports a fixed version with status fixed. Actionable HIGH/CRITICAL rows exit 1, and native_alias_evidence.py now requires both "Scan published " jobs, so a fixable finding holds promotion. The weekly rescan of latest catches fixes published after a release. Receipts are bound to the scanned digest and fail on a mismatch.
  • Base image verified: mirror.gcr.io/library/debian@sha256:734a72b3… is the current official debian:bookworm-slim multi-arch index (amd64/arm64/armv7). Per your scan it removes the only fixable row (apt CVE-2026-109518, fixed in 2.6.2).

Disposition of the unfixed rows (#802): accepted residual risk, not a release hold.

  • CVE-2023-45853 (zlib, CRITICAL, Debian will_not_fix): the bug is in minizip's zipOpenNewFileInZip4_64 (ZIP creation). The native server doesn't create ZIP archives through minizip, so it isn't reachable.
  • 34 HIGH (affected/fix_deferred): Debian-deferred base packages. The weekly rescan plus the fixed-version gate will pick up and require fixes as Debian ships them.
  • Published 3.5.0's apt CVE-2026-109518: low risk, because the server never runs apt at runtime (the exploit needs a networked apt fetch). It's fixed in 3.6.0 by this base change.

Recommended hardening, not blocking: consider a distroless or -slim-minus-apt runtime base for native, so most of these base packages (apt, perl, etc.) aren't shipped at all.

@rmcdaniel
rmcdaniel enabled auto-merge November 12, 2026 01:49
- The scan container is named from RESOURCE_SCOPE (run, attempt, job),
  and each scan job removes it in an if: always() step, as the Docker
  isolation policy requires.
- The isolation checker's pinned hash of native_alias_evidence.py moves
  to the reviewed version that requires the scan jobs.
- The weekly rescan resolves latest through the registry API instead of
  a docker command.
- native_scan_receipt.py reads the report on stdin and the version JSON
  as a value, so no path comes from arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel
rmcdaniel disabled auto-merge November 12, 2026 02:04
@rmcdaniel

Copy link
Copy Markdown
Member Author

Security countercheck of delta c64e1a72: PASS, no objection to auto-merge.

  • Isolation pin: native_alias_evidence.py at c64e1a72 hashes to 2d4a05b812a1a914…, exactly the new pinned value (main is 7097eb55…). Its only change from main is the two "Scan published <platform>" entries added to RELEASE_JOBS. That's the explicit model update and nothing else.
  • Scan container: scoped by run/attempt/job and removed under if: always(). Fine.
  • resolve_latest_platform_digest.py: accepts only https://ghcr.io/ URLs, uses an anonymous pull token, and requires exactly one sha256: platform digest. Note: urllib follows redirects by default, so the prefix check covers only the first URL. That's harmless for GHCR's manifest endpoint, and the scan receipt binds to the scanned digest regardless. Optional: install a no-redirect opener.
  • Receipt reads stdin: good, no path from argv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel
rmcdaniel enabled auto-merge November 12, 2026 02:05
@rmcdaniel

Copy link
Copy Markdown
Member Author

Took the optional no-redirect opener: the resolver now refuses any redirect rather than following it off ghcr.io (commit on this branch). Auto-merge re-enabled after Security's delta PASS.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit f61e09f Nov 12, 2026
71 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant