Skip to content

Commit 8e59a62

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 eab2fac commit 8e59a62

2 files changed

Lines changed: 68 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: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ func (a *ApplicationSnapshotImage) ValidateImageSignature(ctx context.Context) e
183183

184184
for _, s := range sigs {
185185
if useBundles {
186+
// This will appears in the output under "signatures" so filter out
187+
// the sigs that are provenance attestations leaving only the sigs
188+
// that are image signatures. Note: This does seems confusing and
189+
// I'm not 100% sure we're doing the right thing here. Maybe revisit
190+
// once we have a better idea about sigstore bundles and how they're
191+
// handled by cosign itself.
192+
if !isImageSignatureAttestation(s) {
193+
log.Debugf("Skipping non-image signature attestation")
194+
continue
195+
}
196+
186197
// For bundle image signatures produced by cosign v3, the old
187198
// method of accessing the signatures doesn't work. Instead we have
188199
// to extract them from the bundle. And the bundle actually has
@@ -193,7 +204,8 @@ func (a *ApplicationSnapshotImage) ValidateImageSignature(ctx context.Context) e
193204
}
194205
a.signatures = append(a.signatures, signatures...)
195206
} else {
196-
// For older non-bundle image signatures produced by cosign v2
207+
// For older non-bundle image signatures produced by cosign v2.
208+
// Note that filtering isn't needed, since we have only image sigs here.
197209
es, err := signature.NewEntitySignature(s)
198210
if err != nil {
199211
return err
@@ -220,6 +232,11 @@ func (a *ApplicationSnapshotImage) ValidateAttestationSignature(ctx context.Cont
220232
return err
221233
}
222234

235+
// Todo:
236+
// - For the non-bundle code path we actually check the syntax.
237+
// We should do that for bundles as well probably.
238+
// - Doing an early return like thi shere seems untidy, refactor
239+
// maybe?
223240
if useBundles {
224241
return a.parseAttestationsFromBundles(layers)
225242
}
@@ -272,8 +289,19 @@ func (a *ApplicationSnapshotImage) ValidateAttestationSignature(ctx context.Cont
272289
// parseAttestationsFromBundles extracts attestations from Sigstore bundles.
273290
// Bundle-wrapped layers report an incorrect media type, so we unmarshal the
274291
// DSSE envelope from the raw payload directly.
292+
//
293+
// Note: For v3 bundles, this function filters out image signature attestations
294+
// (https://sigstore.dev/cosign/sign/v1) since those are handled in ValidateImageSignature.
295+
// Only provenance and other attestations are added to the attestations array.
275296
func (a *ApplicationSnapshotImage) parseAttestationsFromBundles(layers []cosignOCI.Signature) error {
276297
for _, sig := range layers {
298+
// For v3 bundles, filter out image signature attestations - those are handled
299+
// in ValidateImageSignature. Only add provenance attestations here.
300+
if isImageSignatureAttestation(sig) {
301+
log.Debugf("Skipping image signature attestation - handled in ValidateImageSignature")
302+
continue
303+
}
304+
277305
payload, err := sig.Payload()
278306
if err != nil {
279307
log.Debugf("Skipping bundle entry: cannot read payload: %v", err)
@@ -296,8 +324,8 @@ func (a *ApplicationSnapshotImage) parseAttestationsFromBundles(layers []cosignO
296324
if err != nil {
297325
return fmt.Errorf("unable to parse bundle attestation: %w", err)
298326
}
299-
t := att.PredicateType()
300-
log.Debugf("Found bundle attestation with predicateType: %s", t)
327+
log.Debugf("Found bundle attestation with predicateType: %s", att.PredicateType())
328+
301329
a.attestations = append(a.attestations, att)
302330
}
303331
return nil
@@ -502,6 +530,43 @@ func (a *ApplicationSnapshotImage) WriteInputFile(ctx context.Context) (string,
502530
return inputJSONPath, inputJSON, nil
503531
}
504532

533+
// extractPredicateType extracts the predicateType field from a JSON
534+
// payload lazily, i.e. without unmarshalling all the other fields
535+
func extractPredicateType(payload []byte) (string, error) {
536+
var attestation struct {
537+
PredicateType string `json:"predicateType"`
538+
}
539+
if err := json.Unmarshal(payload, &attestation); err != nil {
540+
return "", err
541+
}
542+
return attestation.PredicateType, nil
543+
}
544+
545+
// hasPredicateType checks if a JSON payload has the specified predicate type.
546+
func hasPredicateType(payload []byte, expectedType string) bool {
547+
predicateType, err := extractPredicateType(payload)
548+
if err != nil {
549+
log.Debugf("Cannot parse JSON payload: %v", err)
550+
return false
551+
}
552+
return predicateType == expectedType
553+
}
554+
555+
const cosignSignPredicateType = "https://sigstore.dev/cosign/sign/v1"
556+
557+
// isImageSignatureAttestation checks if a signature from a bundle represents
558+
// an image signature attestation (vs. a provenance attestation).
559+
func isImageSignatureAttestation(sig cosignOCI.Signature) bool {
560+
payload, err := sig.Payload()
561+
if err != nil {
562+
log.Debugf("Cannot read signature payload: %v", err)
563+
return false
564+
}
565+
566+
// Image signature attestations use the cosign/sign predicate type
567+
return hasPredicateType(payload, cosignSignPredicateType)
568+
}
569+
505570
// extractSignaturesFromBundle extracts signature information from a bundle
506571
// image signature attestation, using the same pattern as createEntitySignatures.
507572
//

0 commit comments

Comments
 (0)