Skip to content

Commit a7ce061

Browse files
simonbairdclaude
andcommitted
Filter v3 attestations and sigs in output
In v3 signature bundles, the signature is actually just another type of attestation. So when we list attestations (in v3) we see both the sig and the provenance. Similarly, when we list the image signatures we see the image signature and the attestation signature. In the detailed output for ec validate image we show details about the signatures and attestations. This changes attempts to avoid listing provenance atts in the image sig list, and imag sigs in the provenance att list. As mentioned in the comments, there might be other ways to do this. Ref: https://issues.redhat.com/browse/EC-1690 Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 4da4643 commit a7ce061

2 files changed

Lines changed: 61 additions & 17 deletions

File tree

features/__snapshots__/task_validate_image.snap

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,6 @@ true
249249
],
250250
"success": true,
251251
"signatures": [
252-
{
253-
"keyid": "",
254-
"sig": "MEQCIDj5l7I0bPCua+H1ZfAAUnd4Hd4k7wUUEi/lpWYSLkOFAiBGgK9KWiNR1t+C4TbmkU/vnpHonmg5hNnwLRC70xc2Rg=="
255-
},
256252
{
257253
"keyid": "",
258254
"sig": "MEUCIBZc+dmgTn8SCx30h9yvCOjsBwj1+aZX0gW53c7TeyuSAiEAp4zWGNHMrjql9NFl/fCmFXnJkgDkOqbN5n7H7mw6aqI="
@@ -268,16 +264,6 @@ true
268264
"sig": "MEUCIQC5bGm4zzbExXBMrZCmqZ98iqUhi8TV/maq/8dJ/c3POAIgCNw+RkeO7PAkT6JDWIvISZ2AjILu9YuPQ0qqfNwCqug="
269265
}
270266
]
271-
},
272-
{
273-
"type": "https://in-toto.io/Statement/v0.1",
274-
"predicateType": "https://sigstore.dev/cosign/sign/v1",
275-
"signatures": [
276-
{
277-
"keyid": "",
278-
"sig": "MEUCID1cJkxyk1oGvXcoAVkDST9A1vfX2gxPEz+LUzN10nDmAiEAxh9rp79yr4fZmAWWOit0dZ5QWK+uYIU8fQVb0/rLIyM="
279-
}
280-
]
281267
}
282268
]
283269
}

internal/evaluation_target/application_snapshot_image/application_snapshot_image.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ func (a *ApplicationSnapshotImage) ValidateImageSignature(ctx context.Context) e
162162
var err error
163163

