-
Notifications
You must be signed in to change notification settings - Fork 699
Description
Summary
WriteAttestationsReferrer in pkg/oci/remote/write.go silently drops per-layer OCI descriptor annotations when pushing DSSE attestations via the OCI 1.1 Referrers API. This makes referrer attestation manifests unverifiable — cosign, Kyverno, and any policy engine reading attestation referrers cannot find the signing certificate, chain, or RFC3161 timestamp.
Root Cause
WriteAttestationsReferrer (line 421) calls atts.Layers() which returns []v1.Layer — raw layer content with no descriptor metadata. These layers are passed to WriteReferrer, which rebuilds descriptors (lines 357-361) using only MediaType, Digest, and Size, omitting the Annotations field entirely.
Cosign stores verification materials as per-layer descriptor annotations (not in layer content):
dev.sigstore.cosign/certificatedev.sigstore.cosign/chaindev.sigstore.cosign/rfc3161timestampdev.cosignproject.cosign/signaturepredicateType
All of these are silently lost.
The sibling function WriteSignaturesExperimentalOCI() does not have this bug because it calls sigs.Get() (which returns []oci.Signature carrying annotations) rather than sigs.Layers().
Steps to Reproduce
This function is not called by cosign's CLI directly, but is exported public API in pkg/oci/remote/. We hit this as library consumers calling WriteAttestationsReferrer from our own signing tool.
- Build an
oci.SignedEntitywith DSSE attestations that have per-layer descriptor annotations (certificate, chain, RFC3161 timestamp, predicateType):// Annotations are set via mutate.AppendSignatures → mutate.Addendum{Annotations: ...} // Verified present via atts.Get() → sig.Annotations() before push
- Push via the Referrers API:
d, err := ociremote.ResolveDigest(ref, remoteOpts...) ociremote.WriteAttestationsReferrer(d, signedEntity, remoteOpts...)
- Fetch the resulting referrer manifest and inspect layer descriptors - annotations are missing:
"layers": [{ "mediaType": "application/vnd.in-toto+json", "size": 1757, "digest": "sha256:..." // No "annotations" field - certificate, chain, timestamp all dropped }]
- Verification fails - e.g.
cosign verify-attestationreturnsx509: certificate signed by unknown authoritybecause the cert/chain annotations are gone.
For comparison, pushing the same SignedEntity via WriteAttestations (tag-based) preserves all per-layer annotations correctly,
Expected Behavior
Layer descriptors in the referrer manifest should include all annotations from the original attestation layers (certificate, chain, timestamp, predicateType).
Proposed Fix
- In
WriteAttestationsReferrer: useatts.Get()instead ofatts.Layers()to retrieve[]oci.Signatureobjects that carry annotations, then convert to[]v1.Layer. - In
WriteReferrer: after building each layer descriptor, check if the layer implements anAnnotations()method (via structural interface assertion) and populate the descriptor'sAnnotationsfield.
I have a fix and test ready — will submit a PR shortly.