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")
as.deferredSbom = func() (*sbomStub, error) {
var sbom *sbomStub
for _, dgst := range refs { for _, dgst := range refs {
mfst, ok := r.manifests[dgst] mfst, ok := r.manifests[dgst]
if !ok { if !ok {
return errors.Errorf("referenced image %s not found", dgst) return nil, errors.Errorf("referenced image %s not found", dgst)
} }
for _, layer := range mfst.manifest.Layers { 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 == "application/vnd.in-toto+json" && layer.Annotations["in-toto.io/predicate-type"] == "https://spdx.dev/Document" {
_, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer) _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer)
if err != nil { if err != nil {
return err return nil, err
} }
dt, err := content.ReadBlob(ctx, l.cache, layer) dt, err := content.ReadBlob(ctx, l.cache, layer)
if err != nil { if err != nil {
return err return nil, err
} }
var spdx struct { var spdx struct {
Predicate interface{} `json:"predicate"` Predicate interface{} `json:"predicate"`
} }
if err := json.Unmarshal(dt, &spdx); err != nil { if err := json.Unmarshal(dt, &spdx); err != nil {
return err return nil, err
} }
if as.sbom == nil { if sbom == nil {
as.sbom = &sbomStub{} sbom = &sbomStub{}
as.sbom.SPDX = spdx.Predicate sbom.SPDX = spdx.Predicate
} else { } else {
as.sbom.AdditionalSPDXs = append(as.sbom.AdditionalSPDXs, spdx.Predicate) sbom.AdditionalSPDXs = append(sbom.AdditionalSPDXs, spdx.Predicate)
}
} }
} }
} }
return sbom, nil
} }
return nil return nil
} }
@ -301,34 +308,38 @@ 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")
as.deferredProvenance = func() (*provenanceStub, error) {
var provenance *provenanceStub
for _, dgst := range refs { for _, dgst := range refs {
mfst, ok := r.manifests[dgst] mfst, ok := r.manifests[dgst]
if !ok { if !ok {
return errors.Errorf("referenced image %s not found", dgst) return nil, errors.Errorf("referenced image %s not found", dgst)
} }
for _, layer := range mfst.manifest.Layers { 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 == "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) _, err := remotes.FetchHandler(l.cache, fetcher)(ctx, layer)
if err != nil { if err != nil {
return err return nil, err
} }
dt, err := content.ReadBlob(ctx, l.cache, layer) dt, err := content.ReadBlob(ctx, l.cache, layer)
if err != nil { if err != nil {
return err return nil, err
} }
var slsa struct { var slsa struct {
Predicate interface{} `json:"predicate"` Predicate interface{} `json:"predicate"`
} }
if err := json.Unmarshal(dt, &slsa); err != nil { if err := json.Unmarshal(dt, &slsa); err != nil {
return err return nil, err
} }
as.provenance = &provenanceStub{ provenance = &provenanceStub{
SLSA: slsa.Predicate, 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.deferredProvenance == nil {
continue
}
if a.provenance == nil { if a.provenance == nil {
provenance, err := a.deferredProvenance()
if err != nil {
return nil, err
}
if provenance == nil {
continue 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.deferredSbom == nil {
continue
}
if a.sbom == nil { if a.sbom == nil {
sbom, err := a.deferredSbom()
if err != nil {
return nil, err
}
if sbom == nil {
continue 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