164164
if a.hasBundles(ctx) {
165+
// For v3 bundles, both image signatures and attestations are stored as
166+
// "attestations" in the unified bundle format. So we use VerifyImageAttestations
167+
// to extract image signatures from the bundle, even though it seems unintuitive.
168+
// This is different from v2 where image signatures and attestations were separate.
169+
//
170+
// The certificate extraction requires different handling for bundles and
171+
// should be addressed in future work to achieve full v2 parity.
165172
opts.NewBundleFormat = true
166173
opts.ClaimVerifier = cosign.IntotoSubjectClaimVerifier
167174
sigs, _, err = client.VerifyImageAttestations(a.reference, &opts)
@@ -175,6 +182,17 @@ func (a *ApplicationSnapshotImage) ValidateImageSignature(ctx context.Context) e
175182

176183
for _, s := range sigs {
177184
if a.hasBundles(ctx) {
185+
// This will appears in the output under "signatures" so filter out
186+
// the sigs that are provenance attestations leaving only the sigs
187+
// that are image signatures. Note: This does seems confusing and
188+
// I'm not 100% sure we're doing the right thing here. Maybe revisit
189+
// once we have a better idea about sigstore bundles and how they're
190+
// handled by cosign itself.
191+
if !isImageSignatureAttestation(s) {
192+
log.Debugf("Skipping non-image signature attestation")
193+
continue
194+
}
195+
178196
// For bundle image signatures produced by cosign v3, the old
179197
// method of accessing the signatures doesn't work. Instead we have
180198
// to extract them from the bundle. And the bundle actually has
@@ -185,7 +203,8 @@ func (a *ApplicationSnapshotImage) ValidateImageSignature(ctx context.Context) e
185203
}
186204
a.signatures = append(a.signatures, signatures...)
187205
} else {
188-
// For older non-bundle image signatures produced by cosign v2
206+
// For older non-bundle image signatures produced by cosign v2.
207+
// Note that filtering isn't needed, since we have only image sigs here.
189208
es, err := signature.NewEntitySignature(s)
190209
if err != nil {
191210
return err
@@ -212,6 +231,11 @@ func (a *ApplicationSnapshotImage) ValidateAttestationSignature(ctx context.Cont
212231
return err
213232
}
214233

234+
// Todo:
235+
// - For the non-bundle code path we actually check the syntax.
236+
// We should do that for bundles as well probably.
237+
// - Doing an early return like thi shere seems untidy, refactor
238+
// maybe?
215239
if useBundles {
216240
return a.parseAttestationsFromBundles(layers)
217241
}
@@ -264,8 +288,19 @@ func (a *ApplicationSnapshotImage) ValidateAttestationSignature(ctx context.Cont
264288
// parseAttestationsFromBundles extracts attestations from Sigstore bundles.
265289
// Bundle-wrapped layers report an incorrect media type, so we unmarshal the
266290
// DSSE envelope from the raw payload directly.
291+
//
292+
// Note: For v3 bundles, this function filters out image signature attestations
293+
// (https://sigstore.dev/cosign/sign/v1) since those are handled in ValidateImageSignature.
294+
// Only provenance and other attestations are added to the attestations array.
267295
func (a *ApplicationSnapshotImage) parseAttestationsFromBundles(layers []cosignOCI.Signature) error {
268296
for _, sig := range layers {
297+
// For v3 bundles, filter out image signature attestations - those are handled
298+
// in ValidateImageSignature. Only add provenance attestations here.
299+
if isImageSignatureAttestation(sig) {
300+
log.Debugf("Skipping image signature attestation - handled in ValidateImageSignature")
301+
continue
302+
}
303+
269304
payload, err := sig.Payload()
270305
if err != nil {
271306
log.Debugf("Skipping bundle entry: cannot read payload: %v", err)
@@ -288,8 +323,8 @@ func (a *ApplicationSnapshotImage) parseAttestationsFromBundles(layers []cosignO
288323
if err != nil {
289324
return fmt.Errorf("unable to parse bundle attestation: %w", err)
290325
}
291-
t := att.PredicateType()
292-
log.Debugf("Found bundle attestation with predicateType: %s", t)
326+
log.Debugf("Found bundle attestation with predicateType: %s", att.PredicateType())
327+
293328
a.attestations = append(a.attestations, att)
294329
}
295330
return nil
@@ -494,6 +529,29 @@ func (a *ApplicationSnapshotImage) WriteInputFile(ctx context.Context) (string,
494529
return inputJSONPath, inputJSON, nil
495530
}
496531

532+
// isImageSignatureAttestation checks if a signature from a bundle represents
533+
// an image signature attestation (vs. a provenance attestation).
534+
func isImageSignatureAttestation(sig cosignOCI.Signature) bool {
535+
payload, err := sig.Payload()
536+
if err != nil {
537+
log.Debugf("Cannot read signature payload: %v", err)
538+
return false
539+
}
540+
541+
att, err := attestation.ProvenanceFromBundlePayload(sig, payload)
542+
if err != nil {
543+
log.Debugf("Cannot parse bundle attestation: %v", err)
544+
return false
545+
}
546+
547+
predicateType := att.PredicateType()
548+
// Image signature attestations use the cosign/sign predicate type
549+
isImageSig := predicateType == "https://sigstore.dev/cosign/sign/v1"
550+
log.Debugf("Attestation predicateType: %s, isImageSignature: %t", predicateType, isImageSig)
551+
552+
return isImageSig
553+
}
554+
497555
// extractSignaturesFromBundle extracts signature information from a bundle
498556
// image signature attestation, using the same pattern as createEntitySignatures.
499557
//

0 commit comments

Comments
 (0)