From 78d8b926db63c4a4bbd48dc185f96ec6bc0bf677 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Tue, 10 Jan 2023 13:00:39 +0000 Subject: [PATCH] inspect: lazily load attestation data Delay loading the attestation data immediately, and only compute it upon request. We do this using a deferred function which allows to define the computation in the same place as before, but perform the computation later. With this patch, we ensure that the attestation data is only pulled from the remote if it is actually referenced in the format string - otherwise, we can skip it, for improved performance. Signed-off-by: Justin Chadwell --- util/imagetools/loader.go | 149 ++++++++++++++++++++++-------------- util/imagetools/printers.go | 14 +++- 2 files changed, 100 insertions(+), 63 deletions(-) diff --git a/util/imagetools/loader.go b/util/imagetools/loader.go index 86f5a745..9bba4e21 100644 --- a/util/imagetools/loader.go +++ b/util/imagetools/loader.go @@ -49,6 +49,9 @@ type asset struct { config *ocispec.Image sbom *sbomStub provenance *provenanceStub + + deferredSbom func() (*sbomStub, error) + deferredProvenance func() (*provenanceStub, error) } type result struct { @@ -261,36 +264,40 @@ 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") - for _, dgst := range refs { - mfst, ok := r.manifests[dgst] - if !ok { - return 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" { - _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) - if err != nil { - return err - } - dt, err := content.ReadBlob(ctx, l.cache, layer) - if err != nil { - return err - } - var spdx struct { - Predicate interface{} `json:"predicate"` - } - if err := json.Unmarshal(dt, &spdx); err != nil { - return err - } - - if as.sbom == nil { - as.sbom = &sbomStub{} - as.sbom.SPDX = spdx.Predicate - } else { - as.sbom.AdditionalSPDXs = append(as.sbom.AdditionalSPDXs, spdx.Predicate) + as.deferredSbom = func() (*sbomStub, error) { + var sbom *sbomStub + for _, dgst := range refs { + mfst, ok := r.manifests[dgst] + if !ok { + 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" { + _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) + if err != nil { + return nil, err + } + dt, err := content.ReadBlob(ctx, l.cache, layer) + if err != nil { + return nil, err + } + var spdx struct { + Predicate interface{} `json:"predicate"` + } + if err := json.Unmarshal(dt, &spdx); err != nil { + return nil, err + } + + if sbom == nil { + sbom = &sbomStub{} + sbom.SPDX = spdx.Predicate + } else { + sbom.AdditionalSPDXs = append(sbom.AdditionalSPDXs, spdx.Predicate) + } } } } + return sbom, nil } return nil } @@ -301,33 +308,37 @@ 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") - for _, dgst := range refs { - mfst, ok := r.manifests[dgst] - if !ok { - return 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/") { - _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) - if err != nil { - return err - } - dt, err := content.ReadBlob(ctx, l.cache, layer) - if err != nil { - return err - } - var slsa struct { - Predicate interface{} `json:"predicate"` - } - if err := json.Unmarshal(dt, &slsa); err != nil { - return err - } - as.provenance = &provenanceStub{ - SLSA: slsa.Predicate, + as.deferredProvenance = func() (*provenanceStub, error) { + var provenance *provenanceStub + for _, dgst := range refs { + mfst, ok := r.manifests[dgst] + if !ok { + 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/") { + _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) + if err != nil { + return nil, err + } + dt, err := content.ReadBlob(ctx, l.cache, layer) + if err != nil { + return nil, err + } + var slsa struct { + Predicate interface{} `json:"predicate"` + } + if err := json.Unmarshal(dt, &slsa); err != nil { + return nil, err + } + provenance = &provenanceStub{ + SLSA: slsa.Predicate, + } + break } - break } } + return provenance, nil } return nil } @@ -346,30 +357,50 @@ func (r *result) Configs() map[string]*ocispec.Image { return res } -func (r *result) Provenance() map[string]provenanceStub { +func (r *result) Provenance() (map[string]provenanceStub, error) { if len(r.assets) == 0 { - return nil + return nil, nil } res := make(map[string]provenanceStub) for p, a := range r.assets { - if a.provenance == nil { + if a.deferredProvenance == nil { continue } + if a.provenance == nil { + provenance, err := a.deferredProvenance() + if err != nil { + return nil, err + } + if provenance == nil { + continue + } + a.provenance = provenance + } res[p] = *a.provenance } - return res + return res, nil } -func (r *result) SBOM() map[string]sbomStub { +func (r *result) SBOM() (map[string]sbomStub, error) { if len(r.assets) == 0 { - return nil + return nil, nil } res := make(map[string]sbomStub) for p, a := range r.assets { - if a.sbom == nil { + if a.deferredSbom == nil { continue } + if a.sbom == nil { + sbom, err := a.deferredSbom() + if err != nil { + return nil, err + } + if sbom == nil { + continue + } + a.sbom = sbom + } res[p] = *a.sbom } - return res + return res, nil } diff --git a/util/imagetools/printers.go b/util/imagetools/printers.go index b119a622..78041cc7 100644 --- a/util/imagetools/printers.go +++ b/util/imagetools/printers.go @@ -213,7 +213,10 @@ type tplInput struct { } func (inp tplInput) SBOM() (sbomStub, error) { - sbom := inp.result.SBOM() + sbom, err := inp.result.SBOM() + if err != nil { + return sbomStub{}, nil + } for _, v := range sbom { return v, nil } @@ -221,7 +224,10 @@ func (inp tplInput) SBOM() (sbomStub, error) { } func (inp tplInput) Provenance() (provenanceStub, error) { - provenance := inp.result.Provenance() + provenance, err := inp.result.Provenance() + if err != nil { + return provenanceStub{}, nil + } for _, v := range provenance { return v, nil } @@ -237,9 +243,9 @@ type tplInputs struct { } func (inp tplInputs) SBOM() (map[string]sbomStub, error) { - return inp.result.SBOM(), nil + return inp.result.SBOM() } func (inp tplInputs) Provenance() (map[string]provenanceStub, error) { - return inp.result.Provenance(), nil + return inp.result.Provenance() }