From b748185f48c97dfafec572a78008ba7b04b0eea1 Mon Sep 17 00:00:00 2001 From: Laurent Goderre Date: Mon, 15 Jan 2024 15:34:42 -0500 Subject: [PATCH 1/2] Add support for DSSE envelope for attestation and provenance in imagetools Signed-off-by: Laurent Goderre --- util/imagetools/loader.go | 52 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/util/imagetools/loader.go b/util/imagetools/loader.go index 24ec148e7ef1..80103c25456c 100644 --- a/util/imagetools/loader.go +++ b/util/imagetools/loader.go @@ -4,7 +4,9 @@ package imagetools import ( "context" + "encoding/base64" "encoding/json" + "regexp" "sort" "strings" "sync" @@ -21,6 +23,8 @@ import ( "golang.org/x/sync/errgroup" ) +const inTotoGenericMime = "application/vnd.in-toto+json" + var ( annotationReferences = []string{ "com.docker.reference.digest", @@ -274,7 +278,7 @@ type sbomStub struct { } func (l *loader) scanSBOM(ctx context.Context, fetcher remotes.Fetcher, r *result, refs []digest.Digest, as *asset) error { - ctx = remotes.WithMediaTypeKeyPrefix(ctx, "application/vnd.in-toto+json", "intoto") + ctx = remotes.WithMediaTypeKeyPrefix(ctx, inTotoGenericMime, "intoto") as.deferredSbom = func() (*sbomStub, error) { var sbom *sbomStub for _, dgst := range refs { @@ -283,7 +287,8 @@ func (l *loader) scanSBOM(ctx context.Context, fetcher remotes.Fetcher, r *resul return nil, errors.Errorf("referenced image %s not found", dgst) } for _, layer := range mfst.manifest.Layers { - if layer.MediaType == "application/vnd.in-toto+json" && layer.Annotations["in-toto.io/predicate-type"] == "https://spdx.dev/Document" { + if (layer.MediaType == inTotoGenericMime || isInTotoDSSE(layer.MediaType)) && + layer.Annotations["in-toto.io/predicate-type"] == "https://spdx.dev/Document" { _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) if err != nil { return nil, err @@ -292,6 +297,12 @@ func (l *loader) scanSBOM(ctx context.Context, fetcher remotes.Fetcher, r *resul if err != nil { return nil, err } + + dt, err = decodeDSSE(dt, layer.MediaType) + if err != nil { + return nil, err + } + var spdx struct { Predicate interface{} `json:"predicate"` } @@ -318,7 +329,7 @@ type provenanceStub struct { } func (l *loader) scanProvenance(ctx context.Context, fetcher remotes.Fetcher, r *result, refs []digest.Digest, as *asset) error { - ctx = remotes.WithMediaTypeKeyPrefix(ctx, "application/vnd.in-toto+json", "intoto") + ctx = remotes.WithMediaTypeKeyPrefix(ctx, inTotoGenericMime, "intoto") as.deferredProvenance = func() (*provenanceStub, error) { var provenance *provenanceStub for _, dgst := range refs { @@ -327,7 +338,8 @@ func (l *loader) scanProvenance(ctx context.Context, fetcher remotes.Fetcher, r return nil, errors.Errorf("referenced image %s not found", dgst) } for _, layer := range mfst.manifest.Layers { - if layer.MediaType == "application/vnd.in-toto+json" && strings.HasPrefix(layer.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") { + if (layer.MediaType == inTotoGenericMime || isInTotoDSSE(layer.MediaType)) && + strings.HasPrefix(layer.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") { _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) if err != nil { return nil, err @@ -336,6 +348,12 @@ func (l *loader) scanProvenance(ctx context.Context, fetcher remotes.Fetcher, r if err != nil { return nil, err } + + dt, err = decodeDSSE(dt, layer.MediaType) + if err != nil { + return nil, err + } + var slsa struct { Predicate interface{} `json:"predicate"` } @@ -415,3 +433,29 @@ func (r *result) SBOM() (map[string]sbomStub, error) { } return res, nil } + +func isInTotoDSSE(mime string) bool { + isDSSE, _ := regexp.MatchString("application/vnd\\.in-toto\\..*\\+dsse", mime) + + return isDSSE +} + +func decodeDSSE(dt []byte, mime string) ([]byte, error) { + if isInTotoDSSE(mime) { + var dsse struct { + Payload string `json:"payload"` + } + if err := json.Unmarshal(dt, &dsse); err != nil { + return nil, err + } + + decoded, err := base64.StdEncoding.DecodeString(dsse.Payload) + if err != nil { + return nil, err + } + + dt = decoded + } + + return dt, nil +} From dcdcce6c52b69b3cc0a140056c147f62c71659ea Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 28 Feb 2024 19:25:42 -0800 Subject: [PATCH 2/2] imagetools: supress warnings for dsse mediatypes Signed-off-by: Tonis Tiigi --- util/imagetools/loader.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/util/imagetools/loader.go b/util/imagetools/loader.go index 80103c25456c..06df9c01ac78 100644 --- a/util/imagetools/loader.go +++ b/util/imagetools/loader.go @@ -23,7 +23,11 @@ import ( "golang.org/x/sync/errgroup" ) -const inTotoGenericMime = "application/vnd.in-toto+json" +const ( + inTotoGenericMime = "application/vnd.in-toto+json" + inTotoSPDXDSSEMime = "application/vnd.in-toto.spdx+dsse" + inTotoProvenanceDSSEMime = "application/vnd.in-toto.provenance+dsse" +) var ( annotationReferences = []string{ @@ -278,7 +282,7 @@ type sbomStub struct { } func (l *loader) scanSBOM(ctx context.Context, fetcher remotes.Fetcher, r *result, refs []digest.Digest, as *asset) error { - ctx = remotes.WithMediaTypeKeyPrefix(ctx, inTotoGenericMime, "intoto") + ctx = withIntotoMediaTypes(ctx) as.deferredSbom = func() (*sbomStub, error) { var sbom *sbomStub for _, dgst := range refs { @@ -329,7 +333,7 @@ type provenanceStub struct { } func (l *loader) scanProvenance(ctx context.Context, fetcher remotes.Fetcher, r *result, refs []digest.Digest, as *asset) error { - ctx = remotes.WithMediaTypeKeyPrefix(ctx, inTotoGenericMime, "intoto") + ctx = withIntotoMediaTypes(ctx) as.deferredProvenance = func() (*provenanceStub, error) { var provenance *provenanceStub for _, dgst := range refs { @@ -459,3 +463,10 @@ func decodeDSSE(dt []byte, mime string) ([]byte, error) { return dt, nil } + +func withIntotoMediaTypes(ctx context.Context) context.Context { + for _, mime := range []string{inTotoGenericMime, inTotoSPDXDSSEMime, inTotoProvenanceDSSEMime} { + ctx = remotes.WithMediaTypeKeyPrefix(ctx, mime, "intoto") + } + return ctx +}