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 <me@jedevc.com>
pull/1546/head
Justin Chadwell 2 years ago
parent 19291d900e
commit 78d8b926db

@ -49,6 +49,9 @@ type asset struct {
config *ocispec.Image config *ocispec.Image
sbom *sbomStub sbom *sbomStub
provenance *provenanceStub provenance *provenanceStub
deferredSbom func() (*sbomStub, error)
deferredProvenance func() (*provenanceStub, error)
} }
type result struct { 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 { 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, "application/vnd.in-toto+json", "intoto")
for _, dgst := range refs { as.deferredSbom = func() (*sbomStub, error) {
mfst, ok := r.manifests[dgst] var sbom *sbomStub
if !ok { for _, dgst := range refs {
return errors.Errorf("referenced image %s not found", dgst) mfst, ok := r.manifests[dgst]
} if !ok {
for _, layer := range mfst.manifest.Layers { return nil, errors.Errorf("referenced image %s not found", dgst)
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) for _, layer := range mfst.manifest.Layers {
if err != nil { if layer.MediaType == "application/vnd.in-toto+json" && layer.Annotations["in-toto.io/predicate-type"] == "https://spdx.dev/Document" {
return err _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer)
} if err != nil {
dt, err := content.ReadBlob(ctx, l.cache, layer) return nil, err
if err != nil { }
return err dt, err := content.ReadBlob(ctx, l.cache, layer)
} if err != nil {
var spdx struct { return nil, err
Predicate interface{} `json:"predicate"` }
} var spdx struct {
if err := json.Unmarshal(dt, &spdx); err != nil { Predicate interface{} `json:"predicate"`
return err }
} if err := json.Unmarshal(dt, &spdx); err != nil {
return nil, err
if as.sbom == nil { }
as.sbom = &sbomStub{}
as.sbom.SPDX = spdx.Predicate if sbom == nil {
} else { sbom = &sbomStub{}
as.sbom.AdditionalSPDXs = append(as.sbom.AdditionalSPDXs, spdx.Predicate) sbom.SPDX = spdx.Predicate
} else {
sbom.AdditionalSPDXs = append(sbom.AdditionalSPDXs, spdx.Predicate)
}
} }
} }
} }
return sbom, nil
} }
return 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 { 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, "application/vnd.in-toto+json", "intoto")
for _, dgst := range refs { as.deferredProvenance = func() (*provenanceStub, error) {
mfst, ok := r.manifests[dgst] var provenance *provenanceStub
if !ok { for _, dgst := range refs {
return errors.Errorf("referenced image %s not found", dgst) mfst, ok := r.manifests[dgst]
} if !ok {
for _, layer := range mfst.manifest.Layers { return nil, errors.Errorf("referenced image %s not found", dgst)
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) for _, layer := range mfst.manifest.Layers {
if err != nil { if layer.MediaType == "application/vnd.in-toto+json" && strings.HasPrefix(layer.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") {
return err _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer)
} if err != nil {
dt, err := content.ReadBlob(ctx, l.cache, layer) return nil, err
if err != nil { }
return err dt, err := content.ReadBlob(ctx, l.cache, layer)
} if err != nil {
var slsa struct { return nil, err
Predicate interface{} `json:"predicate"` }
} var slsa struct {
if err := json.Unmarshal(dt, &slsa); err != nil { Predicate interface{} `json:"predicate"`
return err }
} if err := json.Unmarshal(dt, &slsa); err != nil {
as.provenance = &provenanceStub{ return nil, err
SLSA: slsa.Predicate, }
provenance = &provenanceStub{
SLSA: slsa.Predicate,
}
break
} }
break
} }
} }
return provenance, nil
} }
return nil return nil
} }
@ -346,30 +357,50 @@ func (r *result) Configs() map[string]*ocispec.Image {
return res return res
} }
func (r *result) Provenance() map[string]provenanceStub { func (r *result) Provenance() (map[string]provenanceStub, error) {
if len(r.assets) == 0 { if len(r.assets) == 0 {
return nil return nil, nil
} }
res := make(map[string]provenanceStub) res := make(map[string]provenanceStub)
for p, a := range r.assets { for p, a := range r.assets {
if a.provenance == nil { if a.deferredProvenance == nil {
continue 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 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 { if len(r.assets) == 0 {
return nil return nil, nil
} }
res := make(map[string]sbomStub) res := make(map[string]sbomStub)
for p, a := range r.assets { for p, a := range r.assets {
if a.sbom == nil { if a.deferredSbom == nil {
continue 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 res[p] = *a.sbom
} }
return res return res, nil
} }

@ -213,7 +213,10 @@ type tplInput struct {
} }
func (inp tplInput) SBOM() (sbomStub, error) { 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 { for _, v := range sbom {
return v, nil return v, nil
} }
@ -221,7 +224,10 @@ func (inp tplInput) SBOM() (sbomStub, error) {
} }
func (inp tplInput) Provenance() (provenanceStub, 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 { for _, v := range provenance {
return v, nil return v, nil
} }
@ -237,9 +243,9 @@ type tplInputs struct {
} }
func (inp tplInputs) SBOM() (map[string]sbomStub, error) { 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) { func (inp tplInputs) Provenance() (map[string]provenanceStub, error) {
return inp.result.Provenance(), nil return inp.result.Provenance()
} }

Loading…
Cancel
Save