Skip to content

Bring container evidence to parity with AICR: per-platform SBOM and VEX #607

Description

@lockwobr

Summary

Bring nodewright's container evidence to parity with the layout AICR settled on in NVIDIA/aicr#2426: a CycloneDX SBOM and an OpenVEX document attested to each platform manifest digest, with SLSA provenance on the index.

Nodewright is most of the way there already. The gaps are the attestation subject, a missing VEX document, and two cosign flags.

Current state

.github/actions/cosign-sign-sbom already does the right things in the right order:

  • cosign sign on the image
  • cosign attest --type cyclonedx with a Syft-generated CycloneDX SBOM
  • actions/attest-build-provenance for SLSA

Images are built multi-arch (PLATFORMS: linux/amd64,linux/arm64), and operator-ci.yaml resolves the multi-platform manifest digest and passes that as subject-digest.

Gaps

1. Evidence is attached to the index, not the platform manifests

This is the substantive one, and it is the same defect AICR fixed in #2426.

crane digest <tag> returns the index. An SBOM describes exactly one root filesystem, so attaching a single SBOM to a multi-platform index means it cannot honestly describe either child. A consumer who resolves linux/amd64 and enumerates referrers there finds nothing.

Fix: resolve each platform manifest with crane digest --platform linux/<arch> and attest that platform's SBOM to its own digest. AICR's .github/actions/attest-image-from-tag does this and fails closed if a platform is missing or resolves to the index digest, which catches a release that silently lost an architecture.

2. No VEX document

Nodewright publishes no OpenVEX (.openvex.json does not exist, zero references in the repo). Without one there is no way to say "this CVE does not apply to us and here is why", so every finding in a scan is either a real problem or unexplained noise.

AICR's .openvex.json plus the aicr-managing-openvex skill is the working model, including the constraint learned the hard way in NVIDIA/aicr#2706: the document is signed and published, so it carries triage statements only. Working notes belong in a separate state file with a lifecycle, or they grow without bound.

Use OpenVEX, not CycloneDX, for the VEX. cosign attest --type cyclonedx sets predicate type https://cyclonedx.org/bom regardless of whether the payload is an SBOM or a VEX, and a referrer descriptor has no name field. Two CycloneDX documents on one manifest are therefore indistinguishable in a referrers listing, and telling them apart means pulling and decoding every payload. OpenVEX gets https://openvex.dev/ns, so the listing stays self-describing.

3. --new-bundle-format=true is not set

Zero occurrences in the action. This controls whether the attestation publishes through the OCI referrers API or as a legacy .att tag, and it has moved between cosign releases. It should be an explicit decision rather than an inherited default. AICR sets it on every attest call and has a release-policy test pinning that.

4. No timeout on cosign calls

AICR bounds every cosign attest with timeout --foreground 120s, so a hung registry call fails the release instead of hanging the job.

Worth copying, and worth not copying

Copy: the per-platform subject resolution with fail-closed checks; the OpenVEX guard that validates the document against the v0.2.0 contract before anything is signed, in both source and projection form; the digest-binding step that rewrites VEX product identifiers to pkg:oci/<image>@sha256:<platform-digest>; and the release-policy tests that pin attestation subjects and predicate types so a refactor cannot silently regress them.

Do not copy: AICR's tooling field history. See #2706 — an 8KB changelog accumulated in a field meant for a tool identifier, because the maintenance workflow required producing revision evidence and named nowhere to put it. If nodewright adopts a VEX document, give it a state file with a deletion rule from day one.

Note: nodewright is ahead on one thing

Nodewright runs cosign sign on the image; AICR does not, and publishes attestations only. That gap is currently causing real friction — Kyverno's ImageValidatingPolicy has a known issue specific to signature-less, attestation-only images. Nodewright should keep doing this, and AICR should probably follow.

Success criteria

  • CycloneDX SBOM attested to each platform manifest digest, not the index.
  • An OpenVEX document published per platform manifest, with product identifiers bound to that digest.
  • SLSA provenance stays on the index.
  • The SBOM and the VEX are distinguishable from a referrers listing without fetching payloads.
  • --new-bundle-format=true set explicitly on every attest call.
  • Every cosign call bounded by a timeout.
  • A release fails closed if a platform manifest is missing or resolves to the index digest.
  • If a VEX document is adopted, working notes live outside it with an explicit deletion rule.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

component/ciCI workflows, GitHub Actions, and repo toolingcomponent/testsEnd-to-end / chainsaw test suites (k8s-tests)

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions