Skip to content

Commit 38c7291

Browse files
authored
fix(release): enforce complete Grype reports (#532)
Require complete Grype JSON and delegate the blocking decision to the reviewed advisory policy. Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
1 parent aaaa6dd commit 38c7291

4 files changed

Lines changed: 47 additions & 5 deletions

File tree

‎.github/workflows/release-candidate.yml‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,27 @@ jobs:
723723
fi
724724
printf '%s\n' "${postgresql_ref}" \
725725
> dist/candidate/dist/images/postgresql.digest
726+
scan_sbom() {
727+
local sbom="$1"
728+
local report="$2"
729+
local status
730+
set +e
731+
grype "sbom:${sbom}" -o json > "${report}"
732+
status=$?
733+
set -e
734+
if ! jq -e '
735+
type == "object" and
736+
(.matches | type == "array") and
737+
(.descriptor.db.built | type == "string") and
738+
(.descriptor.db.checksum | type == "string")
739+
' "${report}" >/dev/null; then
740+
echo "Grype did not emit a complete scan report (exit ${status})" >&2
741+
exit 1
742+
fi
743+
if (( status != 0 )); then
744+
echo "::warning::Grype exited ${status}; enforcing its complete report through the reviewed advisory policy"
745+
fi
746+
}
726747
for name in registry-notary registry-relay; do
727748
digest_ref="$(cat "inputs/build-a/dist/images/${name}.digest")"
728749
expected_label_args=()
@@ -745,8 +766,9 @@ jobs:
745766
"dist/candidate/dist/sbom/${name}.spdx.json" \
746767
--image-name "${name}" \
747768
--digest-ref "${digest_ref}"
748-
grype "sbom:dist/candidate/dist/sbom/${name}.syft.json" -o json \
749-
> "dist/candidate/dist/grype/${name}.grype.json"
769+
scan_sbom \
770+
"dist/candidate/dist/sbom/${name}.syft.json" \
771+
"dist/candidate/dist/grype/${name}.grype.json"
750772
mkdir -p "dist/candidate/dist/rootfs/${name}"
751773
crane export "${digest_ref}" - \
752774
| tar --extract --file=- --directory="dist/candidate/dist/rootfs/${name}" \
@@ -775,8 +797,9 @@ jobs:
775797
dist/candidate/dist/sbom/postgresql.spdx.json \
776798
--image-name postgresql \
777799
--digest-ref "${postgresql_ref}"
778-
grype "sbom:dist/candidate/dist/sbom/postgresql.syft.json" -o json \
779-
> dist/candidate/dist/grype/postgresql.grype.json
800+
scan_sbom \
801+
dist/candidate/dist/sbom/postgresql.syft.json \
802+
dist/candidate/dist/grype/postgresql.grype.json
780803
mkdir -p dist/candidate/dist/rootfs/postgresql
781804
crane export "${postgresql_ref}" - \
782805
| tar --extract --file=- --directory=dist/candidate/dist/rootfs/postgresql \

‎release/OPERATIONS.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ invalid immutable image topology, an unreviewed advisory, or any source and
2828
artifact binding mismatch. Extended proof is additional evidence produced by
2929
the separate repeatability workflow, not a second build in the blocking
3030
candidate path.
31+
Grype process status is not itself the advisory decision. The candidate first
32+
requires a complete scan report, then the reviewed advisory policy decides
33+
whether that report is acceptable.
3134

3235
The selector writes `registry-stack.release-proof-selection.v1` JSON into the
3336
candidate evidence.

‎release/scripts/check-gates-inventory.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,6 @@
559559
"build-a:\n name: Build A cached canonical candidate",
560560
"name: Restore exact-key Cargo cache",
561561
"name: Validate canonical binary inventory",
562-
'grype "sbom:dist/candidate/dist/sbom/${name}.syft.json"',
563562
),
564563
),
565564
(
@@ -648,6 +647,10 @@
648647
(
649648
"name: Scan immutable staging digests",
650649
'digest_ref="$(cat "inputs/build-a/dist/images/${name}.digest")"',
650+
"scan_sbom() {",
651+
'(.matches | type == "array")',
652+
"Grype did not emit a complete scan report",
653+
"enforcing its complete report through the reviewed advisory policy",
651654
"name: Enforce advisory policy",
652655
"--syft-report dist/candidate/dist/sbom/registry-notary.syft.json",
653656
"--rootfs dist/candidate/dist/rootfs/registry-notary",

‎release/scripts/test_registry_release.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,19 @@ def test_release_image_scans_are_policy_enforced_and_preserved(self) -> None:
696696
upload_step = images_job.index("Upload exact candidate payload")
697697
self.assertLess(scan_step, enforcement_step)
698698
self.assertLess(enforcement_step, upload_step)
699+
scan_body = images_job[scan_step:enforcement_step]
700+
self.assertIn("scan_sbom() {", scan_body)
701+
self.assertIn("set +e", scan_body)
702+
self.assertIn('status=$?', scan_body)
703+
self.assertIn('(.matches | type == "array")', scan_body)
704+
self.assertIn(
705+
"Grype did not emit a complete scan report",
706+
scan_body,
707+
)
708+
self.assertIn(
709+
"enforcing its complete report through the reviewed advisory policy",
710+
scan_body,
711+
)
699712
self.assertIn(
700713
"grype dist/candidate/dist/grype/registry-notary.grype.json",
701714
images_job,

0 commit comments

Comments
 (0)