From ec8fb0f5a5344b7cfac803108c5495ea25f2a860 Mon Sep 17 00:00:00 2001 From: Jeremi Joslin Date: Tue, 28 Jul 2026 18:48:02 +0700 Subject: [PATCH] fix(release): accept current Grype database schema Signed-off-by: Jeremi Joslin --- .github/workflows/release-candidate.yml | 40 ++++++++++++++++++++---- release/OPERATIONS.md | 4 ++- release/scripts/check-gates-inventory.py | 3 ++ release/scripts/test_registry_release.py | 9 ++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index ce651a5248..3094edf371 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -734,8 +734,18 @@ jobs: if ! jq -e ' type == "object" and (.matches | type == "array") and - (.descriptor.db.built | type == "string") and - (.descriptor.db.checksum | type == "string") + ( + (.descriptor.db.built // .descriptor.db.status.built) + | type == "string" + ) and + ( + (.descriptor.db.checksum | type == "string") or + ( + .descriptor.db.status.from + | type == "string" and + test("checksum=sha256%3A[0-9a-fA-F]{64}") + ) + ) ' "${report}" >/dev/null; then echo "Grype did not emit a complete scan report (exit ${status})" >&2 exit 1 @@ -744,6 +754,22 @@ jobs: echo "::warning::Grype exited ${status}; enforcing its complete report through the reviewed advisory policy" fi } + read_db_metadata() { + local report="$1" + jq -er ' + [ + (.descriptor.db.built // .descriptor.db.status.built), + ( + .descriptor.db.checksum // + ( + .descriptor.db.status.from + | capture("checksum=sha256%3A(?[0-9a-fA-F]{64})").digest + | ascii_downcase + ) + ) + ] | @tsv + ' "${report}" + } for name in registry-notary registry-relay; do digest_ref="$(cat "inputs/build-a/dist/images/${name}.digest")" expected_label_args=() @@ -779,8 +805,9 @@ jobs: echo "Exported rootfs ${name} contains a forbidden special file" >&2 exit 1 fi - db_built="$(jq -er '.descriptor.db.built' "dist/candidate/dist/grype/${name}.grype.json")" - db_checksum="$(jq -er '.descriptor.db.checksum' "dist/candidate/dist/grype/${name}.grype.json")" + IFS=$'\t' read -r db_built db_checksum < <( + read_db_metadata "dist/candidate/dist/grype/${name}.grype.json" + ) db_built_epoch="$(date -u -d "${db_built}" +%s)" now_epoch="$(date -u +%s)" if (( db_built_epoch > now_epoch || now_epoch - db_built_epoch > 259200 )); then @@ -810,8 +837,9 @@ jobs: echo "Exported rootfs postgresql contains a forbidden special file" >&2 exit 1 fi - db_built="$(jq -er '.descriptor.db.built' dist/candidate/dist/grype/postgresql.grype.json)" - db_checksum="$(jq -er '.descriptor.db.checksum' dist/candidate/dist/grype/postgresql.grype.json)" + IFS=$'\t' read -r db_built db_checksum < <( + read_db_metadata dist/candidate/dist/grype/postgresql.grype.json + ) db_built_epoch="$(date -u -d "${db_built}" +%s)" now_epoch="$(date -u +%s)" if (( db_built_epoch > now_epoch || now_epoch - db_built_epoch > 259200 )); then diff --git a/release/OPERATIONS.md b/release/OPERATIONS.md index 1ad5de9bb8..fc710435f2 100644 --- a/release/OPERATIONS.md +++ b/release/OPERATIONS.md @@ -30,7 +30,9 @@ the separate repeatability workflow, not a second build in the blocking candidate path. Grype process status is not itself the advisory decision. The candidate first requires a complete scan report, then the reviewed advisory policy decides -whether that report is acceptable. +whether that report is acceptable. Database identity accepts both the legacy +flat Grype descriptor and the nested descriptor emitted by the pinned Grype +release. The selector writes `registry-stack.release-proof-selection.v1` JSON into the candidate evidence. diff --git a/release/scripts/check-gates-inventory.py b/release/scripts/check-gates-inventory.py index 201eb247f4..9334036177 100644 --- a/release/scripts/check-gates-inventory.py +++ b/release/scripts/check-gates-inventory.py @@ -649,6 +649,9 @@ 'digest_ref="$(cat "inputs/build-a/dist/images/${name}.digest")"', "scan_sbom() {", '(.matches | type == "array")', + ".descriptor.db.built // .descriptor.db.status.built", + 'test("checksum=sha256%3A[0-9a-fA-F]{64}")', + "read_db_metadata() {", "Grype did not emit a complete scan report", "enforcing its complete report through the reviewed advisory policy", "name: Enforce advisory policy", diff --git a/release/scripts/test_registry_release.py b/release/scripts/test_registry_release.py index 531da4a86a..2d4e793b51 100755 --- a/release/scripts/test_registry_release.py +++ b/release/scripts/test_registry_release.py @@ -701,6 +701,15 @@ def test_release_image_scans_are_policy_enforced_and_preserved(self) -> None: self.assertIn("set +e", scan_body) self.assertIn('status=$?', scan_body) self.assertIn('(.matches | type == "array")', scan_body) + self.assertIn( + ".descriptor.db.built // .descriptor.db.status.built", + scan_body, + ) + self.assertIn( + 'test("checksum=sha256%3A[0-9a-fA-F]{64}")', + scan_body, + ) + self.assertIn("read_db_metadata() {", scan_body) self.assertIn( "Grype did not emit a complete scan report", scan_body,